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, -1, sizeof(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); } } } |
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, NULL, NULL, NULL); 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 (추후에 공부해보면 좋을 것 같습니다)
※ 본 글은 개인 포트폴리오 혹은 공부용으로 사용하기 때문에, 무단 복사 유포는 금지하지만, 개인 공부 용도로는 얼마든지 사용하셔도 좋습니다
'C_C++ 프로그래밍 > TCP_IP' 카테고리의 다른 글
[TCP/IP] 소켓 옵션을 사용해보자 (슬라이딩 윈도우 제어) (6) | 2019.06.21 |
---|---|
[C/C++] [TCP Sliding window] [슬라이딩 윈도우 개념] 슬라이딩 윈도우란? (0) | 2019.06.18 |
Blocking, Unblocking 봉쇄/비봉쇄 통신 (0) | 2019.06.06 |
멀티쓰레드를 이용한 소켓 통신 (0) | 2019.06.04 |
댓글