I/O复用函数的使用——poll

1.poll 的接口介绍

poll 系统调用和 select 类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。poll 的原型如下:

#include <poll.h>

int poll( struct pollfd *fds, nfds_t nfds, int timeout);
/*
poll 系统调用成功返回就绪文件描述符的总数,超时返回 0,失败返回-1
nfds 参数指定被监听事件集合 fds 的大小。
timeout 参数指定 poll 的超时值,单位是毫秒,timeout 为-1 时,poll 调用将永久阻塞,直到某个事件发生,timeout 为 0 时,poll 调用将立即返回。
*/



/*
fds 参数是一个 struct pollfd 结构类型的数组,它指定所有用户感兴趣的文件描述符上发生的可读、可写和异常等事件。pollfd 结构体定义如下:
*/
12. struct pollfd
13. {
14. int fd; // 文件描述符
15. short events; // 注册的关注事件类型
16. short revents; // 实际发生的事件类型,由内核填充
17. };
//其中,fd 成员指定文件描述符,events 成员告诉 poll 监听 fd 上的哪些事件类型。它是一系列事件的按位或,revents 成员则有内核修改,通知应用程序 fd 上实际发生了哪些事件。poll 支持的事件类型如下

在这里插入图片描述

2.示例代码

服务端示例代码

#define _GNU_SOURCE

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


#define MAX_FD 1024
#define DATALEN 128


//初始化服务器的socket套接字
int InitSocket()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd != -1);

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res != -1);

    res = listen(sockfd,5);
    assert(res != -1);

    return sockfd;
}

//初始化记录服务器套接字的数组
void InitPollFds(struct pollfd fds[],int n)
{
    for(int i = 0; i < n; i++)
    {
        fds[i].fd = -1;
    }
}

//将套接字描述符添加到数组中
void AddFdToPollFds(struct pollfd fds[],int n, int fd, short events)
{
    for(int i = 0; i < n; i++)
    {
        if(fds[i].fd == -1)
        {
            fds[i].fd = fd;
            fds[i].events = events;
            break;
        }
    }
}


//接收客户端连接
void GetClientLink(struct pollfd fds[],int n, int sockfd)
{
    struct sockaddr_in caddr;
    memset(&caddr,0,sizeof(caddr));
    int len = sizeof(caddr);

    int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
    if(c < 0)
    {
        return;
    }
    else//接收连接成功,将客户端套接字描述符和我们关心的事件类型添加到fds中
    {
        printf("cilent %d  link sucess\n",c);
        AddFdToPollFds(fds,n,c,POLLIN | POLLRDHUP);//关心事件类型为可读数据和客户端关闭
    }
}


//处理客户端数据 
int DealClientData(int clifd)
{
    char data[DATALEN] = {0};
    int num = recv(clifd,data,DATALEN-1,0);
    if(num <= 0)//读取失败,返回
    {
        return -1;
    }
    else
    {
        printf("%d:%s\n",clifd,data);
        send(clifd,"OK",2,0);
    }
    return 0;
}


//处理poll返回的就绪事件
void DealReadyEvent(struct pollfd fds[],int n,int sockfd)
{
    for(int i = 0; i < n; i++)
    {
        if(fds[i].fd != -1)
        {
            if(fds[i].revents & POLLRDHUP)//判断客户端是否关闭,客户端关闭,这里也要关闭客户端描述符
            {
                printf("client %d over\n",fds[i].fd);
                close(fds[i].fd);
                fds[i].fd = -1;
                continue;
            }
            else if(fds[i].revents & POLLIN)//客户端有可读事件处理
            {
                if(fds[i].fd == sockfd)//如果是服务器套接字描述符有可读事件处理,就是有客户端连接
                {
                    GetClientLink(fds,n,fds[i].fd);
                }
                else//客户端套接字描述符有可读事件处理,去读取数据
                {
                    DealClientData(fds[i].fd);
                }
            }
        }

    }
}

int main()
{
    int sockfd = InitSocket();
    assert(sockfd != -1);

    struct pollfd fds[MAX_FD];
    InitPollFds(fds,MAX_FD);
    AddFdToPollFds(fds,MAX_FD,sockfd,POLLIN);
    
    while(1)
    {
        int n = poll(fds,MAX_FD,5000);//5秒时间
        if(n < 0)//poll失败,服务器退出
        {
            printf("poll error\n");
            break;
        }
        else if(n == 0)//规定时间内没有就绪事件产生
        {
            printf("time out\n");
            continue;
        }
        else//有就绪事件产生
        {
            DealReadyEvent(fds,MAX_FD,sockfd);
        }
    }
    exit(0);
}

客户端示例代码

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

int InitSocket()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd != -1);

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res != -1);

    return sockfd;
}

int main()
{
    int sockfd = InitSocket();
    assert(sockfd != -1);

    while(1)
    {
        printf("please input:\n");
        char buff[128] = {0};
        fgets(buff,127,stdin);
        if(strncmp(buff,"end",3) == 0)
        {
            break;
        }

        int n = send(sockfd,buff,strlen(buff)-1,0);
        if(n < 0)
        {
            printf("send error\n");
            break;
        }

        memset(buff,0,128);
        n = recv(sockfd,buff,127,0);
        if(n < 0)
        {
            printf("recv error\n");
            break;
        }
        printf("%s\n");

    }
    close(sockfd);
    exit(0);

}

运行结果
服务端:
在这里插入图片描述
客户端1和客户端2:
在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孟小胖_H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值