一、简单的服务器I/O模型
最简单的的TCP服务器,有三种模式:
1、单执行流,一个server端连接一个client端
2、多进程,一个server端通过多进程的方式,每个进程连接一个client端
3、多线程,一个server端通过多进程的方式,每个线程连接一个client端
(http://zhweizhi.blog.51cto.com/10800691/1830267) 这里实现过
要提升服务器性能,其实就是想要让一个server端能在负载允许的情况下,连接尽可能多的client端。
因此,以上三种模式中:
第一种模式,一个服务器连接一个客户端 ~!@#%…… 这是低性能服务器 是没人会考虑的
第二种模式,用多进程,能同时服务多个client端,不过线程开销较大,因此这也不是最好的方式
第三种模式,用多线程,线程的开销比进程要小,因此这种方式是 这三种方式中,最优的方式。
二、所谓高性能
我们仔细分析第三种模式下,server端每个线程和对应连接的client在进行通信的过程中,其实都是阻塞方式的。
也就是,对于server端的每个线程,在对面客户端端有消息的时候(连接、接受、断开时)进行处理,而当对面没有消息过来的时候,则一直等待。
因此,在这种阻塞模式下,这些等待的时间被白白浪费掉了。归根到底,每个线程也只能服务一个客户端,因此还不够高性能。要想提高性能,就是要尽可能减少等待时间,也就是说,服务器最高效的工作状态是: 在能力范围内一直在进行数据搬迁。
要想进一步提升服务器性能,达到上述的工作状态,条件允许时(大部分情况都是如此)自然需要换一种效率更高的I/O模型,这里有以下模型:
//1、非阻塞I/O; //2、I/O复用 //3、信号驱动I/O //4、异步I/O
以上在这些模型当中,效率最高的当属第二种:I/O复用模型。
关于I/O复用,客官且听我慢慢道来
三、所谓I/O复用
I/O复用,是针对一个单线程而言的,采用I/O复用的server端,一个线程就可以处理多个client端。
它的实现,就好像有一个"管家",这个"管家"被托管了许多个套接字。当有新的client端要连接的时候,把这个client端(的句柄)托管给这个“管家”,所以这个“管家“上很有可能被托管了许多个client端。
管家的任务就是:管理他托管的这些套接字,如果这些套接字有消息传来,就通知server。
而server则平时一直等待它的"管家",直到"管家"告诉它有消息,才做相应的处理。
这样,server端的一个线程,就能同时服务许多client端,相比多线程模型,线程用来等待时间的比例明显低了很多,效率也高了许多。
要实现I/O复用,有三种模式:分别是: select、poll、epoll,以下分别介绍。
三、I/O复用——select 和 poll
别看poll 和 epoll 名字就差一个e,但其实 poll 和 select 更像,所以这里就把这两个模型放在一起介绍了。
(1)、select模式
还记得前面说到的那个"管家"吗?现在这里管家就是select,管家有一个(或多个)名单fd,fd上记录着所有被托管着的句柄。
//fd是一个fd_set类型的变量。fd_set中存放的句柄,都是通过位运算的方式存放的。
select模式,用到的函数有这些:
int select(int nfds, fd_set *readfds, fd_set *exceptfds, struct timeval *timeout); void FD_CLR(int fd, fd_set *set); int FD_ISSET(int fd, fd_set *set); void FD_SET(int fd, fd_set *set); void FD_ZERO(fd_set *set);
(2)、poll模式
高性能服务器——I/O多路转接的三种模式(select &poll& epoll)
原文地址:http://zhweizhi.blog.51cto.com/10800691/1832644