epoll的实现

    xiaoxiao2022-07-14  175

    关于epoll的介绍已经在这篇博客中介绍了 下面将展示这个具体实现

    #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/select.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <poll.h> int main(int argc,char *argv[]) { int udpfd = 0; int ret = 0; struct pollfd fds[2];//监测文件描述结构体数组:2个 状态分离 所以需要两个数组 //用于存放server和client端的地址 struct sockaddr_in saddr; struct sockaddr_in caddr; //初始化 bzero(&saddr,sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = htons(8000); saddr.sin_addr.s_addr = htonl(INADDR_ANY); bzero(&caddr,sizeof(caddr)); caddr.sin_family = AF_INET; caddr.sin_port = htons(8000); //创建套接字 if( (udpfd = socket(AF_INET,SOCK_DGRAM, 0)) < 0) { perror("socket error"); exit(-1); } //套接字端口绑字 if(bind(udpfd, (struct sockaddr*)&saddr, sizeof(saddr)) != 0) { perror("bind error"); close(udpfd);//绑定失败的话就直接关闭套接字 exit(-1); } printf("input: \"sayto 192.168.220.X\" to sendmsg to somebody\033[32m\n"); fds[0].fd = 0; //标准输入描述符 数组一 fds[1].fd = udpfd; //udp描述符 数组二 /* //这里会展示pollfd结构体 struct pollfd{ int fd; //文件描述符 short events; //等待的事件 short revents; //实际发生的事件 }; */ fds[0].events = POLLIN; // 普通或优先级带数据可读 fds[1].events = POLLIN; // 普通或优先级带数据可读 while(1) { // 监视并等待多个文件(标准输入,udp套接字)描述符的属性变化(是否可读) // 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时 /* 成功时,poll() 返回结构体中 revents 域不为 0 的文件描述符个数;如果在超时前没有任何事件发生,poll()返回 0,失败时,poll() 返回 -1, */ write(1,"UdpQQ:",6);//向标准输出输出UdpQQ ret = poll(fds, 2, -1); //监听两个数组 由于timeout参数设置为-1,所以说会一直等待到有时间发生 if(ret == -1){ // 出错 perror("poll()"); } else if(ret > 0){ // 准备就绪的文件描述符 char buf[100] = {0}; //缓冲区 if( ( fds[0].revents & POLLIN ) == POLLIN ){ // 标准输入 通过位与操作 类似于select函数的ISSET fgets(buf, sizeof(buf), stdin);//从键盘输入 buf[strlen(buf) - 1] = '\0'; if(strncmp(buf, "sayto", 5) == 0)//出现sayto 后 就将其后面的ip地址赋值给addr { char ipbuf[16] = ""; inet_pton(AF_INET, buf+6, &caddr.sin_addr);//给addr套接字地址再赋值. printf("\rsay to %s\n",inet_ntop(AF_INET,&caddr.sin_addr,ipbuf,sizeof(ipbuf))); continue; } else if(strcmp(buf, "exit")==0)//判断输入 exit 出现时候直接退出 { close(udpfd); exit(0); } sendto(udpfd, buf, strlen(buf),0,(struct sockaddr*)&caddr, sizeof(caddr)); } else if( ( fds[1].revents & POLLIN ) == POLLIN ){ //udp套接字 struct sockaddr_in addr; char ipbuf[INET_ADDRSTRLEN] = ""; socklen_t addrlen = sizeof(addr); bzero(&addr,sizeof(addr)); //显示输出 recvfrom(udpfd, buf, 100, 0, (struct sockaddr*)&addr, &addrlen); printf("\r\033[31m[%s]:\033[32m%s\n",inet_ntop(AF_INET,&addr.sin_addr,ipbuf,sizeof(ipbuf)),buf); } } else if(0 == ret){ // 超时 printf("time out\n"); } } return 0; }

    关于怎样测试 参照 添加链接描述 下面加入关于tcp并发服务器(poll实现)

    #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/select.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <poll.h> #include <errno.h> #define OPEN_MAX 100 int main(int argc, char *argv[]) { //1.创建tcp监听套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); //2.绑定sockfd struct sockaddr_in my_addr; bzero(&my_addr, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(8000); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)); //3.监听listen listen(sockfd, 10); //4.poll相应参数准备 struct pollfd client[OPEN_MAX]; int i = 0, maxi = 0; for(;i<OPEN_MAX; i++) client[i].fd = -1;//初始化poll结构中的文件描述符fd 初始化的时候将其都设置为-1 client[0].fd = sockfd;//需要监测的描述符 client[0].events = POLLIN;//普通或优先级带数据可读 //5.对已连接的客户端的数据处理 while(1) { write(1,"UdpQQ:",6); int ret = poll(client, maxi+1, -1);//对加入poll结构体数组所有元素进行监测 timeout 设置位-1 就是阻塞等待 //5.1监测sockfd(监听套接字)是否存在连接 if((client[0].revents & POLLIN) == POLLIN )//进行位操作 判断是否有事件 { struct sockaddr_in cli_addr; int clilen = sizeof(cli_addr); int connfd = 0; //5.1.1 从tcp完成连接中提取客户端 connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen); char buf[100] = {0}; //缓冲区 fgets(buf, sizeof(buf), stdin);//从键盘输入 buf[strlen(buf) - 1] = '\0'; if(strncmp(buf, "sayto", 5) == 0)//出现sayto 后 就将其后面的ip地址赋值给addr { char ipbuf[16] = ""; inet_pton(AF_INET, buf+6, &cli_addr.sin_addr);//给addr套接字地址再赋值. printf("\rsay to %s\n",inet_ntop(AF_INET,&cli_addr.sin_addr,ipbuf,sizeof(ipbuf))); continue; } else if(strcmp(buf, "exit")==0)//判断输入 exit 出现时候直接退出 { close(connfd); exit(0); } sendto(connfd, buf, strlen(buf),0,(struct sockaddr*)&caddr, sizeof(caddr)); //5.1.2 将提取到的connfd放入poll结构体数组中,以便于poll函数监测 for(i=1; i<OPEN_MAX; i++)//因为client[0]已经放了sockfd 所以从位置1开始 { if(client[i].fd < 0)//如果这个位置是初始化的-1 那么说明这个位置空缺 { client[i].fd = connfd; client[i].events = POLLIN; break; } } //5.1.3 maxi更新 if(i > maxi)//如果i超出了最大值 就将i赋值给最大值 maxi = i; //5.1.4 如果没有就绪的描述符,就继续poll监测,否则继续向下看 if(--ret <= 0)//如果poll没有监听到事件 那么返回继续监听 continue; } //5.2继续响应就绪的描述符 for(i=1; i<=maxi; i++) { if(client[i].fd < 0) continue; if(client[i].revents & (POLLIN | POLLERR))//通过位操作 判断是否有请求 { int len = 0; char buf[128] = ""; //5.2.1接受客户端数据 if((len = recv(client[i].fd, buf, sizeof(buf), 0)) < 0) { if(errno == ECONNRESET)//tcp连接超时、RST 判断是否报错 { close(client[i].fd);//关闭这个文件描述符 client[i].fd = -1;//重新将这个位置置为-1 } else perror("read error:");//输出读取错误 } else if(len == 0)//客户端关闭连接 { close(client[i].fd); client[i].fd = -1; } else//正常接收到服务器的数据 printf("\r\033[31m[%s]:\033[32m%s\n",inet_ntop(AF_INET,&cli_addr.sin_addr,ipbuf,sizeof(ipbuf)),buf); //5.2.2所有的就绪描述符处理完了,就退出当前的for循环,继续poll监测 if(--ret <= 0) break; } } } return 0; }
    最新回复(0)