网络异步IO编程:Poll

学习记录,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);
            }
        }

    }
}

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值