当前位置: 代码迷 >> 综合 >> linux中网络编程I/O模型---poll
  详细解决方案

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

热度:47   发布时间:2023-09-29 21:11:50.0

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:acceptfdstruct sockaddr_in my_addr;  //本机地址信息//初始化流式socketif ((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 : bstatic 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);}}
}