poll epoll简单的回显服务器
1.poll io服务器(单线程处理所有请求)
server.c
#include <stdio.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <stdlib.h> #include <arpa/inet.h> #include <sys/time.h> #include <unistd.h> #include <sys/select.h> #include <strings.h> #include <string.h> #include <fcntl.h> #include <netdb.h> #include <pthread.h> #include <unistd.h> #include <arpa/inet.h> /* inet(3) functions */ #include <poll.h> /* poll function */ #include <limits.h> #include "pollio_server.h" static struct pollfd client_fds[MAX_SELECT_CLIENT_NUM] = {0}; static reg_rcv_handle_call_back callbackfunc = NULL; static int server_create(int port); static void *poll_loop(void *arg); /***************************************** *func:server_init *describe:初始化服务器端 *para: port 端口号 *return:成功则返回0,error则返回RET_VAL_FAILED *****************************************/ int server_init(const int port, const reg_rcv_handle_call_back func) { int cnt = 10; static int ret_fd = -1; int ret; for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++) { client_fds[i].fd=-1; } while (cnt--) { ret_fd = server_create(port); if (RET_VAL_FAILED != ret_fd) { if (NULL == callbackfunc) callbackfunc = func; break; } usleep(100 * 1000); } if (ret_fd < 0) { return RET_VAL_FAILED; } cnt = 10; while (cnt--) { pthread_t loop_thread_id; ret = pthread_create(&loop_thread_id, NULL, poll_loop, &ret_fd); if (0 == ret) { pthread_detach(loop_thread_id); printf("init done ,wait client connect...\n"); return RET_VAL_SUCCESS; } } return RET_VAL_FAILED; } /***************************************** *func:server_create *describe:创建监听的fd *para: port 端口号 *return: 成功则返回监听的fd,error则返回RET_VAL_FAILED *****************************************/ int server_create(int in_port) { struct addrinfo hints; struct addrinfo *result, *rp; int server_fd, retval; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; // Return IPv4 and IPv6 choices. hints.ai_socktype = SOCK_STREAM; // We want a TCP socket. hints.ai_flags = AI_PASSIVE; // All interfaces. char port[16]={0}; snprintf(port,sizeof(port),"%d",in_port); //数填写的套接口地址结构,port可以是http ftp字符 retval = getaddrinfo(NULL, port, &hints, &result); if (retval != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(retval)); return RET_VAL_FAILED; } for (rp = result; rp != NULL; rp = rp->ai_next) { server_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (server_fd == RET_VAL_FAILED) continue; ////设置端口复用,防止被占用,服务器退出时再次绑定不会影响再次绑定 int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(int)) < 0) { printf(" set server_fd option SO_REUSEADDR error !"); goto failed; } //bind soucket retval = bind(server_fd, rp->ai_addr, rp->ai_addrlen); if (retval == 0) { // We managed to bind successfully! break; } goto failed; } if (rp == NULL) { fprintf(stderr, "server_fd could not bind\n"); goto failed; } freeaddrinfo(result); //设置不阻塞模式 int flags, s; flags = fcntl(server_fd, F_GETFL, 0); if (flags == RET_VAL_FAILED) { perror("server_fd fcntl set error ,F_GETFL\n"); goto failed; } flags |= O_NONBLOCK; s = fcntl(server_fd, F_SETFL, flags); if (s == RET_VAL_FAILED) { perror("server_fd fcntl set error F_SETFL\n"); goto failed; } //绑定端口号 retval = listen(server_fd, SOMAXCONN); if (retval == RET_VAL_FAILED) { perror("server_fd listen error\n"); goto failed; } client_fds[0].fd=server_fd;//监听fd client_fds[0].events = POLLRDNORM; return server_fd; failed: close(server_fd); return RET_VAL_FAILED; } void *poll_loop(void *arg) { int listen_fd = *((int *)arg); int max_fd = 0; int retval; int wait_timeout_ms=1000;//1s //select多路复用 while (1) { max_fd = listen_fd; for (int i = 1; i < MAX_SELECT_CLIENT_NUM; i++) { if (client_fds[i].fd > 0) { if (max_fd <= client_fds[i].fd) { max_fd = client_fds[i].fd; } } } retval = poll(client_fds,max_fd+1,wait_timeout_ms); if (retval > 0) { if ((client_fds[0].revents)&POLLRDNORM) //有新的客户端连接 { struct sockaddr_in client_address; socklen_t address_len; int client_sock_fd = accept(listen_fd, (struct sockaddr *)&client_address, &address_len); if (client_sock_fd > 0) { int flags = RET_VAL_FAILED; //一个客户端到来分配一个fd,超过CLI_NUM以后跳出for循环,flags重新被赋值为RET_VAL_FAILED for (int i = 1; i < MAX_SELECT_CLIENT_NUM; i++) { if (client_fds[i].fd <= 0) { flags = i; client_fds[i].fd = client_sock_fd; client_fds[i].events= POLLRDNORM; client_fds[i].revents = 0;//目前还没有任何事件返回,置为零 break; } } if (flags != RET_VAL_FAILED) { printf("new user client[%d]=%d add sucessfully!\n", flags, client_sock_fd); } else //flags=RET_VAL_FAILED { printf("the client nums is more than %d, can't login.close now!\n", MAX_SELECT_CLIENT_NUM); close(client_sock_fd); } } } else { //deal with the message for (int i = 1; i < MAX_SELECT_CLIENT_NUM; i++) { if ((client_fds[i].revents)&POLLRDNORM) { char rcv_buff[MAX_RCV_BUFF_SIZE] = {0}; int len = recv(client_fds[i].fd, rcv_buff, MAX_RCV_BUFF_SIZE, MSG_DONTWAIT); if (len > 0) { int send_len = send(client_fds[i].fd, rcv_buff, len, MSG_DONTWAIT); printf("message form client[%d]=%d,rcv len%d,send_len=%d\n", i, client_fds[i].fd, len, send_len); if (NULL != callbackfunc) callbackfunc(client_fds[i].fd, rcv_buff, len); continue; } else if (len <= 0) { if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { continue; } close(client_fds[i].fd); client_fds[i].fd = -1; if (0 == len) { printf("clien[%d] exit by itself!bye\n", i); } else { //printf("clien[%d] is error!\n", i); } } } } } } if (retval < 0) { printf("poll error:%s\n", strerror(errno)); continue; } else if (0 == retval) { //printf("poll timeout!\n"); continue; } } } void get_all_client(int out_clients[], const int size) { if (size > MAX_SELECT_CLIENT_NUM) { for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++) { out_clients[i] = client_fds[i].fd; } } else { for (int i = 0; i < size; i++) { out_clients[i] = client_fds[i].fd; } } }
server.h
#ifndef _pollio_server_H_ #define _pollio_server_H_ #ifdef __cplusplus extern "C" { #endif #ifndef RET_VAL_SUCCESS #define RET_VAL_SUCCESS (0) #endif #ifndef RET_VAL_FAILED #define RET_VAL_FAILED (-1) #endif #ifndef MAX_SELECT_CLIENT_NUM #define MAX_SELECT_CLIENT_NUM (50200)//数量无限制 #endif #ifndef MAX_RCV_BUFF_SIZE #define MAX_RCV_BUFF_SIZE (4096*100) #endif typedef struct _client_info_t { int fd; int login_id; }client_info_t; typedef int (*reg_rcv_handle_call_back)(const int fd,const void *data,const int data_len); /***************************************** *func:server_init *describe:初始化服务器端 *para: port 端口号 *return:成功则返回0,error则返回-1 *****************************************/ int server_init(const int port,const reg_rcv_handle_call_back func); void get_all_client(int out_clients[],const int size); #ifdef __cplusplus } #endif #endif
main.c
#include <stdio.h> #include <stdio.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <stdlib.h> #include <arpa/inet.h> #include <sys/time.h> #include <unistd.h> #include <sys/select.h> #include <strings.h> #include <string.h> #include <fcntl.h> #include <netdb.h> #include <pthread.h> #include "pollio_server.h" int sysUsecTime(char *pTime) { struct timeval tv; struct timezone tz; struct tm *p; gettimeofday(&tv, &tz); p = localtime(&tv.tv_sec); sprintf(pTime, "%04d-%02d-%02d-%02d:%02d:%02d:.%06ld", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, tv.tv_usec); return 0; } int handle_call_back(const int fd,const void *data,const int data_len) { static int cat_fd=5; if(cat_fd==fd) { char pTime[64]={0}; sysUsecTime(pTime); printf("[%s]:fd=%d,len=%d\n",pTime,fd,data_len); } } int main(int argc, char **argv) { server_init(11111, handle_call_back); int out_clients[1024]={0}; while (1) { #if 0 get_all_client(out_clients, 1024); int print_flag=-1; for (int i = 0; i < 1024; i++) { if (out_clients[i] > 0) { if(print_flag<0) printf("client:"); print_flag=1; printf("client[%d]=%d ",i ,out_clients[i]); } } if(print_flag>0) printf("\n"); #endif sleep(5); } }
2.epoll io 服务器(单线程)
#include <stdio.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <stdlib.h> #include <arpa/inet.h> #include <sys/time.h> #include <unistd.h> #include <sys/select.h> #include <strings.h> #include <string.h> #include <fcntl.h> #include <netdb.h> #include <pthread.h> #include <unistd.h> #include <arpa/inet.h> /* inet(3) functions */ #include <sys/epoll.h> #include <limits.h> #include "epollio_server.h" #define MAX_EPOOL_EVENTS (1024) static int client_fds[MAX_SELECT_CLIENT_NUM] = {0}; static reg_rcv_handle_call_back callbackfunc = NULL; static int server_create(int port); static int epoll_init(int listen_fd); static void *epool_loop(void *arg); /***************************************** *func:server_init *describe:初始化服务器端 *para: port 端口号 *return:成功则返回0,error则返回RET_VAL_FAILED *****************************************/ int server_init(const int port, const reg_rcv_handle_call_back func) { int cnt = 10; static int ret_fd = -1; int ret; for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++) { client_fds[i] = -1; } while (cnt--) { ret_fd = server_create(port); if (RET_VAL_FAILED != ret_fd) { if (NULL == callbackfunc) callbackfunc = func; break; } usleep(100 * 1000); } if (ret_fd < 0) { return RET_VAL_FAILED; } cnt = 10; while (cnt--) { int ret =epoll_init(ret_fd); if (RET_VAL_FAILED != ret) { printf("init done ,wait client connect...\n"); return RET_VAL_SUCCESS; } usleep(100 * 1000); } return RET_VAL_FAILED; } /***************************************** *func:server_create *describe:创建监听的fd *para: port 端口号 *return: 成功则返回监听的fd,error则返回RET_VAL_FAILED *****************************************/ int server_create(int in_port) { struct addrinfo hints; struct addrinfo *result, *rp; int server_fd, retval; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; // Return IPv4 and IPv6 choices. hints.ai_socktype = SOCK_STREAM; // We want a TCP socket. hints.ai_flags = AI_PASSIVE; // All interfaces. char port[16] = {0}; snprintf(port, sizeof(port), "%d", in_port); //数填写的套接口地址结构,port可以是http ftp字符 retval = getaddrinfo(NULL, port, &hints, &result); if (retval != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(retval)); return RET_VAL_FAILED; } for (rp = result; rp != NULL; rp = rp->ai_next) { server_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (server_fd == RET_VAL_FAILED) continue; ////设置端口复用,防止被占用,服务器退出时再次绑定不会影响再次绑定 int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(int)) < 0) { printf(" set server_fd option SO_REUSEADDR error !"); goto failed; } //bind soucket retval = bind(server_fd, rp->ai_addr, rp->ai_addrlen); if (retval == 0) { // We managed to bind successfully! break; } goto failed; } if (rp == NULL) { fprintf(stderr, "server_fd could not bind\n"); goto failed; } freeaddrinfo(result); //设置不阻塞模式 int flags, s; flags = fcntl(server_fd, F_GETFL, 0); if (flags == RET_VAL_FAILED) { perror("server_fd fcntl set error ,F_GETFL\n"); goto failed; } flags |= O_NONBLOCK; s = fcntl(server_fd, F_SETFL, flags); if (s == RET_VAL_FAILED) { perror("server_fd fcntl set error F_SETFL\n"); goto failed; } //绑定端口号 retval = listen(server_fd, SOMAXCONN); if (retval == RET_VAL_FAILED) { perror("server_fd listen error\n"); goto failed; } client_fds[0] = server_fd; //监听fd return server_fd; failed: close(server_fd); return RET_VAL_FAILED; } int epoll_init(int listen_fd) { static int listenfd=0; listenfd=listen_fd; int cnt = 10; int ret = -1; while (cnt--) { pthread_t loop_thread_id; ret = pthread_create(&loop_thread_id, NULL, epool_loop, &listenfd); if (0 == ret) { pthread_detach(loop_thread_id); return RET_VAL_SUCCESS; } usleep(10 * 1000); } return RET_VAL_FAILED; } void *epool_loop(void *arg) { int listen_fd = *((int *)arg); int max_fd = 0; int retval; int wait_timeout_ms = 1000; //1s int ret = -1; int efd; struct epoll_event event; struct epoll_event *events; //除了参数size被忽略外,此函数和epoll_create完全相同 efd = epoll_create1(0); if (efd == -1) { perror("epoll_create error,exit"); exit(-1); } event.data.fd = *((int *)arg); event.events = EPOLLIN | EPOLLET; //读入,边缘触发方式 ret = epoll_ctl(efd, EPOLL_CTL_ADD, listen_fd, &event); if (ret == -1) { perror("epoll_ctl error,exit"); exit(-1); } /* Buffer where events are returned */ events = calloc(MAX_EPOOL_EVENTS, sizeof(event)); //select多路复用 while (1) { int n = 0, i = 0; n = epoll_wait(efd, events, MAX_EPOOL_EVENTS, -1); for (i = 0; i < n; i++) { if ((events[i].events & EPOLLIN)) { if (listen_fd != events[i].data.fd) { int len=-1; do { char rcv_buff[MAX_RCV_BUFF_SIZE] = {0}; len = recv(events[i].data.fd, rcv_buff, MAX_RCV_BUFF_SIZE, MSG_DONTWAIT); if (len > 0) { int send_len = send(events[i].data.fd, rcv_buff, len, MSG_DONTWAIT); //printf("message form client=%d,rcv len%d,send_len=%d\n", events[i].data.fd, len, send_len); if (NULL != callbackfunc) callbackfunc(events[i].data.fd, rcv_buff, len); continue; } else if (len <= 0) { if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { continue; } close(events[i].data.fd); for(int k=0;k<MAX_SELECT_CLIENT_NUM;k++) { if( client_fds[k]==events[i].data.fd) { client_fds[k]=-1; break; } } if (0 == len) { printf("clien[%d] exit by itself!bye\n", events[i].data.fd); } else { printf("clien[%d] is error!\n", events[i].data.fd); } events[i].data.fd = -1; } } while (len==MAX_RCV_BUFF_SIZE); } else { struct sockaddr in_addr; socklen_t in_len; int infd; char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; in_len = sizeof in_addr; infd = accept(listen_fd, &in_addr, &in_len); if (infd == -1) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { /* We have processed all incoming connections. */ continue; } else { perror("accept error\n"); continue; } } //将地址转化为主机名或者服务名 ret = getnameinfo(&in_addr, in_len, hbuf, sizeof hbuf, sbuf, sizeof sbuf, NI_NUMERICHOST | NI_NUMERICSERV); //主机地址和服务地址,flag参数:以数字名返回 if (ret == 0) { printf("Accepted connection on descriptor %d (host=%s, port=%s)\n", infd, hbuf, sbuf); } else if (ret == -1) { perror("accept:getnameinfo error \n"); close(infd); continue; } event.data.fd = infd; event.events = EPOLLIN | EPOLLET; ret = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event); if (ret == -1) { perror("epoll_ctl error ,"); close(infd); continue; } for(int k=0;k<MAX_SELECT_CLIENT_NUM;k++) { if(-1== client_fds[k]) { client_fds[k]=infd; break; } } } } else if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP)) { /* An error has occured on this fd, or the socket is not ready for reading (why were we notified then?) */ fprintf(stderr, "epoll error\n"); close(events[i].data.fd); for(int k=0;k<MAX_SELECT_CLIENT_NUM;k++) { if( client_fds[k]==events[i].data.fd) { client_fds[k]=-1; break; } } continue; } } } free(events); close(listen_fd); } void get_all_client(int out_clients[], const int size) { if (size > MAX_SELECT_CLIENT_NUM) { for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++) { out_clients[i] = client_fds[i]; } } else { for (int i = 0; i < size; i++) { out_clients[i] = client_fds[i]; } } }
3.测试客户端
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <pthread.h> #include <stdio.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <stdlib.h> #include <arpa/inet.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <sys/select.h> #include <strings.h> #include <string.h> #define MAXLINE 4096*100 int sockfd[1000] = {0}; char recvLine[MAXLINE], sendLine[MAXLINE]; void *recv_func(void *arg) { fd_set ser_fdset; int max_fd =2; struct timeval mytime; while (1) { max_fd =2; FD_ZERO(&ser_fdset); for (int i = 0; i < 1000; i++) { if (sockfd[i] > 0) { FD_SET(sockfd[i], &ser_fdset); if(sockfd[i]>max_fd) { max_fd=sockfd[i]; } } } mytime.tv_sec = 1; int ret = select(max_fd+1, &ser_fdset, NULL, NULL, &mytime); if (ret < 0) { perror("select failure\n"); continue; } else if (ret == 0) { //printf("time out! \n"); continue; } else { for (int i = 0; i < 1000; i++) { if (FD_ISSET(sockfd[i], &ser_fdset)) { int len = -1; memset(recvLine, 0, sizeof(recvLine)); if (sockfd[i] > 0) { len = recv(sockfd[i], recvLine, sizeof(recvLine), MSG_DONTWAIT); printf("recv len=%d\n", len); } } } } } } int main(int argc, char **argv) { int n; struct sockaddr_in servaddr; while (1) { static int i = 0; if ((sockfd[i] = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("create socket error: %s(errno: %d)\n", strerror(errno), errno); return 0; } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(11111); if (inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr) <= 0) { printf("inet_pton error for 127.0.0.1\n"); return 0; } if (connect(sockfd[i], (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { //客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。connect()成功返回0,出错返回-1 printf("connect error: %s(errno: %d)\n", strerror(errno), errno); sockfd[i]=-1; } usleep(1 * 100); i++; if (i >= 1000) break; } printf("send msg to server: \n"); memset(sendLine, 0, sizeof(sendLine)); for (int i = 0; i < MAXLINE; i++) { sendLine[i] = 'c'; } sendLine[MAXLINE - 1] = 0; int len = strlen(sendLine); pthread_t recv_pth; pthread_create(&recv_pth, NULL, recv_func, NULL); pthread_detach(recv_pth); while (1) { for (int i = 0; i < 1000; i++) { if (sockfd[i] > 0) { int ret = send(sockfd[i], sendLine, len, MSG_DONTWAIT); if (ret < 0) { printf("send msg error: %s(errno: %d)\n", strerror(errno), errno); } printf("send len=%d\n", ret); usleep(3); } } } for (int i = 0; i < 1000; i++) close(sockfd[i]); return 0; }
测试结果:
1000个客户端连接,每3微秒发100K数据
100个客户端连接,每3微秒发100K数据