poll实现IO复用

poll 与select对比

流程

poll

select

1.建立一个文件描述符的表

建立结构体数组

struct pollfd fds[n];

fd_set线性表

2.将关心的文件描述符加到表中

结构体填充

fds[m].fd=fd;

fds[m].events=POLLIN

FD_SET(fd,&readfds)

3. 然后调用一个函数。 select / poll 

4. 当这些文件描述符中的一个或多个已准备好进行I/O操作的时候

该函数才返回(阻塞)

poll

select

5.判断

fds[m].revents == POLLIN

FD_ISSET

6.相关操作

  • poll实现IO复用

1、poll创建流程

poll实现IO复用_第1张图片

poll实现IO复用_第2张图片

poll实现IO复用_第3张图片

poll实现IO复用_第4张图片

2、poll特点

1. 优化文件描述符个数的限制;(根据poll函数第一个函数的参数来定,如果监听的事件为1个,则结构体数组元素个数为1,如果想监听100个,那么这个结构体数组的元素个数就为100,由程序员自己来决定)
2. poll被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低
3. poll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可

3、poll 创建一个表,表内包括一个fds结构体

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
   参数:
   struct pollfd *fds
 参数:
     关心的文件描述符数组struct pollfd fds[N];
     nfds:个数
     timeout: 超时检测
    毫秒级的:如果填10001
     如果-1,阻塞
 struct pollfd 
 {
     int   fd;         /* 检测的文件描述符 */
     short events;     /* 检测事件 */
     short revents;    /* 调用poll函数返回填充的事件,poll函数一旦返回,将对应事件自动填充结构体这个成员。只需要判断这个成员的值就可以确定是否产生事件 */
 };
    事件:  POLLIN :读事件
           POLLOUT : 写事件
           POLLERR:异常事件

(1)poll实现服务器

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  /* superset of previous */
#include 
#include 
#include 
#include 
int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        printf("plase input \n");
        return -1;
    }
    //1.创建套接字,用于链接
    int sockfd;
    int acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd); 
    //2.绑定 ip+port 填充结构体
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;//协议族ipv4
    saddr.sin_port = htons(atoi(argv[1]));//端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //ip地址,转化为16进制表示
    socklen_t len = sizeof(saddr); //结构体大小
    //bind绑定ip和端口
    if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind success\n");
    //3.启动监听,把主动套接子变为被动套接字
    if (listen(sockfd, 1) < 0)
    {
        perror("listen err");
        return -1;
    }
    printf("listen success\n");
    char buf[64] = {0};
    //引入poll
    //创建表
    int last = -1;
    struct pollfd fds[100] = {0};
    fds[++last].fd = 0;
    fds[last].events = POLLIN;
    fds[++last].fd = sockfd;
    fds[last].events = POLLIN;
    while (1)
    {
        int ret = poll(fds, last + 1, -1);
        if (ret < 0)
        {
            perror("poll err");
            return -1;
        }
        //判断
        for (int i = 0; i <= last; i++)
        {
            if (fds[i].revents == POLLIN)
            {
                if (fds[i].fd == 0)
                {
                    //5.执行操作
                    fgets(buf, sizeof(buf), stdin);
                    if (strncmp(buf, "quit", 4) == 0) //接收到quit退出
                    {
                        break;
                    }
                    if (buf[strlen(buf)] == '\0') //去掉fgets补的'\n'
                    {
                        buf[strlen(buf) - 1] = '\0';
                    }
                    for (int j = 2; j <= last; j++) //遍历链接的客户端发送信息
                    {
                        send(fds[j].fd, buf, sizeof(buf), 0); //发送
                    }
                }
                else if (fds[i].fd == sockfd)//链接客户端
                {
                    //阻塞等待客户端的链接请求
                    acceptfd = accept(sockfd, (struct sockaddr *)&saddr, &len);
                    //获取客户端的ip和端口,(struct sockaddr *)&saddr:用来存放返回的ip,和端口
                    if (acceptfd < 0)
                    {
                        perror("accept err");
                        return -1;
                    }
                    printf("client ip:%s ,port:%d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
                    printf("connect success\n");
                    fds[++last].fd = acceptfd;
                    fds[last].events = POLLIN;
                }
                else
                {//接收信号并转发给除当前客户端的其他客户端
                    int ret = recv(fds[i].fd, buf, sizeof(buf), 0); //接收的信号
                    if (ret < 0)
                    {
                        perror("recv err.");
                        return -1;
                    }
                    else if (ret == 0)
                    {
                        printf("%d client exit\n", i);
                        close(fds[i].fd);       //关闭描述符
                        fds[i--] = fds[last--]; //将最后一个有 效数据与当前描述符位置置换
                    }
                    else
                    {
                        printf("client %d:%s\n", fds[i].fd, buf);
                        for (int j = 2; j <= last; j++)
                        {
                            if (== j)
                            {
                                continue;
                            }
                            send(fds[j].fd, buf, sizeof(buf), 0); //发送
                        }
                    }
                }
            }
        }
    }

    close(sockfd);
    close(acceptfd);
    return 0;
}

(2)poll实现客户端

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  /* superset of previous */
#include 
#include 
#include 
#include 
int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        printf("plase input \n");
        return -1;
    }
    //1.创建套接字,用于链接
    int sockfd;
    int acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd);
    //2.绑定 ip+port 填充结构体
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;                 //协议族ipv4
    saddr.sin_port = htons(atoi(argv[2]));      //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
    saddr.sin_addr.s_addr = inet_addr(argv[1]); //ip地址,转化为16进制表示
    socklen_t len = sizeof(saddr);              //结构体大小
    //3用于连接服务器;
    if (connect(sockfd, (struct sockaddr *)&saddr, len) < 0)
    {
        perror("connect err");
        return -1;
    }
    printf("connect success\n");
   char buf[64] = {0};
    //引入poll
    //创建表
    int last = -1;
    struct pollfd fds[100] = {0};
    fds[++last].fd = 0;
    fds[last].events = POLLIN;
    fds[++last].fd = sockfd;
    fds[last].events = POLLIN;
    while (1)
    {
        int ret = poll(fds, last + 1, -1);
        if (ret < 0)
        {
            perror("poll err");
            return -1;
        }
        //判断
        for (int i = 0; i <= last; i++)
        {
            if (fds[i].revents == POLLIN)
            {
                if (fds[i].fd == 0)
                {
                    //5.执行操作
                    fgets(buf, sizeof(buf), stdin);
                    if (buf[strlen(buf)] == '\0') //去掉fgets补的'\n'
                    {
                        buf[strlen(buf) - 1] = '\0';
                    }
                    send(sockfd, buf, sizeof(buf), 0); //发送
                }
                else if(fds[i].fd == sockfd)
                {
                    int ret = recv(fds[i].fd, buf, sizeof(buf), 0); //接收的信号
                    if (ret < 0)
                    {
                        perror("recv err.");
                        return -1;
                    }
                    else if (ret == 0)
                    {
                        continue;
                    }
                    else
                    {
                        printf("client %d:%s\n", fds[i].fd, buf);
                    }
                }
            }
        }
    }
    close(sockfd);
    return 0;
}

你可能感兴趣的:(网络编程,IO,1024程序员节,linux,c语言,网络)