首页 > 代码库 > 0731------Linux网络编程----------论封装的乐趣(select、poll、epoll 服务器模型的封装)

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\n");                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\n");        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\n");                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\n");        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\n");        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。