linux中网络编程I/O模型---poll

linux中网络编程I/O模型—poll(多路复用I/O)

  1. poll函数原型:
int poll(struct pollfd *fds, unsigned int nfds, int timeout);
struct pollfd{
int fd;                 //文件描述符
short events;     //需要监听的事件
short reevents;  //实际发生的事件
}

poll参数列表:

  • struct pollfd * :监听的多个pollfd数组
  • unsigned int:需要监听的文件描述符的数量
  • int:超时时间

events和reevents值列表:

事件分类事件代码意义
合法事件POLLIN有可读数据
合法事件POLLRDNORM有普通数据可读
合法事件POLLRDBAND有优先数据可读
合法事件POLLPRI有紧急数据可读
合法事件POLLOUT写数据不会导致阻塞
合法事件POLLWRNORM写普通数据不会导致阻塞
合法事件POLLWRBAND写优先数据不会导致阻塞
合法事件POLLMSGSIGPOLL消息可用
非法事件POLLER指定的文件描述符发生错误
非法事件POLLHUP指定的文件描述符挂起事件
非法事件POLLNVAL指定的文件描述符非法
  1. poll函数的行为:
    成功时,poll返回fds中revents不为0的文件描述符的数量;如果超时前没有任何事件发生,返回0。失败时,返回-1,并设置errno为下列值之一:
    1)EBADF:一个或多个结构体中指定的文件描述无效
    2)EFAULTfds:指针指向的地址空间超出进程的地址空间
    3)EINTR:请求的事件之前产生一个信号,调用可以重新发起
    3)EINVALnfds:参数超出PLIMIT_NOFILE值
    4)ENOMEM:可用内存不足,无法完成请求

  2. 使用多路复用I/O实现server、client

server.cpp

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <poll.h>



#define IPADDRESS          "127.0.0.1"
#define PORT                    6666
#define MAXLEN               1024
#define LISTENQ               5
#define OPEN_MAX          1000
#define INFTIM                 -1

