专业网站开发开发,百度抓取网站频率,查看关键词被搜索排名的软件,济南集团网站建设方案文章目录 poll模型pollfd结构体nfds_t的定义 一个简单的poll服务器总结 poll模型
poll模型和select模型类似#xff0c;都是在指定时间内轮询一定数量的文件描述符#xff0c;以测试其中是否有就绪者#xff0c;需要使用头文件poll.h#xff1a;
#include poll.h… 文章目录 poll模型pollfd结构体nfds_t的定义 一个简单的poll服务器总结 poll模型
poll模型和select模型类似都是在指定时间内轮询一定数量的文件描述符以测试其中是否有就绪者需要使用头文件poll.h
#include poll.h
int poll(struct pollfd* fds, nfds_t nfds, int timeout);在select中使用的是fd_set结构体而在此处的是pollfd和nfds_ttimeout的作用和select的一样用于指定poll的超时值当timeout的值为-1时poll调用将会永远阻塞直到某个事件发生当timeout为0时poll调用将立即返回。 poll的返回值也和select一致表示就绪文件描述符的总数。
pollfd结构体
struct pollfd{int fd; // 文件描述符short int events; // 注册的事件short int revents; // 实际发生的事件由内核填充
};fd指定文件描述符events成员告诉poll监听fd上的哪些事件它是一系列事件的按位或revents成员由内核修改以通知应用程序fd上实际发生了哪些事件
poll支持以下事件类型
事件描述是否可作为输入是否可作为输出POLLIN数据包括普通数据和优先数据可读是是POLLRNDORM普通数据可读是是POLLRDBAND优先级带数据可读是是POLLPRI高优先级数据可读比如TCP带外数据是是POLLOUT数据包括普通数据和优先数据可写是是POLLWRNORM普通数据可写是是POLLWRBAND优先级带数据可写是是POLLRDHUPTCP连接被对方关闭或者对方关闭了写操作。它由GNU引入是是POLLERR错误否是POLLHUP挂起。比如管道的写端被关闭后读端描述符上将收到POLLHUP事件否是POLLNVAL文件描述符没有打开否是
在表中提到了很多事件但是Linux中没有完全支持它们。 通常应用程序需要根据recv调用的返回值来区分socket上接受到的是有效数据还是对方关闭连接的请求并做相应的处理。 不过自Linux内核2.6.17开始GNU为poll系统调用增加了一个POLLRDHUP事件它在socket上接收到对方关闭连接的请求后触发。这为我们区分recv接受到的数据是有效数据还是对方关闭连接的请求提供了一种更简单的方式。 但使用POLLRDHUP事件时我们需要在代码最开始处定义_GNU_SOURCE。 nfds_t的定义
typedef unsigned long int nfds_t;该参数用来指定被监听事件集合fds的大小。
一个简单的poll服务器
这个服务器是个简单的echo服务器
#include iostream
#include vector
#include sys/socket.h
#include poll.h
#include algorithm
#include arpa/inet.h
#include assert.h
#include vector
#include unistd.husing namespace std;int main(int argc, char* argv[]){if(argc ! 3){cerr 格式为 ip port endl;return 1;}int serverSocket, clientSocket;/*这个头文件在in.h中实际上我们调用的是inet/in.h而inet/in.h被包含在头文件arpa/inet。h中了*/struct sockaddr_in serverAddr{}, clientAddr{};socklen_t clientAddrLen;// 创建监听套接字serverSocket socket(AF_INET, SOCK_STREAM, 0);assert(serverSocket ! -1);serverAddr.sin_family AF_INET;/*htons的英文意思是host to network shrot用于将16位无符号短整型port的主机字节序转换为网络字节序*/serverAddr.sin_port htons(stoi(argv[2])); /*在调用 inet_pton 函数时需要将 (address.sin_addr) 作为参数传递而不是 (address.sin_addr.s_addr)struct sockaddr_in 结构体中的 sin_addr 字段是一个 struct in_addr 类型的结构体它包含了 IP 地址的二进制表示形式。in_addr 结构体中的 s_addr 字段实际上就是一个无符号整数类型uint32_t用来存储 IP 地址的二进制形式。*/inet_pton(AF_INET, argv[1], (serverAddr.sin_addr));// 绑定套接字到地址和端口int ret bind(serverSocket, (struct sockaddr*)serverAddr, sizeof(serverAddr));assert(ret ! -1);ret listen(serverSocket, 5);assert(ret ! -1);// 监听的文件描述符队列/*创建一个pollfd类型的空向量并且将之前创建的serverSocket加入到其中进行监听POLLIN是我们所监视的事件类型这点笔记中有写为什么需要监视serverSocket答这是为了实现服务器的异步IO通过监视serverSocket及时检测到下面两种情况1. 当有新的客户端连接请求到达时我们希望能够立即进行处理。通过监视serverSocket上的POLLIN事件可以检测到是否有客户端尝试建立连接2. 当serverSocket上出现其他错误情况如连接断开或发生错误时我们也希望能及时进行处理通过监视serverSocket上的激长时间如POLLHUP或POLLERR可以检测到这些错误情况*/vectorpollfd fds;fds.push_back({serverSocket, POLLIN});while(true){int numRead poll(fds.data(), fds.size(), -1);if(numRead 0){cerr poll error;return 1;}// for(auto fd : fds){/*确保有新的连接但是不是很理解为什么是一直监听serverSocket*/if(fd.fd serverSocket fd.revents POLLIN){// 有新连接clientAddrLen sizeof(clientAddr);clientSocket accept(serverSocket, (struct sockaddr*)clientAddr, clientAddrLen);if(clientSocket 0){cerr Failed to accepted connection endl;return 1;}else{cout New connection from: inet_ntoa(clientAddr.sin_addr) endl;fds.push_back({clientSocket, POLLIN});}}else if(fd.revents POLLIN){// 需要读取信息// 此时的不是监听socket而是客户端的连接socketchar buffer[1024];ssize_t bytesRead recv(fd.fd, buffer, sizeof(buffer), 0);if(bytesRead 0){if(bytesRead 0){cerr Error reading from client. endl;}else{cout Connection clost by client. endl;}close(fd.fd);// 这个remove_if()不是很熟fds.erase(remove_if(fds.begin(), fds.end(),[](const pollfd pfd){ return pfd.fd fd.fd; }),fds.end());}else{// 处理数据cout Received data: string(buffer, bytesRead) endl;// 将收到的数据回发给客户端send(fd.fd, buffer, bytesRead, 0);}}}}// 关闭监听套接字close(serverSocket);return 0;
}总结
学到这里我就发现想要真正去理解Linux网络编程还是要懂Linux内核比如套接字和文件描述符的联系要去了解下select和poll的工作原理这样才能理解程序为何这么编写。