select poll epoll 水平触发 边缘触发
# select
/* select阻塞函数,多次还是需要while,在新建客户端方面有没有都一样 因为accept多次也需要while 但在判断客户端是否有数据到来方面 使用了select就不需要创建多个线程或进程判断了,select可以批量判断 */ #include <stdio.h> #include <sys/select.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <unistd.h> int main() { int lfd = socket(AF_INET, SOCK_STREAM, 0); if(lfd == -1) { perror("socket"); exit(-1); } char *buf = "192.168.248.128"; int ip; inet_pton(AF_INET, buf, &ip); struct sockaddr_in saddr; saddr.sin_addr.s_addr = ip; saddr.sin_port = htons(9999); saddr.sin_family = AF_INET; // 绑定 int ret = bind(lfd, (struct sockaddr *) &saddr, sizeof(saddr)); if(ret == -1) { perror("bind"); exit(-1); } ret = listen(lfd, 5); if(ret == -1) { perror("listen"); exit(-1); } fd_set readfds, tmp; // 因为select返回时,内核会修改set,为了能够实现多次监听,需要一个副本记录原始set FD_ZERO(&readfds); FD_SET(lfd, &readfds); int maxfd = lfd; // 需要监听的最大序号的描述符 while(1) { tmp = readfds; ret = select(maxfd + 1, &tmp, NULL, NULL, NULL); if(ret == -1) { perror("select"); exit(-1); } else if(ret == 0) { continue; } else { if(FD_ISSET(lfd, &tmp)) // 是否有新的客户端到达 { struct sockadd_in client_addr; int client_addr_len = sizeof(client_addr); int cfd = accept(lfd, (struct sockaddr *) &client_addr, &client_addr_len); FD_SET(cfd, &readfds); maxfd = cfd > maxfd ? cfd : maxfd; } for(int i = lfd + 1; i < maxfd + 1; i++) // 已经到达的客户端是否写了数据到我的读缓冲区 { if(FD_ISSET(i, &tmp)) // 如果我的读缓冲区有数据 { char readBuf[1024]; int read_len = read(i, readBuf, sizeof(readBuf)); // 读取数据 if(read_len == 0) { printf("客户端连接断开······"); close(i); // 既然关闭了连接,则close描述符 FD_CLR(i, &readfds); // 在监听集合中删除相应描述符 } else if(read_len == -1) { perror("read"); exit(-1); } else { printf("recv info : %s\n", readBuf); write(i, readBuf, strlen(readBuf) + 1); } } } } } close(lfd); return 0; }
# poll
#include <stdio.h> #include <sys/select.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <poll.h> int main() { int lfd = socket(AF_INET, SOCK_STREAM, 0); if(lfd == -1) { perror("socket"); exit(-1); } char *buf = "192.168.248.128"; int ip; inet_pton(AF_INET, buf, &ip); struct sockaddr_in saddr; saddr.sin_addr.s_addr = ip; saddr.sin_port = htons(9999); saddr.sin_family = AF_INET; // 绑定 int ret = bind(lfd, (struct sockaddr *) &saddr, sizeof(saddr)); if(ret == -1) { perror("bind"); exit(-1); } ret = listen(lfd, 5); if(ret == -1) { perror("listen"); exit(-1); } struct pollfd fds[1024]; // poll 是通过结构体数组 for(int i = 0; i < 1024; i++) { fds[i].fd = -1; fds[i].events = POLLIN; } int cnt = 0; while(1) { ret = poll(fd, cnt + 1, -1); if(ret == -1) { perror("poll"); exit(-1); } else if(ret == 0) { continue; } else { if(fds[0].revents & POLLIN) // 是否有新的客户端到达,用&是因为防止revents是多个状态的| { struct sockadd_in client_addr; int client_addr_len = sizeof(client_addr); int cfd = accept(lfd, (struct sockaddr *) &client_addr, &client_addr_len); int i; for(i = 1; i < 1024 && fds[i].fd != -1; i++); fds[i].fd = cfd; fds[i].events = POLLIN; cnt = i > cnt ? i : cnt; } for(int i = 0; i < cnt + 1; i++) // 已经到达的客户端是否写了数据到我的读缓冲区 { if(fds[i].revents & POLLIN) // 如果我的读缓冲区有数据 { char readBuf[1024]; int read_len = read(i, readBuf, sizeof(readBuf)); // 读取数据 if(read_len == 0) { printf("客户端连接断开······"); close(i); // 既然关闭了连接,则close描述符 FD_CLR(i, &readfds); // 在监听集合中删除相应描述符 } else if(read_len == -1) { perror("read"); exit(-1); } else { printf("recv info : %s\n", readBuf); write(i, readBuf, strlen(readBuf) + 1); } } } } } close(lfd); return 0; }
# epoll
#include <stdio.h> #include <sys/select.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/epoll.h> int main() { int lfd = socket(AF_INET, SOCK_STREAM, 0); if(lfd == -1) { perror("socket"); exit(-1); } char *buf = "192.168.248.128"; int ip; inet_pton(AF_INET, buf, &ip); struct sockaddr_in saddr; saddr.sin_addr.s_addr = ip; saddr.sin_port = htons(9999); saddr.sin_family = AF_INET; // 绑定 int ret = bind(lfd, (struct sockaddr *) &saddr, sizeof(saddr)); if(ret == -1) { perror("bind"); exit(-1); } ret = listen(lfd, 5); if(ret == -1) { perror("listen"); exit(-1); } int efd = epoll_create(2000); struct epoll_event epevt; epevt.events = EPOLLIN; epevt.data.fd = lfd; epoll_ctl(efd, EPOLL_CTL_ADD, lfd, &epevt); struct epoll_event epevts[1024]; // 传出参数,用于返回到达事件的集合 while(1) { ret = epoll_wait(efd, epevts, 1024, -1); if(ret == -1) { perror("epoll"); exit(-1); } else if(ret == 0) { continue; } else { if(fds[0].revents & POLLIN) // 是否有新的客户端到达,用&是因为防止revents是多个状态的| { struct sockadd_in client_addr; int client_addr_len = sizeof(client_addr); int cfd = accept(lfd, (struct sockaddr *) &client_addr, &client_addr_len); int i; for(i = 1; i < 1024 && fds[i].fd != -1; i++); fds[i].fd = cfd; fds[i].events = POLLIN; cnt = i > cnt ? i : cnt; } for(int i = 0; i < ret; i++) // 已经到达的客户端是否写了数据到我的读缓冲区 { if(epevts[i].data.fd == lfd) { struct sockadd_in client_addr; int client_addr_len = sizeof(client_addr); int cfd = accept(lfd, (struct sockaddr *) &client_addr, &client_addr_len); struct epoll_event epevt; epevt.data.fd = cfd; epevt.events = EPOLLIN; epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &epevt); } else // 如果我的读缓冲区有数据 { char readBuf[1024]; int read_len = read(epevts[i].data.fd, readBuf, sizeof(readBuf)); // 读取数据 if(read_len == 0) { printf("客户端连接断开······"); close(epevts[i].data.fd); // 既然关闭了连接,则close描述符 epoll_ctl(efd, EPOLL_CTL_DEL, epevts[i].data.fd, NULL); } else if(read_len == -1) { perror("read"); exit(-1); } else { printf("recv info : %s\n", readBuf); write(epevts[i].data.fd, readBuf, strlen(readBuf) + 1); } } } } } close(efd); // 关闭epoll的描述符 close(lfd); return 0; }
# 水平触发、边缘触发
默认为水平触发
自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。