• Linux C++ 网络编程学习系列(3)——多路IO之poll实现


    poll实现多路IO

    1. 源码地址:https://github.com/whuwzp/linuxc/tree/master/poll
    2. 源码说明:
      • server.cpp: 监听127.1:6666,功能是将收到的小写转大写
      • include/wrap.cpp: 封装的一些socket基本操作,加了基本的错误处理

    1. 概要

    一定先select实现,poll是select的改进版:,有以下不同

    1. 输入输出参数方面
      • select: 需要allfds和readfds两个集合,因为readfds作为输入输出参数,每次select前都要用allfds赋值给它
      • poll: pollfd结构体中,有events和revents两个,分别用来表示输入输出
    2. 监听集合的形式
      • select: 用fd_set位图标记需要监听的fd
      • poll: 用pollfd结构体中的fd成员标记,用结构体数组表示需要监听的集合
    3. 监听信号类型
      • select: 用readfds, writefds集合分别表示
      • poll: 用POLLIN,POLLOUT表示,fds[0].events = POLLIN
    4. 判断是否有信号
      • select: 用FD_ISSET(fd,readfds)
      • poll: 用fds[0].revents & POLLIN

    2. 核心代码

    #include "include/wrap.h"
    #include <arpa/inet.h>
    #include <ctype.h>
    #include <errno.h>
    #include <netinet/in.h>
    #include <poll.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdnoreturn.h>
    #include <string.h>
    #include <sys/select.h>
    #include <sys/socket.h>
    #include <sys/types.h> /* See NOTES */
    #include <unistd.h>
    #include <wait.h>
    #define LOCALIP "127.0.0.1"
    #define PORT 6666
    
    void handler(char *in, char *out) {
        for (int i = 0; i < (int)strlen(out) + 1; ++i) {
            out[i] = toupper(in[i]);
        }
    }
    
    int workthread(const int &fd_client) {
        char recvbuf[2048] = {0};
        char sendbuf[2048] = {0};
        int  ret           = 0;
    
        ret = (int)Read(fd_client, recvbuf, 2048);
        if (ret <= 0) {
            printf("ret==0
    ");
            return ret;
        }
    
        handler(recvbuf, sendbuf);
    
        ret = (int)Write(fd_client, sendbuf, strlen(sendbuf) + 1);
        return ret;
    }
    
    void startsock(int &fd, struct sockaddr_in &addr, const char *ip,
                   const int port) {
        fd = Socket(AF_INET, SOCK_STREAM, 0);
        memset(&addr, 0, sizeof(addr));
        addr.sin_family      = AF_INET;
        addr.sin_addr.s_addr = inet_addr(ip);
        addr.sin_port        = htons(port);
    }
    int main() {
        int                fd_server = 0;
        int                fd_client = 0;
        int                ret       = 0;
        struct sockaddr_in sock_client;
        struct sockaddr_in sock_server;
        socklen_t          client_len = (socklen_t)sizeof(sock_client);
        int                opt        = 0;
        struct pollfd      fds[FD_SETSIZE];
        int                maxi    = 0;
        int                nselect = 0;
        int                i       = 0;
    
        startsock(fd_server, sock_server, LOCALIP, PORT);
        opt = 1;
        Setsockopt(fd_server, SOL_SOCKET, SO_REUSEADDR, &opt,
                   (socklen_t)sizeof(opt));
        Bind(fd_server, (struct sockaddr *)&sock_server, sizeof(sock_server));
        Listen(fd_server, 5);
    
        for (i = 0; i < FD_SETSIZE; ++i) {
            fds[i].fd     = -1;
            fds[i].events = POLLIN;
        }
        fds[0].fd = fd_server;
        maxi   = 0;
        
    	while (true) {
            printf("polling...
    ");
            nselect = poll(fds, (unsigned long)maxi+1, -1);
            if (nselect == -1) {
                if (errno == EINTR) {
                    continue;
                } else {
                    perror_exit("poll failed");
                }
            }
            printf("get %d select
    ", nselect);
            if (fds[0].revents & POLLIN) {
                fd_client =
                    Accept(fd_server, (struct sockaddr *)&sock_client, &client_len);
                printf("accept: %s: %d
    ", inet_ntoa(sock_client.sin_addr),
                       ntohs(sock_client.sin_port));
                for (i = 0; i < FD_SETSIZE; ++i) {
                    if (fds[i].fd != -1) continue;
                    fds[i].fd = fd_client;
                    break;
                }
                printf("i: %d, FD_SETSIZE: %d
    ", i, FD_SETSIZE);
                if (i == FD_SETSIZE) perror_exit("too many clients");
                if (i > maxi) maxi = i;
                nselect--;
            }
            printf("going to find client, maxi: %d, nselect: %d
    ", maxi, nselect);
            for (i = 0; (i <= maxi) && (nselect > 0); ++i) {
                if (!( fds[i].revents & POLLIN )) continue;
                printf("find client %d
    ", i + 1);
                ret = workthread(fds[i].fd);
                if (ret <= 0) {
                    Close(fds[i].fd);
                    fds[i].fd = -1;
                }
                nselect--;
            }
            sleep(3);
        }
        Close(fd_server);
        for (i = 0; i < FD_SETSIZE; ++i) {
            if (fds[i].fd == -1) continue;
            Close(fds[i].fd);
        }
    }
    

    3. 参考网址

    1. https://www.bilibili.com/video/av53016117
  • 相关阅读:
    page指令
    CMD设IP
    JDBC的几种驱动
    Python的闭包使用
    1189. 扫雷游戏
    1287. 递增的三元子序列
    Pip安装使用国内源的两种方法
    Python Classmethod和Staticmethod函数
    Git提交远程仓库
    Git分支管理
  • 原文地址:https://www.cnblogs.com/whuwzp/p/linux_cpp_network_3.html
Copyright © 2020-2023  润新知