ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TCP/IP] [epoll] epoll을 사용한 소켓 프로그래밍 예제
    C_C++ 프로그래밍/TCP_IP 2019. 6. 10. 20:52

          로재의 개발 일기      

    epoll (사건 기반 파일 처리)

    epoll을 사용하여 server가 모든 클라이언트의 입력을 일일이 반복문을 순회하면서 검사하지 않고

    사건이 발생한 파일 디스크립터만 검사하도록 하였습니다.

    epoll이란? -> https://redcoder.tistory.com/123


    클라이언트는 select와 fd_set 함수들을 사용하였고

    이에 대한 정보를 가까운 시일 내에 정리해서 올리겠습니다.


      Server.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    #include <stdlib.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/epoll.h>
     
    #include <string.h>
    #include <stdio.h>
     
    #define PORT_NUM 3600
    #define EPOLL_SIZE 20
    #define MAXLINE 1024
     
    // user data struct
    struct udata
    {
        int fd;
        char name[80];
    };
     
    int user_fds[1024];
     
    void send_msg(struct epoll_event ev, char *msg);
     
    int main(int argc, char **argv)
    {
        struct sockaddr_in addr, clientaddr;
        struct epoll_event ev, *events;            // ev는 listen 소켓의 사건, *event는 
        struct udata *user_data;                // user들의 데이터가 포인터로 처리가 가능하다.
        
        int listenfd;
        int clientfd;
        int i;
        socklen_t addrlen, clilen;
        int readn;
        int eventn;
        int epollfd;
        char buf[MAXLINE];
     
        // events 포인터를 초기화한다. EPOLL_SIZE = 20
        events = (struct epoll_event *)malloc(sizeof(struct epoll_event) * EPOLL_SIZE);
        // epoll 파일 디스크립터를 만든다.
        if((epollfd = epoll_create(100)) == -1)
            return 1;
     
        addrlen = sizeof(addr);
        if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
            return 1;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(PORT_NUM);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        if(bind (listenfd, (struct sockaddr *)&addr, addrlen) == -1)
            return 1;
     
        listen(listenfd, 5);
     
        // EPOLL_CTL_ADD를 통해 listen 소켓을 이벤트 풀에 추가시켰다.
        ev.events = EPOLLIN;    // 이벤트가 들어오면 알림
        ev.data.fd = listenfd;    // 듣기 소켓을 추가한다.
        epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);    // listenfd의 상태변화를 epollfd를 통해 관찰 
        memset(user_fds, -1sizeof(int* 1024);            
     
        while(1)
        {
            // 사건 발생 시까지 무한 대기
            // epollfd의 사건 발생 시 events에 fd를 채운다.
            // eventn은 listen에 성공한 fd의 수
            eventn = epoll_wait(epollfd, events, EPOLL_SIZE, -1); 
            if(eventn == -1)
            {
                return 1;
            }
            for(i = 0; i < eventn ; i++)
            {
                if(events[i].data.fd == listenfd)    // 듣기 소켓에서 이벤트가 발생함
                {
                    memset(buf, 0x00, MAXLINE);
                    clilen = sizeof(struct sockaddr);
                    clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);
                    user_fds[clientfd] = 1;            // 연결 처리
                
                    user_data = malloc(sizeof(user_data));
                    user_data->fd = clientfd;
                    
                    char *tmp = "First insert your nickname :";
                    write(user_data->fd, tmp, 29);
     
                    sleep(1);
                    read(user_data->fd, user_data->name, sizeof(user_data->name));
                    user_data->name[strlen(user_data->name)-1= 0;
                    
                    printf("Welcome [%s]  \n", user_data->name);
                
                    sleep(1);    
                    sprintf(buf, "Okay your nickname : %s\n", user_data->name);
     
                    write(user_data->fd, buf, 40);
     
                    ev.events = EPOLLIN;
                    ev.data.ptr = user_data;
     
                    epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &ev);
                }
                else                                // 연결 소켓에서 이벤트가 발생함
                {
                    user_data = events[i].data.ptr;
                    memset(buf, 0x00, MAXLINE);
                    readn = read(user_data->fd, buf, MAXLINE);
                    if(readn <= 0)                    // 읽는 도중 에러 발생
                    {
                        epoll_ctl(epollfd, EPOLL_CTL_DEL, user_data->fd, events);
                        close(user_data->fd);
                        user_fds[user_data->fd] = -1;
                        free(user_data);
                    }
                    else                            // 데이터를 읽는다.
                    {
                        send_msg(events[i], buf);
                    }
                }
            }
        }
    }
     
    // client가 보낸 메시지를 다른 client들에게 전송한다.
    void send_msg(struct epoll_event ev, char *msg)
    {
        int i;
        char buf[MAXLINE+24];
        struct udata *user_data;
        user_data = ev.data.ptr;
        for(i =0; i < 1024; i++)
        {
            memset(buf, 0x00, MAXLINE+24);
            sprintf(buf, "%s : %s", user_data->name, msg);
            if((user_fds[i] == 1))
            {
                write(i, buf, MAXLINE+24);
            }
        }
    }
     

    cs

      Client.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/time.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
     
    #define MAXLINE 1024
     
    int main(int argc, char **argv)
    {
      int socketfd;
      struct sockaddr_in sockaddr;
      int socklen;
      fd_set readfds, oldfds;
      int maxfd;
      int fd_num;
      char buf[MAXLINE];
      int readn, writen;
     
      if(argc != 3)
      {
          printf("Usage : %s [ip address] [port]\n", argv[0]);
          return 1;
      }
      if( (socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
      {
          return 1;
      }
     
      sockaddr.sin_family = AF_INET;
      sockaddr.sin_addr.s_addr = inet_addr(argv[1]);
      sockaddr.sin_port = htons(atoi(argv[2]));
     
      socklen = sizeof(sockaddr);
      
      if(connect(socketfd, (struct sockaddr *)&sockaddr, socklen) == -1)
      {        perror("connect error");
             return 1;
      }
      
      // readfd set 0 
      FD_ZERO(&readfds);
      // socketfd에 해당하는 readfds의 비트를 1로 한다.
      FD_SET(socketfd, &readfds);
      // 0번째 인자를 1로 set한다
      FD_SET(0&readfds);
     
      maxfd = socketfd;
      oldfds = readfds;
     
      while(1)
      {
          readfds = oldfds;
          fd_num = select(maxfd + 1&readfds, NULLNULLNULL);
          if(FD_ISSET(socketfd, &readfds))        // 초기 연결인지 아닌지 검사를 한다.
          {
              memset(buf, 0x00, MAXLINE);
              readn = read(socketfd, buf, MAXLINE);
              if(readn <= 0)
              {
                  return 1;
              }
              writen = write(1, buf, readn);
              if(writen != readn)
              {
                  return 1;
              }
          }
          if(FD_ISSET(0&readfds))                // raedfds의 0번째 인자가 set?
          {
              memset(buf, 0x00, MAXLINE);
              readn = read(0, buf, MAXLINE);
              printf("> %s", buf);
              if(readn <= 0)
              {
                  return 1;
              }
              writen = write(socketfd, buf, readn);
              if(readn != writen)
              {
                  return 1;
              }
          }
      }
    }
    cs


    참고

    1. https://gist.github.com/Alexey-N-Chernyshov/4634731 (추후에 공부해보면 좋을 것 같습니다)


    ※ 본 글은 개인 포트폴리오 혹은 공부용으로 사용하기 때문에, 무단 복사 유포는 금지하지만, 개인 공부 용도로는 얼마든지 사용하셔도 좋습니다



    반응형
Designed by Tistory.