//创建监听socket,并bind和listen
int bind_and_listen()
{
    int serverfd;     //监听socket:serverfd;数据传输socket:acceptfd
    struct sockaddr_in my_addr;  //本机地址信息
    //初始化流式socket
    if ((serverfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket");
        return -1;
    }
    printf("socket ok\n");
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(PORT);    //将主机字节序的监听端口转换成网络字节序
    my_addr.sin_addr.s_addr = INADDR_ANY;    //监听来自所有网络的消息
    bzero(&(my_addr.sin_zero), 0);
    //将需要监听的socket:serverfd与本地主机绑定
    if (bind(serverfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
    {
        perror("bind");
        return -2;
    }
    printf("bind ok\n");
    if (listen(serverfd, LISTENQ) == -1)
    {
        perror("listen");
        return -3;
    }
    printf("listen ok\n");
    return serverfd;
}

//多路复用poll
void do_poll(int listenfd)
{
    int connfd;
    struct sockaddr_in sock_client;
    socklen_t sock_len;
    struct pollfd poll_fds[OPEN_MAX];
    int maxi;
    int i;
    int nready;
    //将监听socket:serverfd添加到pollfd:poll_fds中
    poll_fds[0].fd = listenfd;
    //设置监听socket可读事件
    poll_fds[0].events = POLLIN;
    //初始化链接描述符
    for (int index = 1; index < OPEN_MAX; index++)
        poll_fds[index].fd = -1;
    maxi = 0;
    //循环处理
    while (1)
    {
        //获取需要处理的文件描述符的数量
        nready = poll(poll_fds, maxi + 1, INFTIM);
        if (nready == -1)
        {
            perror("poll error");
            exit(1);
        }
        //检查监听描述符是否存在可读消息
        if (poll_fds[0].revents & POLLIN)
        {
            sock_len = sizeof(sock_client);
            //接受新的链接
            if ((connfd = accept(listenfd, (struct sockaddr*)&sock_client, &sock_len)) == -1)
            {
                if (errno == EINTR)
                    continue;
                else
                {
                    perror("accept");
                    exit(1);
                }
            }
            fprintf(stdout, "accpet a new client : %s:%d\n", inet_ntoa(sock_client.sin_addr), sock_client.sin_port);
            //将新的链接文件描述符添加到pollfd:poll_fds中
            for (i = 1; i < OPEN_MAX; i++)
            {
                if (poll_fds[i].fd < 0)
                {
                    poll_fds[i].fd = connfd;
                    break;
                }
            }
            if (i == OPEN_MAX)
            {
                fprintf(stderr, "too many clients.\n");
                exit(1);
            }
            //将新的描述符添加到度描述符集合中
            poll_fds[i].events = POLLIN;
            //记录客户链接套接字数量
            maxi = (i > maxi ? i : maxi);
            if (--nready <= 0)
            {
                continue;
            }
         }

        //处理所有客户端发来的数据
        char buffer[MAXLEN];
        memset(buffer, 0, sizeof(buffer));
        int readlen = 0;
        for (i = 1; i < maxi; i++)
        {
            if (poll_fds[i].fd < 0)
                continue;
            //检测客户端描述符是否准备好
            if (poll_fds[i].revents & POLLIN)
            {
                //接收客户端发来的消息
                readlen = read(poll_fds[i].fd, buffer, MAXLEN);
                if (readlen == 0)
                {
                    close(poll_fds[i].fd);
                    poll_fds[i].fd = -1;
                    continue;
                }
                //
                write(STDOUT_FILENO, buffer, readlen);
                //
                write(poll_fds[i].fd, buffer, readlen);
            }
        }
    }
}


int main()
{
    int listenfd = bind_and_listen();
    if (listenfd < 0)
        return 0;
    do_poll(listenfd);
    return 0;
}

client.cpp

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXLEN 1024
#define PORT       6666
#define max(a, b) (a > b) ? a : b

static void handle_connection(int sockfd);

int main(int argc, char *argv[])
{
    int connfd = 0;
    struct sockaddr_in client;
    if (argc < 2)
    {
        printf("Uasge : Clientent [Server IP address]\n");
        return -1;
    }
    client.sin_addr.s_addr = inet_addr(argv[1]);
    client.sin_family = AF_INET;
    client.sin_port = htons(PORT);
    connfd = socket(AF_INET, SOCK_STREAM, 0);
    if (connfd < 0)
    {
        perror("socket");
        return -2;
    }
    if (connect(connfd, (struct sockaddr*)(&client), sizeof(struct sockaddr)) < 0)
    {
        perror("connect");
        return -3;
    }
    //处理链接描述符
    handle_connection(connfd);
    return 0;
}


static void handle_connection(int sockfd)
{
    char sendline[MAXLEN], recvline[MAXLEN];
    struct pollfd poll_fds[2];
    int n;
    //添加链接描述符
    poll_fds[0].fd = sockfd;
    poll_fds[0].events = POLLIN;
    //添加标准输入描述符
    poll_fds[1].fd = STDIN_FILENO;
    poll_fds[1].events = POLLIN;
    while (1)
    {
        poll(poll_fds, 2, -1);
        if (poll_fds[0].revents & POLLIN)
        {
            n = read(sockfd, recvline, MAXLEN);
            if (n == 0)
            {
                fprintf(stderr, "client : server is closed.\n");
                close(sockfd);
            }
            write(STDOUT_FILENO, recvline, n);
        }
        //检测标准输入描述符是否准备好
        if (poll_fds[1].revents & POLLIN)
        {
            n = read(STDOUT_FILENO, sendline, MAXLEN);
            if (n == 0)
            {
                shutdown(sockfd, SHUT_WR);
                printf("111111111111111111111\n");
                continue;
            }
            write(sockfd, sendline, n);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值