• 【网络编程实例】C++实现基于I/O复用epoll函数的服务器和客户端通信


    参考《TCP/IP网络编程第17章》

    epoll函数
    • epoll函数的优点:
      • 无需编写以监视状态变化为目的的针对所有文件描述符的循环语句;
      • 调用对应于 select 函数的 epoll_wait 函数时无需每次传递监视对象信息。
    • epoll 函数的功能:
      • epoll_create:创建保存 epoll 文件描述符的空间;
      • epoll_ctl:向空间注册并注销文件描述符;
      • epoll_wait:与 select 函数类似,等待文件描述符发生变化;
    • epoll_event 结构体:
      • 用于保存事件的文件描述符结合;
      • epoll_event 的成员 events 中可以保存的常量及所指的事件类型:
        • EPOLLIN:需要读取数据的情况
        • EPOLLOUT:输出缓冲为空,可以立即发送数据的情况
        • EPOLLPRI:收到 OOB 数据的情况
        • EPOLLRDHUP:断开连接或半关闭的情况,这在边缘触发方式下非常有用
        • EPOLLERR:发生错误的情况
    实现基于epoll函数的服务器和客户端通信
    • 服务器端代码
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    
    using std::cout;
    using std::cin;
    using std::string;
    using std::endl;
    
    #define BUF_SIZE 1024
    #define EPOLL_SIZE 50
    #define SERVER_IP "127.0.0.1"    // 指定服务端的IP
    #define SERVER_PORT 9190            // 指定服务端的port
    
    int main()
    {
        int serv_sock, clnt_sock;
        struct sockaddr_in serv_adr, clnt_adr;
        socklen_t adr_sz;
        int str_len, i;
        char buf[BUF_SIZE];
    
        struct epoll_event *ep_events;
        struct epoll_event event;
        int epfd, event_cnt;
    
        serv_sock = socket(PF_INET, SOCK_STREAM, 0);
        bzero(&serv_adr, sizeof(serv_adr));
        serv_adr.sin_family = AF_INET;
        serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_adr.sin_port = htons(SERVER_PORT);
    
        if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1) 
            cout << "Bind error" << errno << ": " << strerror(errno) << endl;
        
        if(listen(serv_sock, 5) == -1) 
             cout << "Listen error" <<  errno << ": " << strerror(errno) << endl;
    
        epfd = epoll_create(EPOLL_SIZE);//可以忽略这个参数,填入的参数为操作系统参考
        ep_events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * EPOLL_SIZE);
    
        event.events = EPOLLIN;//需要读取数据的情况
        event.data.fd = serv_sock;
         //例程epfd 中添加文件描述符 serv_sock,目的是监听 enevt 中的事件
        epoll_ctl(epfd, EPOLL_CTL_ADD,serv_sock, &event);
    
        while(1)
        {
            event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, - 1);//获取改变了的文件描述符,返回数量
            if(event_cnt == -1) {
                cout << "epoll_wait() error" << endl;
                break;
            }
    
            for(i = 0; i < event_cnt; ++i) 
            {
                if(ep_events[i].data.fd == serv_sock)//客户端请求连接时
                {
                    adr_sz = sizeof(clnt_adr);
                    clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
                    event.events = EPOLLIN;
                    event.data.fd = clnt_sock;//把客户端套接字添加进去
                    epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
                    cout << "connected client:" << clnt_sock << endl;
                }
                else //是客户端套接字时
                {
                    str_len = read(ep_events[i].data.fd,buf, BUF_SIZE);
                    if(str_len == 0)
                    {
                        epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, &event);
                        close(ep_events[i].data.fd);
                        cout << "closed client:" << ep_events[i].data.fd << endl;
                    }
                    else
                    {
                        write(ep_events[i].data.fd,buf, str_len);
                    }
                }
            }
        }
        close(serv_sock);
        close(epfd);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 客户端代码
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    
    using std::cout;
    using std::cin;
    using std::string;
    using std::endl;
    
    #define BUFFSIZE 1024
    #define SERVER_IP "127.0.0.1"    // 指定服务端的IP
    #define SERVER_PORT 9190            // 指定服务端的port
    
    
    int main()
    {
        int sock;
        char message[BUFFSIZE];
        int str_len;
        struct sockaddr_in serv_adr;
    
        sock = socket(PF_INET, SOCK_STREAM, 0);
        if(sock == -1) cout << "socket() error" << endl;
    
        bzero(&serv_adr, sizeof(serv_adr));
    
        serv_adr.sin_family = AF_INET;
        serv_adr.sin_addr.s_addr = inet_addr(SERVER_IP);
        serv_adr.sin_port = htons(SERVER_PORT);
    
        if(connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
            cout << "connect() error: " << errno << " " << strerror(errno) << endl;
        else
            cout << "connected............" << endl;
    
        while(1)
        {
            cout << "Input message(Q to quit): " << endl;
            cin >> message;
            if(!strcmp(message, "q") || !strcmp(message, "Q"))
                break;
    
            write(sock, message, strlen(message));
            str_len = read(sock, message, BUFFSIZE - 1);
            message[str_len] = 0;
            cout << "Message from server:" << message << endl;
        }
        close(sock);
        return 0;
    }
    
    
    
    
    
    
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
  • 相关阅读:
    Springboot + Easyexcel读取写入数据,多头行数,多sheet,复杂表头简单实现
    MyBatis的配置文件
    神器必会!特别好使的编辑器Source Insight
    手写分布式配置中心(二)实现分布式配置中心的简单版本
    【云原生】使用Dockerfile制作openGauss镜像
    Tensorflow安装GPU版本的一些问题处理
    python操作数据库pymysql
    【MySQL数据库】(三)函数
    Vue如何监听键盘事件
    【Python 3】函数
  • 原文地址:https://blog.csdn.net/weixin_42659457/article/details/128051301