关于TCP ,select,epoll服务器的区别与联系
来源:互联网 发布:node express restful 编辑:程序博客网 时间:2024/05/20 05:29
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。而TCP服务器要想实现多个描述符的等待需要用多进程多线程的方式实现。IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:
(1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
(2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
(4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
(5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
下面重点对比select服务器和epoll服务器:
(一)select服务器原理图。
(二)epoll服务器模型
注:select 原理图,摘自 IBM iSeries 信息中心。
(三)优缺点对比
select的几大缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,默认是1024点
epoll是select的改进其优势:
对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。
对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)。
对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大
如下是源码:
(1)TCP服务器
server.c
# include<errno.h># include<unistd.h># include<string.h># include<sys/types.h># define _PORT_ 999# define BACKLOG_ 10int main (){ int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { printf("create socket error,error is:%d,errstring is:%d\n",errno,strerror(errno)); } struct sockaddr_in server_socket; struct sockaddr_in client_socket; bzero(&server_socket,sizeof(server_socket)); server_socket.sin_family=AF_INET; server_socket.sin_addr.s_addr=htonl(INADDR_ANY); server_socket.sin_addr.port=htons(_PORT_); if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))<0); { printf("error code is:%d,errstring is:%d\n",errno,strerror(errno)); close(sock); return 1; } if(listen(sock,_BACKLOG_)<0) { printf("listen error,error is:%d,errstring is:%d\n",errno,strerror(errno)); close(sock); return 2; } printf("bind and listen success,wait accept.."); for(::) { socklen_t len=0; int client_sock=accept(sock,(struct sockaddr *)&client_socket,&len); if(client_sock<0) { printf("accept error error code :%d, error string :%d\n",errno,strerror(errno)); close(sock); return 3; } char buf_ip[INET_ADDRSTRLEN]; memset(buf_ip,'\0',sizeof(buf_ip)); inet_ntop(AF_INET,&client_socket.sin_addr,buf_ip,sizeof(buf_ip)); printf("get connect, ip is:%d,port is:%d\n",buf_ip,ntohs(client_socket.sin_port)); while(1) { char buf[1024]; memset(buf,'\0',sizeof(buf)); read(client_sock,buf,sizeof(buf)); printf("client :# %s\n",buf); printf("server:$"); memset(buf,'\0',sizeof(buf)); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1]='\0'; write(client_sock,buf,strlen(buf)+1); printf("please wait.."); } } close(sock); return 0; }client.c
# include<stdio.h># include<unistd.h># include<sys/socket.h># include<sys/types.h># include<strings.h># include<error.h># include< netinet/in.h># include<arpa/inet.h># define SERVER_PORT 9999# define SERVER_IP "1992.168.0.111"int main(int argc,char* argv[]){ if(argc!=2) { printf("Usage:client IP\n"); return 1; } char *str=argv[1]; char buf[1024]; memset(buf,'\0',sizeof(buf)); struct sockaddr_in server_sock; int sock=socket(AF_INET,SOCK_STREAM,0); bzero(&server_sock,szieof(server_sock)); server_sock.sin_family=AF_INET; inet_pton(AF_INET,SERVER_IP,&server_sock.sin_addr); server_sock.sin_port=htons(SERVER_PORT); int ret=connect(sock,(struct sockaddr *)&server_sock,sizeof(server_sock)); if(ret<0) { printf("connect is failed ...error is :%d,error string is:%d\n",errno,strerror(errno)); return 1; } printf("connect success\n") while(1) { printf("client:#"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1]='\0'; write(sock,buf,sizeof(buf)); if(strncasecmp(buf,"quit",4)==0) { printf("quit!\n"); break; } printf("please wait..\n"); read(sock,buf,sizeof(buf)); printf("server:$%s\n",buf); } close(sock); return 0; }(2)select服务器
# include<stdio.h># include<string.h># include<stdlib.h># include<sys/socket.h># include<sys/types.h># include<sys/select.h># include<netinet/in.h>//not clear#define _PORT_ 8080#define _MAX_SIZE_ 10#define _BACK_LOG_ 3#define _BUF_SIZE_ 1024 int fd_arr[_MAX_SIZE_]; int max_fd=0; static void init_fd_arr() { int i=0; for(;i<_MAX_SIZE_;i++) { fd_arr[i]=-1; } }static int add_fd_arr(int fd){ int i=0; for(;i<_MAX_SIZE_;i++) { if(fd_arr[i]==-1) { fd_arr[i]=fd; return 0; } } return 1; } static int remove_fd_arr(int fd) { int i=0; for(;i<_MAX_SIZE_;i++) { if(fd_arr[i]==fd) { fd_arr[i]=-1;//remove target fd break; } } return 0; }static int reload_fd_set(fd_set *fd_setp) //not clear{ int i=0; for(;i<_MAX_SIZE_;i++) { if(fd_arr[i]!=-1) { FD_SET(fd_arr[i],fd_setp); if(fd_arr[i]>max_fd) { max_fd=fd_arr[i]; } } } return 0; } static void print_msg(int i,char buf[]) { printf("fd:%d,msg:%s\n",i,buf); } int select_server() { struct sockaddr_in ser; struct sockaddr_in cli; fd_set fds; int fd=socket(AF_INET,SOCK_STREAM,0); if(fd<0) { perror("create socket error"); return 1; } printf("create socket success\n"); int yes=1;//not clear setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)); memset(&ser,'\0',sizeof(ser)); ser.sin_family=AF_INET; ser.sin_port=htons(_PORT_); ser.sin_addr.s_addr=INADDR_ANY;//auto fill with IP if(bind(fd,(struct sockaddr*)&ser,sizeof(ser))<0) { perror("bind error"); return 2; } printf("bind socket success\n"); init_fd_arr(); add_fd_arr(fd); FD_ZERO(&fds); if(listen(fd,_BACK_LOG_)<0) { perror("listen error"); return 3; } printf("listen socket success\n"); while(1) { #ifdef _SELECT_ reload_fd_set(&fds); struct timeval timeout=(30,0); switch(select(max_fd+1,&fds,NULL,NULL,&timeout)) { case -1: printf("select error,quit!\n"); exit(1); break; case 0: printf("select timeout,continue wait..\n"); break: default://return normal { int index=0; for(;index<_MAX_SIZE_;index++) { if(index==0&&fd_arr[index]!=-1&&FD_ISSELECT(fd_arr[index],&fds))//new accept socklen_t len=sizeof(cli); memset(&cli,'\0',sizeof(cli)); int new_fd=accept(fd,(struct sockaddr*)&cli,&len); if(-1!=new_fd) { printf("get a new requeset!\n"); if(1==add_fd_arr(new_fd))//add new fd failed { perror("fd arr is full.close fd !\n") close(new_fd); } } continue; } if(fd_arr[index]!=-1&&FD_ISSET(fd_arr[index],&fds))//just for read fd { char buf[_BUF_SIZE_]; memset(buf,'\0',sizeof(buf)); ssize_t size=recv(fd_arr[index],buf,sizeof(buf)-1,0);//read data if(size==0||size==-1) { printf("remote client close. size is:,%d\n",size); remove_fd_arr(fd_arr[index]); close(fd_arr[index]); FD_CLR(fd_arr[index],&fds); } else { print_msg(index,buf); } } } } break; } #endif } } int main () { select_server(); return 0; }(3)epoll服务器
# include<stdio.h># include<stdlib.h># include<fcntl.h># include<string.h># include<unistd.h># include<sys/socket.h># include<netinet/in.h># include<arpa/inet.h># include<sys/epoll.h>static int startup(const char *_ip,int _port)//create a socket{//sock()int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){ perror("socket error"); exit(2); }//struct sockaddr_instruct sockaddr_in local;local.sin_family=AF_INET;local.sin_port=htons(_port);local.sin_addr.s_addr=inet_addr(_ip);//bind()if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind error"); exit(3); }//listen()if(listen(sock,5)<0){ perror("listen error"); exit(4); } return sock;}static void usage(const char *proc){ printf("Usage:%s [ip] [port]",proc); }static int set_noblock(int sock){ int fl=fcntl(sock,F_GETFL); return fcntl(sock,F_SETFL,fl|O_NONBLOCK); }int main (int argc,char *argv[]){ // if() if(argc!=3) { usage("argv[0]"); exit(1); }int listen_sock=startup(argv[1],atoi(argv[2]));//create a epollint epfd=epoll_create(256);if(epfd<0){ perror("epoll_create"); exit(5); }//errorelse{ struct epoll_event _ev; _ev.events=EPOLLIN; _ev.data.fd=listen_sock; epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&_ev);//add a socketstruct epoll_event _ready_ev[128];int _ready_evs=128;int _timeout=-1;//blockint nums=0;int done=0;while(!done){ switch(nums=(epoll_wait(epfd,_ready_ev,_ready_evs,_timeout))) { case 0: printf("time out...\n"); break; case -1: perror("epoll_wait"); break; //default default: { int i=0; for(;i<nums;++i) { int _fd=_ready_ev[i].data.fd; if(_fd==listen_sock&&_ready_ev[i].events&EPOLLIN) { //get a new link struct sockaddr_in peer; socklen_t len=sizeof(peer); int new_sock=accept(listen_sock,(struct sockaddr*)&peer,&len);//need finish if(new_sock>0) { printf("client info,socket:%s:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port)); //link success //then add to epoll _ev.events=EPOLLIN | EPOLLET; _ev.data.fd=new_sock; set_noblock(new_sock); epoll_ctl(epfd,EPOLL_CTL_ADD,new_sock,&_ev); } else { if(_ready_ev[i].events&EPOLLIN) { char buf[102400]; memset(buf,'\0',sizeof(buf)); //read or write ssize_t _s=recv(_fd,buf,sizeof(buf)-1,0); if(_s>0) { printf("client#%s\n",buf); _ev.events=EPOLLOUT | EPOLLET; _ev.data.fd=_fd; epoll_ctl(epfd,EPOLL_CTL_MOD,_fd,&_ev); } else if(_s==0) { printf("client close..\n"); //close server //close epoll epoll_ctl(epfd,EPOLL_CTL_DEL,_fd,NULL); close(_fd); } else { perror("error"); } } else if(_ready_ev[i].events&EPOLLOUT) { const char *msg="HTTP/1.1 200 OK\r\n\r\n<h1>hello world +_+</h1>\r\n"; send(_fd,msg,strlen(msg),0); epoll_ctl(epfd,EPOLL_CTL_DEL,_fd,NULL); close(_fd); } } } } break; } } } }}//exc 1. the diference of select poll and epoll//2.epoll// http xieyi(zhengdehenjindian)kanzhegewangye
- 关于TCP ,select,epoll服务器的区别与联系
- select、poll、epoll用法区别与联系
- select、 poll 、epoll 区别与联系
- select,poll,epoll的区别及联系
- select和epoll的区别和联系
- epoll与select的区别
- select poll epoll联系区别
- select poll epoll区别联系
- Nginx 服务器 select 和epoll的区别
- Nginx 服务器 select 和epoll的区别
- Nginx 服务器 select 和epoll的区别
- epoll与select区别
- epoll与select区别
- select、poll与epoll的区别
- select.poll,epoll的区别与应用
- epoll,select的区别
- TCP与UDP的区别与联系
- epoll学习,epoll与select,pool区别
- pjsip学习笔记之一 –pjsip库编译及使用
- UVALive 6621 Pocket Cube(暴力)
- Easy 104题 Maximum Depth of Binary Tree
- CodeForces 589B Layer Cake
- 详解HttpURLConnection
- 关于TCP ,select,epoll服务器的区别与联系
- Codeforces Beta Round #5
- UVALive 6620 Josephina and RPG(概率DP)
- 2015上海EC-Fianl比赛总结
- Aizu - 2224
- .net 验证码
- POJ 2195 Going Home
- MFC 的一些常用类
- ViewPager的使用