zoukankan      html  css  js  c++  java
  • 0731------Linux网络编程----------论封装的乐趣(select、poll、epoll 服务器模型的封装)

    0.春阳语录,代码嵌套三层以上就是一坨垃圾。因此良好的编程风格从封装开始。

    1.封装select服务器模型

      1.1 如何封装?将select需要的数据结构都封装成结构体,通过参数在函数之间传递,将固定的操作封装成相应的函数。

      1.2 封装后的程序:

        1.2.1 封装的头文件 select_t.h   

    #ifndef __SELECT_T_H__
    #define __SELECT_T_H__
    #include <sys/select.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <errno.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    
    typedef struct{
        fd_set allset_;
        fd_set rset_;
        int clients_[FD_SETSIZE];
        int maxi_;
        int maxfd_;
        int nready_;
        int listenfd_;
        void (*handle_callback_)(int, char *buf); //有用户提供回调函数
    }select_t;
    
    void select_init(select_t *sel, int listenfd);
    void select_set_callback(select_t *sel, void (*handler)(int, char *buf));
    void select_do_wait(select_t *sel);
    void select_handle_accept(select_t *sel);
    void select_handle_data(select_t *sel);
    
    #endif

        1.2.2 封装的select类函数 select_t.c

    #include "select_t.h"
    #include "network.h"
    #include <assert.h>
    //仅供本文件内部调用
    void select_add_fd(select_t *sel, int fd); //将新连接的fd加入到监听集合 和 clients数组中
    void select_del_fd(select_t *sel, int i); //将关闭的fd从监听集合和clients数组中基础,并关闭连接
    
    
    void select_init(select_t *sel, int listenfd){
        sel->listenfd_ = listenfd;
        FD_ZERO(&sel->allset_);
        FD_ZERO(&sel->rset_);
        FD_SET(listenfd, &sel->allset_);
        int i;
        for(i = 0; i < FD_SETSIZE; i++){
            sel->clients_[i] = -1;
        }
        sel->maxi_ = -1;
        sel->maxfd_ = listenfd;
    }
    
    void select_set_callback(select_t *sel, void (*handler)(int, char*)){
        sel->handle_callback_ = handler;
    }
    
    void  select_do_wait(select_t *sel){
        sel->rset_ = sel->allset_;
        do{
            sel->nready_ = select(sel->maxfd_ + 1, &sel->rset_, NULL, NULL, NULL);
        }while(sel->nready_ == -1 && errno == EINTR);
    }
    
    void select_handle_accept(select_t *sel){
        if(FD_ISSET(sel->listenfd_, &sel->rset_)){
            int peerfd = accept(sel->listenfd_, NULL, NULL);
            if(peerfd == -1)
                    ERR_EXIT("accept");
            select_add_fd(sel, peerfd);
        }
        sel->nready_--;
    }
    
    void select_handle_data(select_t *sel){
        if(sel->nready_ == 0)
            return;
        int i;
        char recvbuf[1024] = {0};
        for(i = 0; i < FD_SETSIZE; i++){
            if(sel->clients_[i] == -1)
               continue;
            int fd = sel->clients_[i];
            if(FD_ISSET(sel->clients_[i], &sel->rset_)){ //?这里用fd会出错
                int ret = readline(fd, recvbuf, 1024);
                if(ret == -1)
                    ERR_EXIT("readline");
                else if(ret == 0){
                    printf("client closed
    ");
                    select_del_fd(sel, i);
                    continue;
                }
                sel->handle_callback_(fd, recvbuf);
            }
        }
    }
    
    void select_add_fd(select_t *sel, int fd){
        int i;
        for(i = 0; i < FD_SETSIZE; i++){
            if(sel->clients_[i] == -1){
                sel->clients_[i] = fd;
                if(i > sel->maxi_)
                    sel->maxi_ = i;
                break;
            }
        }
        if(i == FD_SETSIZE){
            fprintf(stderr, "too many clients
    ");
            close(fd);
            exit(EXIT_FAILURE);//!
        }
        FD_SET(fd, &sel->allset_);
        if(fd > sel->maxfd_)
            sel->maxfd_ = fd;
    }
    
    void select_del_fd(select_t *sel, int i){
        assert(i >= 0 && i < FD_SETSIZE); //!
        int fd = sel->clients_[i];
        sel->clients_[i] = -1;
        FD_CLR(fd, &sel->allset_);
        close(fd);
    }

        1.2.3 封装后的mian函数 server.c

    #include "network.h"
    #include "select_t.h"
    /*
     * 服务器端使用 select 模型
     *
     * 这里为服务器提供处理客户请求的方法
     * 服务器回调本函数
     */
    void handler(int fd, char *buf){
        printf("recv data:%s", buf);
        writen(fd, buf, strlen(buf));
    }
    
    int main(int argc, const char *argv[])
    {
        if(signal(SIGPIPE, SIG_IGN) == SIG_ERR)
           ERR_EXIT("signal");
        int listenfd = listenfd_init();
        select_t sel;
        select_init(&sel, listenfd);
        select_set_callback(&sel, handler);
        while(1){
            select_do_wait(&sel);
            select_handle_accept(&sel);
            select_handle_data(&sel);
        }
        close(listenfd);
        return 0;
    }

    2.封装poll服务器模型

      2.1 封装后的头文件 poll_t.h

    #ifndef __POLL_T_H__
    #define __POLL_T_H__
    #include <poll.h>
    #include <assert.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    
    typedef struct{
        struct pollfd clients_[2048];
        int maxi_;
        int nready_;
        int listenfd_;
        void (*handle_callback_)(int, char*);
    }poll_t;
    
    void poll_init(poll_t *pol, int listenfd, void (*handler)(int, char*));
    void poll_do_wait(poll_t *pol);
    void poll_handle_accept(poll_t *pol);
    void poll_handle_data(poll_t *pol);
    
    #endif

      2.2 封装的函数 poll_t.c

    #include "poll_t.h"
    #include "network.h"
    
    void poll_add_fd(poll_t *pol, int fd);
    void poll_del_fd(poll_t *pol, int i);
    
    void poll_init(poll_t *pol, int listenfd, void(*handler)(int, char*)){
        pol->listenfd_ = listenfd;
        int i;
        for(i = 0; i < 2048; i++){
           pol->clients_[i].fd = -1;
        }
        pol->clients_[0].fd = listenfd;
        pol->clients_[0].events = POLLIN;
        pol->maxi_ = 0;
        pol->nready_ = 0;
        pol->handle_callback_ = handler;
    }
    
    void poll_do_wait(poll_t *pol){
        int nready;
        do{
            nready = poll(pol->clients_, pol->maxi_ + 1, -1);
        }while(nready == -1 && errno == EINTR);
        if(nready == -1)
            ERR_EXIT("poll");
        pol->nready_ = nready;
    }
    
    void poll_handle_accept(poll_t *pol){
        if(pol->clients_[0].revents & POLLIN){
            int peerfd = accept(pol->listenfd_, NULL, NULL);
            if(peerfd == -1)
                ERR_EXIT("accept");
            poll_add_fd(pol, peerfd);
            --pol->nready_;
        }
    }
    
    void poll_handle_data(poll_t *pol){
        if(pol->nready_ == 0)
            return;
        int i;
        char recvbuf[1024] = {0};
        for(i = 1; i <= pol->maxi_; i++){
            int peerfd = pol->clients_[i].fd;
            if(peerfd == -1)
                continue;
            if(pol->clients_[i].revents & POLLIN){
                int ret = readline(peerfd, recvbuf, 1024);
                if(ret == -1)
                    ERR_EXIT("readline");
                else if(ret == 0){
                    printf("client closed
    ");
                    poll_del_fd(pol, i);
                    continue;
                }
                pol->handle_callback_(peerfd, recvbuf);
            }
        }
    }
    
    void poll_add_fd(poll_t *pol, int fd){
        int i;
        for(i = 0; i < 2048; i++){
            if(pol->clients_[i].fd == -1){
                pol->clients_[i].fd = fd;
                pol->clients_[i].events= POLLIN;
                if(i > pol->maxi_)
                    pol->maxi_ = i;
                break;
            }
        }
        if(i == 2048){
            fprintf(stderr, "too many clients
    ");
            close(fd);
            exit(EXIT_FAILURE);
        }
    }
    
    void poll_del_fd(poll_t *pol, int i){
        assert(i >= 1 && i < 2048);//
        close(pol->clients_[i].fd);
        pol->clients_[i].fd = -1;
    }

      2.3 封装后的main 函数 server.c

    #include "network.h"
    #include "poll_t.h"
    
    void handler(int fd, char *buf){
        printf("recv data:%s", buf);
        writen(fd, buf, strlen(buf));
    }
    
    int main(int argc, const char *argv[])
    {
        if(signal(SIGPIPE, SIG_IGN) == SIG_ERR)
           ERR_EXIT("signal");
        int listenfd = listenfd_init();
        poll_t pol;
        poll_init(&pol, listenfd, handler);
        while(1){
            poll_do_wait(&pol);
            poll_handle_accept(&pol);
            poll_handle_data(&pol);
        }
        close(listenfd);
        return 0;
    }

    3.封装epoll服务器模型

      3.1 封装后的头文件 epoll_t.c

    #ifndef __EPOLL_T_H__
    #define __EPOLL_T_H__
    #include <sys/epoll.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    
    typedef struct{
        int epollfd_;
        struct epoll_event events_[2048];
        int listenfd_;
        int nready_;
        void (*handle_callback_)(int, char*);
    }epoll_t;
    
    void epoll_init(epoll_t *epo, int listenfd, void (*handler)(int, char*));
    void epoll_do_wait(epoll_t *epo);
    void epoll_handle(epoll_t *epo);
    void epoll_close(epoll_t *epo);
    
    #endif  

      3.2 封装后的epoll类函数 epoll_t.c

    #include "epoll_t.h"
    #include "network.h"
    
    void epoll_handle_accept(epoll_t *epo);
    void epoll_handle_data(epoll_t *epo, int peerfd);
    void epoll_add_fd(epoll_t *epo, int fd);
    void epoll_del_fd(epoll_t *epo, int fd);
    
    void epoll_init(epoll_t *epo, int listenfd, void(*handler)(int, char*)){
        if((epo->epollfd_ = epoll_create(2048)) == -1)
            ERR_EXIT("epoll_create");
        epo->listenfd_ = listenfd;
        epoll_add_fd(epo, listenfd);
        epo->nready_ = 0;
        epo->handle_callback_ = handler;
    }
    void epoll_do_wait(epoll_t *epo){
        int nready;
        do{
            nready = epoll_wait(epo->epollfd_, epo->events_, 2048, -1);
        }while(nready == -1 && errno ==  EINTR);
        if(nready == -1)
            ERR_EXIT("epoll_wait");
        epo->nready_ = nready;
    }
    
    void epoll_handle(epoll_t *epo){
        int i;
        for(i = 0 ; i < epo->nready_; i++){
            int fd = epo->events_[i].data.fd;
            if(fd == epo->listenfd_){
                epoll_handle_accept(epo);
            }
            else
                epoll_handle_data(epo, fd);
        }
    }
    
    void epoll_handle_accept(epoll_t *epo){
        int peerfd = accept(epo->listenfd_, NULL, NULL);
        if(peerfd == -1)
            ERR_EXIT("accept");
        epoll_add_fd(epo, peerfd);
    }
    
    void epoll_handle_data(epoll_t *epo, int peerfd){
        char recvbuf[1024] = {0};
        int ret = readline(peerfd, recvbuf, 1024);
        if(ret == -1)
            ERR_EXIT("readline");
        else if(ret == 0){
            printf("client closed
    ");
            epoll_del_fd(epo, peerfd);
            return;
        }
        epo->handle_callback_(peerfd, recvbuf);
    }
    
    void epoll_close(epoll_t *epo){
        close(epo->epollfd_);
        close(epo->listenfd_);
    }
    
    void epoll_add_fd(epoll_t *epo, int fd){
        struct epoll_event ev;
        ev.data.fd = fd;
        ev.events = EPOLLIN;
        if(epoll_ctl(epo->epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1)
            ERR_EXIT("epoll_ctl");
    }
    
    void epoll_del_fd(epoll_t *epo, int fd){
        struct epoll_event ev;
        ev.data.fd = fd;
        if(epoll_ctl(epo->epollfd_, EPOLL_CTL_DEL, fd, &ev) == -1)
            ERR_EXIT("epoll_ctl");
    }

      3.3 封装后的main函数 server.c

    #include "network.h"
    #include "epoll_t.h"
    
    void handler(int fd, char *buf){
        printf("recv data:%s", buf);
        writen(fd, buf, strlen(buf));
    }
    
    int main(int argc, const char *argv[])
    {
        if(signal(SIGPIPE, SIG_IGN) == SIG_ERR)
           ERR_EXIT("signal");
        int listenfd = listenfd_init();
        epoll_t epo;
        epoll_init(&epo, listenfd, handler);
        while(1){
            epoll_do_wait(&epo);
            epoll_handle(&epo);
        }
        epoll_close(&epo);
        return 0;
    }

     4.总结

      封装对一个程序的编写,调试来说都是至关重要的,封装后的代码可读性明显大大提高,因此,以后在写程序时,要注意这一点。此外,从封装这三种模型来看,epoll显然更简单方便,因此,以后在写服务器的时候要改select为epoll。

  • 相关阅读:
    leetcode 第41题 Trapping Rain Water
    leetcode第40题--First Missing Positive
    leetcode第39题--Combination Sum II
    leetcode第38题--Combination Sum
    leetcode第37题--Count and Say
    17_7_18 servlet 动态工程 先加载servlet request注意事项
    17_7_13 Servlet分页思路
    17_7_12 tomcat 安装+(java+tomcat)环境变量+windows安装报错:JRE_HOME
    17_7_12 JDBC 中的 DBCP(链接池)和 Servlet Web工程之间的差异
    17_7_12 DBCP 中 ResultSet 判断是否有值 +判断 一个对象是否包含在数据库中
  • 原文地址:https://www.cnblogs.com/monicalee/p/3883564.html
Copyright © 2011-2022 走看看