首页
最新活动
服务器租用
香港服务器租用
台湾服务器租用
美国服务器租用
日本服务器租用
新加坡服务器租用
高防服务器
香港高防服务器
台湾高防服务器
美国高防服务器
裸金属
香港裸金属服务器
台湾裸金属服务器
美国裸金属服务器
日本裸金属服务器
新加坡裸金属服务器
云服务器
香港云服务器
台湾云服务器
美国云服务器
日本云服务器
CDN
CDN节点
CDN带宽
CDN防御
CDN定制
行业新闻
官方公告
香港服务器资讯
帮助文档
wp博客
zb博客
服务器资讯
联系我们
关于我们
机房介绍
机房托管
登入
注册
帮助文档
专业提供香港服务器、香港云服务器、香港高防服务器租用、香港云主机、台湾服务器、美国服务器、美国云服务器vps租用、韩国高防服务器租用、新加坡服务器、日本服务器租用 一站式全球网络解决方案提供商!专业运营维护IDC数据中心,提供高质量的服务器托管,服务器机房租用,服务器机柜租用,IDC机房机柜租用等服务,稳定、安全、高性能的云端计算服务,实时满足您的多样性业务需求。 香港大带宽稳定可靠,高级工程师提供基于服务器硬件、操作系统、网络、应用环境、安全的免费技术支持。
联系客服
服务器资讯
/
香港服务器租用
/
香港VPS租用
/
香港云服务器
/
美国服务器租用
/
台湾服务器租用
/
日本服务器租用
/
官方公告
/
帮助文档
TCP高并发服务器简介(select、poll、epoll实现与区别)
发布时间:2024-03-10 20:46:49 分类:帮助文档
TCP高并发服务器简介(select、poll、epoll实现与区别) select、poll、epoll三者的实现: select实现TCP高并发服务器的流程: 一、创建套接字(socket函数):二、填充服务器的网络信息结构体:三、套接字和服务器的网络信息结构体进行绑定(bind函数):四、套接字设置成被动监听(listen函数):五、创建要监听的文件描述符集合:使用select函数后,会将没有就绪的文件描述符在集合中去除,所以需要创建两个文件描述符集合,一个是母本read_fds,类似于常量,保持不变,而另一个作为副本read_fds_t,类似于变量,可以改变; fd_set read_fds; FD_ZERO(&read_fds); fd_set read_fds_t; FD_ZERO(&read_fds_t); 六、把创建的套接字添加到要监视的集合中: FD_SET(sockfd,&read_fds); int fd_max = 0; fd_max = fd_max > sockfd ? fd_max : sockfd; 七、设置系统时间结构体变量,用来指定超时的时间: struct timeval tm_out; 八、等待文件描述符中的事件是否就绪,成功则返回就绪的文件描述符的个数(select函数):select函数: #include
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); /* 参数: nfds: 要监视的最大文件描述符+1 readfds: 要监视的读文件描述符集合 不关心可以传NULL writefds: 要监视的写文件描述符集合 不关心可以传NULL exceptfds: 要监视的异常文件描述符集合 不关心可以传NULL timeout: 超时时间 如果设置成NULL 会一直阻塞 直到有文件描述符就绪 返回值: 成功 就绪的文件描述符的个数 超时 0 失败 -1 重置错误码 */ //struct timeval 可以指定超时时间 //如果结构体的两个成员都为0 表示非阻塞 struct timeval { long tv_sec; //秒 long tv_usec; //微秒 }; void FD_CLR(int fd, fd_set *set); //将文件描述符在集合中删除 int FD_ISSET(int fd, fd_set *set); //判断文件描述符是否还在集合中 // 返回0 表示不在了 非0 表示在 void FD_SET(int fd, fd_set *set); //向集合中添加一个文件描述符 void FD_ZERO(fd_set *set); //清空集合 if(-1 == (ret = select(fd_max + 1,&read_fds_t,NULL,NULL,&tm_out))) { perror("select error"); exit(-1); } else if(0 == ret) { puts("timeout!!!!!"); putchar('\n'); continue; } 九、遍历文件描述符集合,判断哪些文件描述符已经准备就绪: for(int i = 3; i < fd_max + 1 && 0 != ret; i++) { ... } 十、判断文件描述符是否还在集合中,并且接收来自客户端的数据(recv函数)和给客户端发送应答消息(send函数): if(FD_ISSET(i,&read_fds_t)) { //说明有新的客户端连接服务器 if(i == sockfd) { if(-1 == (accept_fd = accept(sockfd,NULL,NULL))) { perror("accept error"); exit(-1); } printf("客户端[%d]连接到服务器\n",accept_fd); //将新连接的客户端的套接字添加到要监视的集合中 FD_SET(accept_fd,&read_fds); fd_max = fd_max > accept_fd ? fd_max : accept_fd; } else //之前连接的客户端在向服务器发送信息 { memset(buf,0,sizeof(buf)); if(-1 == (nbytes = recv(i,buf,sizeof(buf),0))) { perror("recv error"); exit(-1); } else if(0 == nbytes) { printf("客户端[%d]已断开连接\n",i); //将已断开连接客户端的套接字在文件描述符集合中剔除 FD_CLR(i,&read_fds); //关闭套接字 close(i); continue; } if(!strncmp(buf,"quit",4)) { printf("客户端[%d]已退出\n",i); //将已断开连接客户端的套接字在文件描述符集合中剔除 FD_CLR(i,&read_fds); //关闭套接字 close(i); continue; } printf("客户端[%d]发来信息[%s]\n",i,buf); //组装应答消息 strcat(buf,"----------k"); //给客户端发送应答消息 if(-1 == send(i,buf,sizeof(buf),0)) { perror("send error"); exit(-1); } } ret--; //减少遍历次数 } 十一、关闭套接字(close函数): poll实现TCP高并发服务器的流程: 一、创建套接字(socket函数):二、填充服务器的网络信息结构体:三、套接字和服务器的网络信息结构体进行绑定(bind函数):四、套接字设置成被动监听(listen函数):五、创建要监听的文件描述符集合并清空文件描述符集合: //创建要监听的文件描述符集合 struct pollfd new_fds[2048] = {0}; //清空文件描述符集合 for(int i = 0; i < 2048; ++i) { new_fds[i].fd = -1; } 六、把创建的套接字添加到要监视的集合中: FD_SET(sockfd,&read_fds); int fd_max = 0; fd_max = fd_max > sockfd ? fd_max : sockfd; 七、套接字添加到要监视的集合中,并且设置要监听的事件: //套接字添加到要监视的集合中 new_fds[0].fd = sockfd; //设置要监听的事件 new_fds[0].events |= POLLIN; 八、记录文件描述符集合中最大的文件描述符,并且设置超时的时间: //记录文件描述符集合中最大的文件描述符 int fd_max = 0; fd_max = fd_max > sockfd ? fd_max : sockfd; //设置超时的时间 int tm_out = 10000; 九、等待文件描述符中的事件是否就绪,成功则返回就绪的文件描述符的个数(poll函数):poll实现TCP中型并发服务器与select实现TCP小型并发服务器的区别就是无需每次重置集合,并且可以设置要监视的最大文件描述符的个数,而select至多监视1024个文件描述符;poll函数: #include
int poll(struct pollfd *fds, nfds_t nfds, int timeout); /* 参数: fds:要监视的文件描述符的集合指向自定义的结构体数据 nfds:fds中已经使用的项目的个数 timeout:超时时间单位是毫秒 设置成10000 表示10s -1 永久阻塞 0 非阻塞 返回值: 0 超时 -1 出错 重置错误码 正数 成功 返回的就绪的文件描述符的个数 */ struct pollfd { int fd; /* 文件描述符 设置成-1 内核就不再监视这一位了*/ short events; /* 要监视的事件 */ short revents; /* 返回的事件 */ }; /* 要监视的事件是用位运算或起来的 要监视的事件放在events字段,而实际就绪的事件在revents字段返回 POLLIN 读事件 POLLOUT 写时间 POLLERR 异常事件 */ if(-1 == (ret = poll(new_fds,fd_max,tm_out))) { perror("poll error"); exit(-1); } else if(0 == ret) { puts("timeout!!!!!"); putchar('\n'); continue; } 十、遍历文件描述符集合,判断哪些文件描述符已经准备就绪: for(k = 0; k <= fd_max && ret != 0; ++k) { ... } 十一、找到实际就绪的事件的文件描述符,并且接收来自客户端的数据(recv函数)和给客户端发送应答消息(send函数): //找到实际就绪的事件的文件描述符 if(0 != (new_fds[k].revents & POLLIN)) { //说明有新的客户端连接服务器 if(new_fds[k].fd == sockfd) { if(-1 == (accept_fd = accept(sockfd,NULL,NULL))) { perror("accept error"); exit(-1); } printf("客户端[%d]连接到服务器\n",accept_fd); //将新连接的客户端的套接字添加到要监视的集合中 //遍历文件描述符集合,给新的文件描述符找一个位置 for(j = 0; j < 2048; j++) { if(-1 == new_fds[j].fd) { new_fds[j].fd = accept_fd; new_fds[j].events |= POLLIN; fd_max = fd_max > accept_fd ? fd_max : accept_fd; break; } } if(2048 == j) { //此时集合容量满了 close(accept_fd); } } else //之前连接的客户端在向服务器发送信息 { memset(buf,0,sizeof(buf)); if(-1 == (nbytes = recv(new_fds[k].fd,buf,sizeof(buf),0))) { perror("recv error"); exit(-1); } else if(0 == nbytes) { printf("客户端[%d]已断开连接\n",new_fds[k].fd); //将已断开连接客户端的套接字在文件描述符集合中剔除 close(new_fds[k].fd); new_fds[k].fd = -1; continue; } if(!strncmp(buf,"quit",4)) { printf("客户端[%d]已退出\n",new_fds[k].fd); //将已断开连接客户端的套接字在文件描述符集合中剔除 close(new_fds[k].fd); new_fds[k].fd = -1; continue; } printf("客户端[%d]发来信息[%s]\n",new_fds[k].fd,buf); //组装应答消息 strcat(buf,"----------k"); //给客户端发送应答消息 if(-1 == send(new_fds[k].fd,buf,sizeof(buf),0)) { perror("send error"); exit(-1); } } ret--; //减少遍历次数 } 十二、关闭套接字(close函数): epoll实现TCP高并发服务器的流程: 一、创建套接字(socket函数):通信域选择IPV4网络协议、套接字类型选择流式; int sock_fd = socket(AF_INET,SOCK_STREAM,0); //通信域选择IPV4、套接字类型选择流式 二、填充服务器和客户机的网络信息结构体:1.分别定义服务器网络信息结构体变量serveraddr和客户机网络信息结构体变量clientaddr;2.分别求出服务器和客户机的网络信息结构体变量的内存空间大小,以作备用;3.网络信息结构体清0;4.使用IPV4网络协议AF_INET;5.在终端预留服务器端主机的IP地址:inet_addr(argv[1]);6.在终端预留服务器端网络字节序的端口号:htons(atoi(argv[2])); struct sockaddr_in serveraddr; //定义服务器网络信息结构体变量 struct sockaddr_in clientaddr; socklen_t serveraddr_len = sizeof(serveraddr);//求出服务器结构体变量的内存空间大小 socklen_t clientaddr_len = sizeof(clientaddr);//求出客户机结构体变量的内存空间大小 memset(&serveraddr,0,serveraddr_len); //服务器结构体清零 memset(&clientaddr,0,clientaddr_len);//客户机结构体清零 serveraddr.sin_family = AF_INET; //使用IPV4网络协议 serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //IP地址 serveraddr.sin_port = htons(atoi(argv[2]));//网络字节序的端口号 三、设置允许端口复用(setsockopt函数):setsockopt函数:功能:设置套接字属性; #include
#include
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); /* 参数: sockfd:套接字 level: 选项的级别 套接字API级别 SOL_SOCKET TCP级别 IPPROTO_TCP IP级别 IPPROTO_IP optname:选项的名字 套接字API级别 SO_BROADCAST 是否允许发送广播 SO_RCVBUF 接收缓冲区的大小 SO_SNDBUF 发送缓冲区的大小 SO_RCVTIMEO 接收超时时间 参数使用的是 struct timeval 结构体 如果超时了 函数调用会立即返回-1 并将错误码置成 EAGAIN SO_SNDTIMEO 发送超时时间 SO_REUSEADDR 端口复用 TCP级别 TCP_NODELAY 使能/禁用Nagle算法 IP级别 IP_ADD_MEMBERSHIP 设置加入多播组 optval: 选项的值 没有特殊说明时 使用的都是int类型 optlen:optval的大小 返回值: 成功 0 失败 -1 重置错误码 */ 特别注意:使用setsockopt设置允许端口复用时,其在代码的位置在填充网络信息结构体和bind之间; int reuse = 1; if(-1 == (setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))) { perror("setsockopt error"); exit(-1); } 四、套接字和服务器的网络信息结构体进行绑定(bind函数): 五、套接字设置成被动监听(listen函数): 六、创建红黑树(epoll_create函数): #include
int epoll_create(int size); /* 功能: 创建epoll/创建epoll实例的描述符 参数: size:参数已经被忽略了,只需要填写大于0的值即可 返回值: epoll_create 调用成功时会返回一个非负整数epfd, 表示新创建的 epoll 实例的文件描述符, 如果调用失败则返回 -1,并设置 errno 变量以指示具体错误原因 */ int epfd = epoll_create(1); if(-1 == epfd) { perror("epoll_create error"); exit(-1); } 七、定义事件结构体变量和存放就绪事件描述符的数组:事件结构体:epoll_event用于描述一个文件描述符上的事件; typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; //EPOLLIN 读 / EPOLLOUT 写 epoll_data_t data; //存放用户的数据 }; struct epoll_event event; struct epoll_event events[N]; 八、将关心的文件描述符加入到红黑树(epoll_ctl函数):功能:epoll的控制操作或者用于向 epoll 实例中添加、修改、删除事件;epoll_ctl函数: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); /* 参数: epfd:epoll的文件描述符 op:控制方式 EPOLL_CTL_ADD:添加 EPOLL_CTL_MOD:修改 EPOLL_CTL_DEL:删除 fd:被操作的文件描述符 event:(事件)结构体指针 返回值: 成功返回0, 失败返回-1 置位错误码 */ //添加要检测事件的描述符 event.events = EPOLLIN; event.data.fd = sock_fd; //将关心的文件描述符加入到红黑树 if(-1 == (epoll_ctl(epfd,EPOLL_CTL_ADD,sock_fd,&event))) { perror("epoll_ctl error"); exit(-1); } 九、等待文件描述符中的事件是否就绪,成功则返回就绪的文件描述符的个数(epoll_wait函数):epoll_wait函数: int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); /* 参数: epfd:epoll的文件描述符 events:准备好的事件的结构体地址 maxevents:返回的最大的文件描述符的个数 timeout:超时 >0 :毫秒级别的超时时间 =0 :立即返回 =-1:不关心超时时间 返回值: 成功返回准备好的文件描述符的个数 返回0代表超时时间到了 失败返回-1置位错误码 */ if(-1 == (ret = epoll_wait(epfd,events,N,-1))) { perror("epoll_wait error"); exit(-1); } 十、遍历就绪的文件描述符集,判断哪些文件描述符已经准备就绪: for(int i = 0; i < ret; ++i) { ... } 十一、找到实际就绪的事件的文件描述符,并且接收来自客户端的数据(recv函数)和给客户端发送应答消息(send函数): if(events[i].data.fd == sock_fd) { //获取连接成功后新的客户端 new_fd = accept(sock_fd,(struct sockaddr *)&clientaddr,&clientaddr_len); if(-1 == new_fd) { perror("accept error"); exit(-1); } printf("文件描述符[%d]客户端[%s:%d]连接到了服务器\n",new_fd,inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port)); //添加要检测的文件描述符 event.events = EPOLLIN; event.data.fd = new_fd; if(-1 == (epoll_ctl(epfd,EPOLL_CTL_ADD,new_fd,&event))) { perror("epoll_ctl error"); exit(-1); } printf("文件描述符[%d]成功挂载在红黑树上\n",new_fd); } else { memset(buf,0,sizeof(buf)); int old_fd = events[i].data.fd; if(-1 == (nbytes = recv(old_fd,buf,sizeof(buf),0))) { perror("recv error"); exit(-1); } else if(0 == nbytes) { printf("文件描述符[%d]客户端断开了服务器\n",old_fd); //关闭对应的文件描述符 close(old_fd); //剔除挂在树上对应的文件描述符 epoll_ctl(epfd,EPOLL_CTL_DEL,old_fd,&event); } if(!strncmp(buf,"quit",4)) { printf("文件描述符[%d]客户端退出了服务器\n",old_fd); //关闭对应的文件描述符 close(old_fd); //剔除挂在树上对应的文件描述符 epoll_ctl(epfd,EPOLL_CTL_DEL,old_fd,&event); } printf("文件描述符[%d]客户端发来数据[%s]\n",old_fd,buf); //组装应答消息 strcat(buf,"-----k"); //给客户端发送应答消息 send(old_fd,buf,sizeof(buf),0); 十二、关闭套接字(close函数): select、poll、epoll三者的区别:
上一篇
香港免备案服务器租用
下一篇
香港和记电讯官网
相关文章
最新技术整理3款开源免费直播推流工具,实现实时视频推流、视频拉流,目标端可以是服务器、云平台、移动设备等(附源码)
win2003安全策略怎么打开
RHCE DNS域名解析服务器
服务器和CDN推荐
python套接字(二):实现一个服务器和多客户端连接
电商怎么选择服务器6
java上传文件到指定服务器
百度云怎么选择代理商
服务器租用与服务器托管
香港云服务器租用推荐
服务器租用资讯
·广东云服务有限公司怎么样
·广东云服务器怎么样
·广东锐讯网络有限公司怎么样
·广东佛山的蜗牛怎么那么大
·广东单位电话主机号怎么填写
·管家婆 花生壳怎么用
·官网域名过期要怎么办
·官网邮箱一般怎么命名
·官网网站被篡改怎么办
服务器租用推荐
·美国服务器租用
·台湾服务器租用
·香港云服务器租用
·香港裸金属服务器
·香港高防服务器租用
·香港服务器租用特价
7*24H在线售后
高可用资源,安全稳定
1v1专属客服对接
无忧退款试用保障
德讯电讯股份有限公司
电话:00886-982-263-666
台湾总部:台北市中山区建国北路一段29号3楼
香港分公司:九龙弥敦道625号雅兰商业二期906室
服务器租用
香港服务器
日本服务器
台湾服务器
美国服务器
高防服务器购买
香港高防服务器出租
台湾高防服务器租赁
美国高防服务器DDos
云服务器
香港云服务器
台湾云服务器
美国云服务器
日本云服务器
行业新闻
香港服务器租用
服务器资讯
香港云服务器
台湾服务器租用
zblog博客
香港VPS
关于我们
机房介绍
联系我们
Copyright © 1997-2024 www.hkstack.com All rights reserved.