首页 > 代码库 > 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。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。