关于epoll的介绍以及与其它I/O模型的优缺点,各大网站论坛均有介绍,这里就不多说了;
本Simple使用了epoll的边缘出发模式(ET),支持多客户端连接;
首先定义自己的数据结构以及相关全局变量:
#define MAX_EVENTS 10 #define MAX_BUFFER_SIZE 1024 #define LISTEN_PORT 8000 struct global_data { int server_sock; int server_epoll; }; struct client_data { char *read_buf; char *write_buf; int read_len; int read_off; int write_len; int write_off; int fd; struct sockaddr_in addr; }; struct global_data g_gd; client_data结构体用来存储和处理客户端上下文数据;下面初始化一个新的客户端上下文结构:
struct client_data *alloc_client_data(int fd, struct sockaddr_in *addr = NULL) { struct client_data *cd = NULL; cd = (struct client_data *)malloc(sizeof(struct client_data)); if(cd == NULL) { return NULL; } memset(cd, 0, sizeof(struct client_data)); cd->fd = fd; cd->write_len = MAX_BUFFER_SIZE; cd->write_off = 0; cd->read_len = MAX_BUFFER_SIZE; cd->read_off = 0; cd->write_buf = (char *)malloc(cd->write_len + 1); cd->read_buf = (char *)malloc(cd->read_len + 1); assert(cd->write_buf); assert(cd->read_buf); if(addr) memcpy(&cd->addr, addr, sizeof(cd->addr)); return cd; } 释放客户端上下文数据: void free_client_data(struct client_data *cd) { if(cd == NULL) return ; close(cd->fd); if(cd->write_buf) free(cd->write_buf); if(cd->read_buf) free(cd->read_buf); free(cd); return ; } 设置socket为非阻塞模式: int set_sock_non_block(int fd) { int flags, result; flags = fcntl(fd, F_GETFL, 0); if(flags == -1) { return -1; } flags |= O_NONBLOCK; result = fcntl(fd, F_SETFL, flags); if(result == -1) { return -1; } return 0; } 处理客户端连接请求: int accept_client() { int ret = -1; int addrlen; int client_sock; struct sockaddr_in remote_addr; struct epoll_event e_event; do { while(1) { addrlen = sizeof(struct sockaddr_in); client_sock = accept(g_gd.server_sock, (struct sockaddr *)&remote_addr, (socklen_t *)&addrlen); if(client_sock == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { break; } else { printf("accept failed, error: %s \n", strerror(errno)); break; } } else { set_sock_non_block(client_sock); struct client_data *cd = alloc_client_data(client_sock, &remote_addr); assert(cd); e_event.events = EPOLLIN | EPOLLET; e_event.data.ptr = cd; if(epoll_ctl(g_gd.server_epoll, EPOLL_CTL_ADD, client_sock, &e_event) == -1) { printf("epoll_ctl client failed \n"); free_client_data(cd); } else { printf("accept client: %s:%d \n", inet_ntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port)); } } } ret = 0; }while(0); return ret; } 接收客户端数据: int recv_data(struct client_data *client) { int ret = -1; int len_recv = 0; struct epoll_event e_event; if(client == NULL) return -1; do { while(1) { len_recv = recv(client->fd, client->read_buf, client->read_len, 0); if(len_recv == -1) { if(errno == EAGAIN) { break; } else { free_client_data(client); break; } } else if(len_recv == 0) { printf("client closed connection!!! \n"); free_client_data(client); break; } else { client->read_buf[len_recv] = '\0'; printf("[%s:%d] recv data: %s:%d \n", inet_ntoa(client->addr.sin_addr), ntohs(client->addr.sin_port), client->read_buf, len_recv); client->read_off = len_recv; // send response strcpy(client->write_buf, "OK!"); client->write_off = strlen(client->write_buf); e_event.events = EPOLLOUT | EPOLLET; e_event.data.ptr = client; epoll_ctl(g_gd.server_epoll, EPOLL_CTL_MOD, client->fd, &e_event); } } ret = 0; }while(0); return ret; } 发送数据给客户端: int send_data(struct client_data *client) { int ret = -1; int len_send = 0; struct epoll_event e_event; if(client == NULL) return -1; do { len_send = send(client->fd, client->write_buf, client->write_off, 0); if(len_send == -1) { if(errno != EAGAIN) { printf("send error %s \n", strerror(errno)); free_client_data(client); } } else if(len_send == 0) { free_client_data(client); } else { e_event.events = EPOLLIN | EPOLLET; e_event.data.ptr = client; epoll_ctl(g_gd.server_epoll, EPOLL_CTL_MOD, client->fd, &e_event); } ret = 0; }while(0); return ret; } 服务器主程: void epoll_server() { int len_recv; int len_send; int len_sin; int signal_count; struct sockaddr_in local_addr; struct epoll_event e_event; struct epoll_event e_events[MAX_EVENTS]; char buff[MAX_BUFFER_SIZE]; memset(&g_gd, '\0', sizeof(struct global_data)); memset(buff, '\0', sizeof(buff)); do { // create server socket g_gd.server_sock = socket(AF_INET, SOCK_STREAM, 0); if(g_gd.server_sock < 0) { printf("create socket failed \n"); break; } // bind local_addr.sin_family = AF_INET; local_addr.sin_addr.s_addr = INADDR_ANY; local_addr.sin_port = htons(LISTEN_PORT); if(bind(g_gd.server_sock, (struct sockaddr *)&local_addr, sizeof(local_addr)) == -1 ) { printf("bind socket failed \n"); break; } // set non-block if(set_sock_non_block(g_gd.server_sock) == -1) { printf("set_sock_non_block failed \n"); break; } // listen if(listen(g_gd.server_sock, SOMAXCONN) == -1) { printf("listen fialed \n"); break; } // create epoll handle g_gd.server_epoll = epoll_create(MAX_EVENTS); if(g_gd.server_epoll < 0) { printf("epoll create failed \n"); break; } // register server socket event e_event.events = EPOLLIN | EPOLLET; e_event.data.fd = g_gd.server_sock; if(epoll_ctl(g_gd.server_epoll, EPOLL_CTL_ADD, g_gd.server_sock, &e_event) < 0) { printf("epoll_ctl failed \n"); break; } while(1) { printf("epoll wait... \n"); signal_count = epoll_wait(g_gd.server_epoll, e_events, MAX_EVENTS, -1); if(signal_count == -1) { printf("epoll_wait failed \n"); break; } printf("wait signals: %d \n", signal_count); for(int i=0; i<signal_count; ++i) { if(e_events[i].events & EPOLLERR || e_events[i].events & EPOLLHUP) { printf("socket error occured \n"); continue; } else if(e_events[i].data.fd == g_gd.server_sock) { accept_client(); } else if(e_events[i].events & EPOLLIN) { recv_data((struct client_data *)e_events[i].data.ptr); } else if(e_events[i].events & EPOLLOUT) { send_data((struct client_data *)e_events[i].data.ptr); } else { printf("Some error happend \n"); } } } }while(0); close(g_gd.server_sock); close(g_gd.server_epoll); return ; }
