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。
0731------Linux网络编程----------论封装的乐趣(select、poll、epoll 服务器模型的封装),古老的榕树,5-wow.com
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。