linux之pool

本文介绍了Linux中的poll函数,它是一种I/O多路复用技术,用于同时监视多个文件描述符。与select函数不同,poll使用结构体数组存储文件描述符,没有最大文件描述符数量限制。文中提供了一个示例,展示如何使用poll函数监听socket,处理新客户端连接和数据读写的事件。
摘要由CSDN通过智能技术生成

poll 函数是 Linux 系统中的一种 I/O 多路复用机制,与 select 函数类似。它可以同时监视多个文件描述符,当其中任意一个文件描述符发生读写事件时,就会通知程序进行相应的处理。与 select 不同的是,poll 函数使用一个结构体数组来存储需要监视的文件描述符,而不是使用 fd_set 集合。此外,poll 函数没有最大文件描述符数量的限制,也不会改变传入的 nfds 参数的值,因此使用起来更加方便。

 int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
	int   fd;         /* file descriptor */
	short events;     /* requested events */
	short revents;    /* returned events */
};

在调用 poll 函数时,调用者需要指定监视的文件描述符数量,即 nfds 参数。该参数的值应该是要监视的文件描述符数量加一,因为 poll 函数还需要监视一个额外的文件描述符,即用于中断 poll 函数阻塞的文件描述符。这个文件描述符通常是一个管道或者信号量。

因此,nfds 参数的值应该是 struct pollfd 数组中元素的数量加一。例如,如果要监视三个文件描述符,那么 nfds 的值应该是 4。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/fcntl.h>

// ulimit -n
#define MAXNFDS  1024

// 初始化服务端的监听端口。
int initserver(int port);

int main(int argc,char *argv[])
{
    if (argc != 2)
    {
        printf("usage: ./tcppoll port\n"); return -1;
    }

    // 初始化服务端用于监听的socket
    int listensock = initserver(atoi(argv[1]));
    printf("listensock=%d\n",listensock);

    if (listensock < 0)
    {
        printf("initserver() failed.\n"); return -1;
    }

    int maxfd;   // fds数组中需要监视的socket的大小。
    struct pollfd fds[MAXNFDS];  // fds存放需要监视的socket

    for (int ii=0; ii<MAXNFDS; ii++) fds[ii].fd=-1; // 初始化数组,把全部的fd设置为-1

    // 把listensock添加到数组中。
    fds[listensock].fd = listensock;
    fds[listensock].events = POLLIN;  // 有数据可读事件,包括新客户端的连接, 客户端socket有数据可读和客户端socket断开三种情况
    maxfd = listensock;

    while (1)
    {
        int infds = poll(fds, maxfd+1, 5000);

        // 返回失败
        if (infds < 0)
        {
            printf("poll() failed.\n"); perror("poll():"); break;
        }

        // 超时
        if (infds == 0)
        {
            printf("poll() timeout.\n"); continue;
        }

        // 检查有事情发生的socket,包括监听和客户端连接的socket
        // 这里是客户端的socket事件,每次都要遍历整个集合,因为可能有多个socket有事件
        for (int eventfd=0; eventfd <= maxfd; eventfd++)
        {
            if (fds[eventfd].fd < 0) continue;

            if ((fds[eventfd].revents & POLLIN) == 0) continue;

            fds[eventfd].revents = 0;  // 先把revents清空

            if (eventfd==listensock)
            {
                // 如果发生事件的是listensock,表示有新的客户端连上来
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int clientsock = accept(listensock, (struct sockaddr*)&client, &len);
                if (clientsock < 0)
                {
                  printf("accept() failed.\n"); continue;
                }

                printf ("client(socket=%d) connected ok.\n",clientsock);

                if (clientsock>MAXNFDS)
                {    
                  printf("clientsock(%d)>MAXNFDS(%d)\n",clientsock,MAXNFDS); close(clientsock); continue;
                }

                fds[clientsock].fd = clientsock;
                fds[clientsock].events=POLLIN; //监听 新客户端连接, 数据可读 以及 断开连接
                fds[clientsock].revents = 0; 
                if (maxfd < clientsock) maxfd = clientsock;

                printf("maxfd=%d\n",maxfd);
                continue;
            }
            else 
            {
                // 客户端有数据过来或客户端的socket连接被断开
                char buffer[1024];
                memset(buffer,0,sizeof(buffer));

                // 读取客户端的数据
                ssize_t isize=read(eventfd,buffer,sizeof(buffer));

                // 发生了错误或socket被对方关闭
                if (isize <=0)
                {
                    printf("client(eventfd=%d) disconnected.\n",eventfd);

                    close(eventfd);  // 关闭客户端的socket。

                    fds[eventfd].fd = -1;

                    // 重新计算maxfd的值,注意,只有当eventfd==maxfd时才需要计算。
                    if (eventfd == maxfd)
                    {
                        for (int ii=maxfd; ii>0; ii--)
                        {
                            if ( fds[ii].fd != -1)
                            {
                              maxfd = ii; break;
                            }
                        }

                      printf("maxfd=%d\n",maxfd);
                    }

                    continue;
                }

                printf("recv(eventfd=%d,size=%d):%s\n",eventfd,isize,buffer);

                // 把收到的报文发回给客户端。
                write(eventfd,buffer,strlen(buffer));
            }
        }
    }

    return 0;
}

// 初始化服务端的监听端口。
int initserver(int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if (sock < 0)
    {
        printf("socket() failed.\n"); return -1;
    }

    // Linux如下
    int opt = 1; unsigned int len = sizeof(opt);
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,len);
    setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,&opt,len);

    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);

    if (bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0 )
    {
        printf("bind() failed.\n"); close(sock); return -1;
    }

    if (listen(sock,5) != 0 )
    {
        printf("listen() failed.\n"); close(sock); return -1;
    }

    return sock;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值