学习记录,poll方法网络IO通信 服务器端
先复习select方法的缺点
select方法的缺点:
1、每次调用都需要重新设置监视set。select中布置监视任务的位置和返回监视的结果位置存放在同一空间,一旦监视行为发生,函数返回,其它集合会被清空。
2、select函数为系统调用函数,需要频繁的从用户态切换内核态对监视数组进行拷贝,效率不高。
3、底层采用轮询机制,大量连接下效率很低。
4、select 支持监听的fd有限。
poll方法:
优点:
1.每次调用不用重新设置监视set。pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式. 接口使用比select更方便
2.poll并没有最大数量限制
缺点:
1.和select函数一样,每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中
2.和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符
3.此外,select可以跨系统使用,poll不能跨系统使用
一、监听端口
//创建socket,返回文件描述符
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
//定义地址
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(socketfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr))==-1){
perror("bind error");
return -1;
}
listen(socketfd, 10);
二、相关结构体类型和函数解析
1.pollfd解析
我们先看pollfd在源码中的定义
struct pollfd
{
int fd; /* File descriptor to poll. */
short int events; /* Types of events poller cares about. */
short int revents; /* Types of events that actually occurred. */
};
每一个 pollfd 代表一个被监视的文件描述符,可以传递一个pollfd数组,通过poll监视多个文件描述符
int fd: 文件描述符
short int events: 表示要告诉操作系统需要监测fd的事件,用户设置。
short int revents: 操作结果事件,即实际发生的事情,内核在调用返回时设置这个域
2.poll函数解析
poll (struct pollfd *__fds, nfds_t __nfds, int __timeout)
struct pollfd *__fds: pollfd数组,监视的集合
nfds_t __nfds: 文件描述符的范围。注意到传入的参数应该是最大文件描述符+1。因为遍历逻辑为 i < __nfds
int __timeout: poll函数的超时毫秒数
函数特点:
每当函数调用后,不会清空这个__fds指向的数组,适用于大量socket 描述符的情况。而select()函数调用后会清空它所检测的socket描述符集合,因此select()只是用检测一个socket 描述符的情况。
如果待监测的socket 描述符为负值,则这个描述符的检测就会被忽略,poll()函数返回时直接把revents 设置为0
该poll()函数不会受到socket 描述符上的O_NDELAY 标记和O_NONBLOCK 标记的影响。
三、定义pollfd数组并初始化
struct pollfd fds[1024] = {0};
//从socket_fd开始方便将索引直接作为文件描述符
fds[socket_fd].fd = socket_fd;
fds[socket_fd].events = POLLIN;
//标志最大文件描述符
int maxfd = socket_fd;
四、while循环poll并处理就绪的监听socket和客户端通信socket
以下为while(1){}循环体中的内容:
1.poll函数监视数组是否有就绪的socket,如果有会将pollfd的revents属性修改
poll(fds, maxfd + 1, -1);
2.处理监听socket,判断是否有新的连接
如果监听进程就绪,就新建一个socket与客户端连接,并将新socket添加到已连接的集合中fds,同时更新最大的文件描述符
if(fds[socket_fd].revents & POLLIN){
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int new_socket = accept(socket_fd, (struct sockaddr*)&client_addr, &len);
printf("accept new socket connect, index: %d\n", new_socket);
//监视该socket
fds[new_socket].fd = new_socket;
fds[new_socket].events = POLLIN;
maxfd = new_socket;
}
3.处理收到信息的socket
遍历所有文件描述符,如果是就绪的,就进行处理
for(int i=socket_fd+1;i < maxfd+1; i++){
if(fds[i].revents & POLLIN){
char message[64] = {0};
if(0 == recv(i, message, 64, 0)){
printf("client index %d disconnect.\n", i);
fds[i].fd = -1;
fds[i].events = 0;
close(i);
continue;
}
printf("received message from client index %d, context: %s\n", i, message);
strcpy(message, "Hello");
send(i, message, 64, 0);
}
五、完整代码
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <sys/select.h>
int main() {
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(8888);
if (-1 == bind(socket_fd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr))) {
perror("bind");
return -1;
}
listen(socket_fd, 10);
struct pollfd fds[1024] = {0};
fds[socket_fd].fd = socket_fd;
fds[socket_fd].events = POLLIN;
int maxfd = socket_fd;
while(1){
poll(fds, maxfd + 1, -1);
if(fds[socket_fd].revents & POLLIN){
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int new_socket = accept(socket_fd, (struct sockaddr*)&client_addr, &len);
printf("accept new socket connect, index: %d\n", new_socket);
//监视该socket
fds[new_socket].fd = new_socket;
fds[new_socket].events = POLLIN;
maxfd = new_socket;
}
for(int i=socket_fd+1;i < maxfd+1; i++){
if(fds[i].revents & POLLIN){
char message[64] = {0};
if(0 == recv(i, message, 64, 0)){
printf("client index %d disconnect.\n", i);
fds[i].fd = -1;
fds[i].events = 0;
close(i);
continue;
}
printf("received message from client index %d, context: %s\n", i, message);
strcpy(message, "Hello");
send(i, message, 64, 0);
}
}
}
}