赞
踩
目录
1.5 poll实现多个(客户)client端连接(服务器)server端
2.4 epoll实现多个(客户)client端连接(服务器)server端
- poll与select非常类似
- poll没有最大描述符号限制
- select在操作描述符号时使用描述符号集合fd_set, poll在操作描述符号时使用pollfd结构体链表或者数组
- //1. 创建fd结构体数组
- struct pollfd fds[300];
- int fdNum = 0; //当前描述符号数量
- //2. 把要监视的描述符号设置好
- fds[0].fd = 0;
- fds[0].events = POLLIN;
- fdNum++;
- //3. 监视
- int r = poll(fds,fdNum,0);
- if(-1 == r){
- //错误
- }else if(0 == r){
- //没有动静
- continue;
- }else{
- //有动静,检查对应事件
- if(fds[0].revents & POLLIN){
-
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- #include <stdio.h>
- #include <unistd.h>
- #include <poll.h>
- #include <string.h>
-
- int main(){
- //1. 创建fd结构体数组
- struct pollfd fds[8];
- int fdNum = 0; //当前描述符号数量
- //2. 把要监视的描述符号设置好
- fds[0].fd = 0;
- fds[0].events = POLLIN;
- fdNum++;
- //3. 监视
- int r;
- char buff[1024];
-
- while(1){
- r = poll(fds,fdNum,0);
- if(-1 == r){
- //错误
- printf("监视出错!\n");
- }else if(0 == r){
- //没有动静
- continue;
- }else{
- printf("有动静!\n");
- //有动静,检查对应事件
- if(fds[0].revents & POLLIN){
- memset(buff,0,1024);
- scanf("%s",buff);
- printf(">> %s\n",buff);
- }
- }
- }
-
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- //需要的头文件
- #include <poll.h>
-
- struct pollfd{
- int fd; //文件描述符号
- short events; //请求的事件
- short revents; //返回的事件
- }
-
- int poll(struct pollfd *fds, //链表头指针
- nfds_t nfds, //链表节点数
- int timeout); //延时
- //事件宏
- POLLIN //有数据可读
- POLLRDNORM //有普通数据可读
- POLLRDBAND //有优先数据可读
-
- POLLPRI //有紧急数据可读
-
- POLLOUT //写数据不会阻塞,可写数据
- POLLWRNORM //可以写普通数据
- POLLWRBAND //可以写优先数据
-
- POLLMSGSIGPOLL //消息可以使用
- POLLER //指定的fd出现错误
- POLLHUP //指定的fd被挂起
- POLLNVAL //指定的fd是非法的
服务器(server端)
- //服务器端
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <string.h>
- #include <signal.h>
- #include <poll.h>
-
- //最多允许的客户端数量
- #define NUM 100
-
- int serverSocket;
-
- void hand(int val){
- close(serverSocket);
- printf("bye bye!\n");
- exit(0);
- }
- int main(int argc,char* argv[]){
- if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
- printf("ip: %s port:%d\n",argv[1],atoi(argv[2]));
-
- signal(SIGINT,hand);
-
- //1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
- serverSocket = socket(AF_INET,SOCK_STREAM,0);
- if(-1 == serverSocket) printf("创建socket失败:%m\n"),exit(-1);
- printf("创建socket成功!\n");
-
- //2. 创建服务器协议地址簇
- struct sockaddr_in sAddr = { 0 };
- sAddr.sin_family = AF_INET; //协议类型 和socket函数第一个参数一致
- sAddr.sin_addr.s_addr = inet_addr(argv[1]); //将字符串转整数
- sAddr.sin_port = htons(atoi(argv[2])); //将字符串转整数,再将小端转换成大端
-
- //3. 绑定服务器协议地址簇
- int r = bind(serverSocket,(struct sockaddr*)&sAddr,sizeof sAddr);
- if(-1 == r) printf("绑定失败:%m\n"),close(serverSocket),exit(-2);
- printf("绑定成功!\n");
-
- //4. 监听
- r = listen(serverSocket,10); //数量
- if(-1 == r) printf("监听失败:%m\n"),close(serverSocket),exit(-3);
- printf("监听成功!\n");
-
- //监视serverSocket和ClientSocket
- struct sockaddr_in cAddr = {0};
- int len = sizeof(cAddr);
- int cfd; //临时保存一个客户端fd
- char buff[1024];
- int fdNum = 0;
-
- //准备一个pollfd数组
- struct pollfd fds[NUM];
- //初始化数组
- for(int i = 0;i < NUM; i++){
- fds[i].fd = -1;
- fds[i].events = POLLIN;
- }
-
- //将serverSocket放进去
- fds[0].fd = serverSocket;
- fds[0].events = POLLIN; //监视是否有数据可读
- fdNum++;
-
- while(1){
- r = poll(fds,fdNum,10); //延时10ms
- if(-1 == r){
- printf("监视失败:%m\n"),close(serverSocket),exit(-1);
- }else if(0 == r){
- continue;
- }else{
- //有动静
- if(fds[0].revents & POLLIN){
- //有客户端连接服务器的动作
- cfd = accept(serverSocket,(struct sockaddr*)&cAddr,&len);
- if(-1 == cfd){
- printf("server Error!\n");
- continue;
- }else{
- printf("客户端%d -- %s连接上服务器了!\n",cfd,inet_ntoa(cAddr.sin_addr));
- }
- //将客户端的描述符号添加到监视数组中
- for(int i = 1; i < NUM; i++){
- if(-1 == fds[fdNum].fd){
- fds[fdNum].fd = cfd;
- fds[fdNum].events = POLLIN;
- fdNum++;
- break;
- }
- }
- }
- }
- //检查客户端动静,看是否有数据发送过来
- for(int i = 1; i < NUM; i++){
- if(-1 != fds[i].fd && (fds[i].revents & POLLIN)){
- r = recv(fds[i].fd,buff,1023,0);
- if(r > 0){
- buff[r] = 0;
- printf("%d >> %s\n",fds[i].fd,buff);
- }else{
- //客户端掉线了
- fds[i].fd = -1;
- fdNum--;
- }
- }
- }
- }
-
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
客户(client)端
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <string.h>
- #include <signal.h>
-
- int clientSocket;
- void hand(int val){
- //5. 关闭连接
- close(clientSocket);
- printf("bye bye!\n");
- exit(0);
- }
- int main(int argc,char* argv[]){
- if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
- printf("ip: %s port:%d\n",argv[1],atoi(argv[2]));
-
- signal(SIGINT,hand);
-
- //1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
- clientSocket = socket(AF_INET,SOCK_STREAM,0);
- if(-1 == clientSocket) printf("创建socket失败:%m\n"),exit(-1);
- printf("创建socket成功!\n");
-
- //2. 创建服务器协议地址簇
- struct sockaddr_in cAddr = { 0 };
- cAddr.sin_family = AF_INET;
- cAddr.sin_addr.s_addr = inet_addr(argv[1]); //将字符串转整数
- cAddr.sin_port = htons(atoi(argv[2])); //将字符串转整数,再将小端转换成大端
-
- //3.连接服务器
- int r = connect(clientSocket,(struct sockaddr*)&cAddr,sizeof cAddr);
- if(-1 == r) printf("连接服务器失败:%m\n"),close(clientSocket),exit(-2);
- printf("连接服务器成功!\n");
-
- //4. 通信
- char buff[256] = {0};
- while(1){
- printf("你想要发送:");
- scanf("%s",buff);
- send(clientSocket,buff,strlen(buff),0);
- }
-
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- select与poll没有太大的区别, 均是轮循fd监视实现, 因此效率受描述符号数量影响(监视的fd越多,速率越慢) --- 就相当于一直盯着手机看是否有人打电话
- epoll是优化后的poll,通过注册事件实现, 不需要再轮循监视,因此效率不受监视的描述符号数量影响 --- 就相当于设置了来电提示看是否有人打电话
创建epoll epoll_create
注册描述符号事件 epoll_ctl
等待,挨个处理事件 epoll_wait 检查 使用&EPOLLIN(与poll相同)
- //需要的头文件
- #include <sys/epoll.h>
-
- int epoll_create(int size); //数量
-
- int epoll_ctl(int epfd, //epoll_create的返回值,处理过的文件描述符
- int op, //操作方式
- int fd, //需要监视的文件描述符
- struct epoll_event *event); //监视的描述符号链表表头地址(或者数组首地址)
-
- //参数二op
- EPOLL_CTL_ADD //添加
- EPOLL_CTL_MOD //修改
- EPOLL_CTL_DEL //删除
-
- int epoll_wait(int epfd, //epoll_create的返回值,处理过的文件描述符
- struct epoll_event *events, //监视的描述符号链表表头地址(或者数组首地址)
- int maxevents, //事件结构体数量(第二个参数的数量)
- int timeout); //延时
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- //使用的数据类型
- typedef union epoll_data{
- void *ptr;
- int fd;
- uint32_t u32;
- uint64_t u64;
- } epoll_data_t;
-
- struct epoll_event{
- uint32_t events;
- epoll_data_t data;
- };
服务器(server)端
- //服务器端
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <string.h>
- #include <signal.h>
- #include <sys/epoll.h>
-
- //最多允许的客户端数量
- #define NUM 100
-
- int serverSocket;
-
- void hand(int val){
- close(serverSocket);
- printf("bye bye!\n");
- exit(0);
- }
- int main(int argc,char* argv[]){
- if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
- printf("ip: %s port:%d\n",argv[1],atoi(argv[2]));
-
- signal(SIGINT,hand);
-
- //1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
- serverSocket = socket(AF_INET,SOCK_STREAM,0);
- if(-1 == serverSocket) printf("创建socket失败:%m\n"),exit(-1);
- printf("创建socket成功!\n");
-
- //2. 创建服务器协议地址簇
- struct sockaddr_in sAddr = { 0 };
- sAddr.sin_family = AF_INET; //协议类型 和socket函数第一个参数一致
- sAddr.sin_addr.s_addr = inet_addr(argv[1]); //将字符串转整数
- sAddr.sin_port = htons(atoi(argv[2])); //将字符串转整数,再将小端转换成大端
-
- //3. 绑定服务器协议地址簇
- int r = bind(serverSocket,(struct sockaddr*)&sAddr,sizeof sAddr);
- if(-1 == r) printf("绑定失败:%m\n"),close(serverSocket),exit(-2);
- printf("绑定成功!\n");
-
- //4. 监听
- r = listen(serverSocket,10); //数量
- if(-1 == r) printf("监听失败:%m\n"),close(serverSocket),exit(-3);
- printf("监听成功!\n");
-
- //监视serverSocket和ClientSocket
- struct sockaddr_in cAddr = {0};
- int len = sizeof(cAddr);
- int cfd; //临时保存一个客户端fd
- char buff[1024];
- int fdNum = 0;
-
- //创建epoll 参数可缺省(只需设置最大描述符号即可)
- int epfd = epoll_create(NUM);
- if(-1 == epfd){
- printf("创建epoll失败:%m\n");
- close(serverSocket);
- exit(-1);
- }
- printf("创建epoll成功!\n");
-
- //注册描述符号事件
- struct epoll_event ev; //注册时使用
- struct epoll_event events[NUM]; //等待处理事件时使用
-
- ev.events = EPOLLIN;
- ev.data.fd = serverSocket;
-
- r = epoll_ctl(epfd,EPOLL_CTL_ADD,serverSocket,&ev);
- if(-1 == r){
- printf("注册serverSocket事件失败:%m\n");
- close(epfd);
- close(serverSocket);
- exit(-2);
- }
- printf("注册serverSocket事件成功!\n");
- //等待,挨个处理事件
- //epoll_wait 成功返回有动静的描述符号数量
- int curCfd; //用于保存当前发数据的客户端epoll_wait的返回值
- int nfds; //用于保存epoll_wait的返回值
- while(1){
- nfds = epoll_wait(epfd,events,NUM,1000); //延时1000ms
- if(nfds < 0){
- printf("epoll_wait失败:%m\n");
- close(epfd);
- close(serverSocket);
- exit(-3);
- }else if(0 == nfds){
- //没有动静
- continue;
- }else{
- //有动静
- printf("有动静发生----\n");
- for(int i = 0;i < nfds; i++){
- if(serverSocket == events[i].data.fd){
- cfd = accept(serverSocket,(struct sockaddr*)&cAddr,&len);
- if(-1 == cfd){
- printf("服务器崩溃:%m\n");
- close(epfd);
- close(serverSocket);
- exit(-4);
- }
- printf("有客户端%d连接上服务器了:%s\n",cfd,
- inet_ntoa(cAddr.sin_addr));
-
- //注册客户端描述符号事件
- ev.events = EPOLLIN;
- ev.data.fd = cfd;
-
- r = epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
- if(-1 == r){
- printf("注册clientSocket失败:%m\n");
- close(epfd);
- close(serverSocket);
- exit(-5);
- }
- printf("注册clientSocket事件成功!\n");
- }else if(events[i].events && EPOLLIN){
- //有客户端发数据过来
- curCfd = events[i].data.fd;
- r = recv(curCfd,buff,1023,0);
- if(r > 0){
- buff[r] = 0;
- printf("%d >> %s\n",curCfd,buff);
- }
- }
- }
- }
- }
-
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
客户(client)端 (与上方poll代码相同)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。