Linux高性能服务器之多路转接(1)----select模型实现
来源:互联网 发布:淘宝手机卖场 编辑:程序博客网 时间:2024/06/05 02:50
本文将讨论如下几个问题:
什么是多路转接
如何用select模型搭建高性能服务器(select基本使用)
select和多线程各有什么优缺点
一、什么是多路转接
在Linux中,有五种I/O模型,阻塞式、非阻塞式、多路转接、信号驱动、异步,其中前四种又叫同步I/O。
要理解select,必须先理解I/O; 我们在网络上读写数据是通过Socket完成的,但是读的时候如何确定对方写了数据或对方有没有写完数据,而写的时候怎么知道有空间可以写,所以就需要I/O机制来控制。(系统内部同理)
由此可知I/O分为两步:等和数据迁移
这五种I/O模型决定了I/O中等待部分的等待方式,本文讨论的多路转接相对其他效率高一些。
用钓鱼来比喻这五种模型,分别如下:
阻塞式I/O : 放一个鱼竿,盯着鱼钩什么都不干;
非阻塞式 I/O :放一个鱼竿,隔一段时间检查一次有没有鱼上钩;
信号驱动I/O : 鱼竿上系着一个铃铛,钓鱼的人在旁边看书,一旦铃铛响,直接把鱼竿拿上来(不检查是否有鱼);
多路转接I/O : 放10个鱼竿,哪个鱼竿有鱼上钩就拿起鱼竿;
异步I/O : 找一个人帮他钓鱼, 本身只负责布置任务和回收资源(鱼)
可以看出来,多路转接的等待方式充分利用了资源,把I/O中等的时间最小化。
二、select搭建的服务器
先看一下select函数的各个参数
各参数作用:
nfds --- 关心的文件描述符数量
readfds---select关心该集合中的文件描述符是否可读
writefds---关心该集合中文件描述符是否可写
exceptfds --- 关心是否出错
这三个文件描述符集返回的时候都表示哪个文件描述符发生了变化
timeout--- 当设置为NULL时,阻塞等待,设置成0时,非阻塞等待,大于0时表示等待的时间
timeout的结构体:
struct timeval { long tv_sec; /* 秒*/ long tv_usec; /* 微秒 */ };
返回值:失败返回-1,成功返回发生变化的文件描述符个数
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/socket.h>#include<sys/types.h>#include<unistd.h>#include<netinet/in.h>#include<sys/select.h>#include<sys/time.h>int array_fds[1024];static void usage(char* proc){ printf("usage:%s [ip][port]",proc);}ssize_t startup(char* ip,int port){ ssize_t sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); exit(2); } int flag=1; setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag)); struct sockaddr_in server_addr; server_addr.sin_family=AF_INET; server_addr.sin_port=htons(port); server_addr.sin_addr.s_addr=inet_addr(ip); if(bind(sock,(struct sockaddr*)&server_addr,sizeof(server_addr))<0) { perror("bind"); exit(3); } if(listen(sock,10)<0) { perror("listen"); exit(4); } return sock;}int main(int argc,char* argv[]){ if(argc!=3) { usage(argv[0]); exit(1); } int listen_sock=startup(argv[1],atoi(argv[2])); fd_set rfds;//监视读的文件描述符集合 fd_set wfds;//监视写的文件描述符集合 int maxfd=0; int array_size=sizeof(array_fds)/sizeof(array_fds[0]); array_fds[0]=listen_sock; int i=1; for(;i<array_size;++i) { array_fds[i]=-1; } while(1) { struct timeval _timeout={2,0}; FD_ZERO(&rfds); maxfd=-1; for(i=0;i<array_size;++i) { if(array_fds[i]>0) { FD_SET(array_fds[i],&rfds); FD_SET(array_fds[i],&wfds); if(array_fds[i]>maxfd) { maxfd=array_fds[i]; } } } switch(select(maxfd+1,&rfds,&wfds,NULL,NULL)) { case 0: printf("timeout..."); break; case -1: perror("select"); break; default: { int j=0; for(;j<array_size;++j) { if(array_fds[j]<0) { continue; } if(j==0&&FD_ISSET(array_fds[j],&rfds)) { struct sockaddr_in client_addr; socklen_t len=sizeof(client_addr); int new_sock=accept(array_fds[j],(struct sockaddr*)&client_addr,&len); if(new_sock<0) { perror("accept"); exit(5); } int k=1; for(;k<array_size;k++) { if(array_fds[k]<0) { array_fds[k]=new_sock; break; } else if(k==array_size) { close(new_sock); } else { continue; } } } if(j!=0&&FD_ISSET(array_fds[j],&rfds)) { char buff[1024]; memset(buff,0,sizeof(buff)/sizeof(buff[0])); int s=read(array_fds[j],buff,sizeof(buff)); if(s>0) { buff[s]='\0'; printf("client say:%s",buff); if(j!=0&&FD_ISSET(array_fds[j],&wfds)) { write(array_fds[j],buff,strlen(buff)); } } else if(s==0) { printf("client quit!\n"); fflush(stdout); close(array_fds[j]); array_fds[j]=-1; } else { perror("read"); close(array_fds[j]); array_fds[j]=-1; } } } break; } } } return 0;}
客户端用dup2进行了输出重定向
#include<stdio.h>#include<unistd.h>#include<sys/socket.h>#include<sys/types.h>#include<string.h>#include<netinet/in.h>#include<stdlib.h>#include<fcntl.h>#include<arpa/inet.h>#include<sys/stat.h>static void usage(char* proc){ printf("usage:%s[server ip][server port]",proc);}int main(int argc,char*argv[]){ if(argc!=3) { usage(argv[0]); exit(1); } int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); exit(2); } struct sockaddr_in server_addr; server_addr.sin_family=AF_INET; server_addr.sin_port=htons(atoi(argv[2])); server_addr.sin_addr.s_addr=inet_addr(argv[1]); if(connect(sock,(struct sockaddr*)&server_addr,sizeof(server_addr))) { perror("connect"); exit(3); } int oldfd=dup(STDOUT_FILENO); char buff[1024]; while(1) { printf("Enter Please:"); fflush(stdout); dup2(sock,STDOUT_FILENO); int s=read(0,buff,sizeof(buff)-1); if(s>0) { if(buff[0]=='\n') { dup2(oldfd,STDOUT_FILENO); continue; } if(strncmp(buff,"quit",4)==0) { break; } buff[s]=0; printf("%s",buff); fflush(stdout); dup2(oldfd,STDOUT_FILENO); int _s=read(sock,buff,sizeof(buff)-1); if(_s>0) { buff[_s]=0; printf("server echo:#%s",buff); } else if(s<=0) { continue; } } } close(sock); close(oldfd); return 1;}
三、select和多线程相比
select
优点:
提高效率,减少了等的时间
缺点:
1)每次select之后都要设置几个集合的值,增大了开销
2)每次调用select要遍历返回的集合
3)select可以监视的文件描述符有限
多线程虽然等待时间长,但是每一部分没有这么大的开销
多线程致命的缺点是当用户量增大时,占用了太多内存,增大了服务器的压力
- Linux高性能服务器之多路转接(1)----select模型实现
- Linux高性能服务器之多路转接(2)---poll模型
- Linux网络编程【五】:TCP协议高性能服务器(http)模型之I/O多路转接select
- 【Linux网络编程】基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型
- 【Linux网络编程】I/O多路转接之 epoll 高性能简洁http服务器模型
- Linux网络编程【六】:TCP协议高性能服务器(http)模型之I/O多路转接epoll
- Linux网络编程【七】:TCP协议高性能服务器(http)模型之I/O多路转接poll
- (五十三)高并发服务器——多路IO转接机制Select模型
- linux高性能服务器编程之多进程
- 高级I/O之多路转接select
- Linux的I/O多路转接模型和select()
- I/O多路转接----select的服务器实现
- 实现多路转接I/O——select服务器
- 多路转接模型服务器和客户端的实现
- 【Linux网络编程】基于TCP流 I/O多路转接(poll) 的高性能http服务器
- Linux中select实现高性能服务器以及与多进程服务器对比
- 多路转接服务器之select
- 服务器与客户端的模型之select的多路转接
- 创建定时任务—Timer与Alarm
- liunx (centos )redis下载安装配置最新
- Intellij IDEA svn的使用详解
- 开源一个文本分析项目
- 待写博客列表
- Linux高性能服务器之多路转接(1)----select模型实现
- Android 中CollapsingToolbarLayout和Toolbar实现炫酷效果
- MySQL InnoDB中各类语句加锁方式
- Bit Twiddling Hacks
- docker(二)docker容器使用
- liunx (centos) JDK环境配置
- JAVA线程间协作:wait.notify.notifyAll
- FZU
- 仿淘宝、京东、美团使用ViewPager+GridView实现左右滑动查看更多分类导航功能