服务器需要解决的如下问题:
(1)如何检测有新客户端连接? 答: IO复用的select、poll、epoll等socket API
首先关于IO复用机制的比较,目前windows系统支持select、WSAAsyncSelect、WSAEventSelect、完成端口(IOCP),linux系统支持select、poll、epoll。
1. select和poll :主动定时轮询是否有可读可写事件,效率不高;
2.WSAAsyncSelect、WSAEventSelect、完成端口(IOCP)、epoll:被动异步事件通知,减少无效时间,WSAAsyncSelect是利用windows消息队列的事件机制来通知我们设定的窗口过程函
数,IOCP是利用GetQueuedCompletionStatus返回正确的状态,epoll是epoll_wait函数返回。比如connect函数连接另外一端,如果连接socket是异步的,那么connect虽然不能立刻连接完成,但是也是
会立刻返回,无需等待,等连接完成之后,WSAAsyncSelect会返回FD_CONNECT事件告诉我们连接成功,epoll会产生EPOLLOUT事件,我们也能知道连接完成。甚至socket有数据可读时
WSAAsyncSelect产生FD_READ事件,epoll产生EPOLLIN事件,等等。
(2)如何接受客户端连接? 答:socket API的accept函数
默认accept函数会阻塞在那里,如果epoll检测到侦听socket上有EPOLLIN事件,或者WSAAsyncSelect检测到有FD_ACCEPT事件,那么就表明此时有新连接到来,这个时候调用accept函数,就不
会阻塞了。当然产生的新socket你应该也设置成非阻塞的。这样就能在新socket上收发数据了。
(3)如何检测客户端是否有数据发来?答:如上(1)中注册的IO复用机制是否有可写事件
(4)如何收取客户端发来的数据? 答:收取客户端数据用recv函数,socket上有可读事件的时候才去收取数据,这样调用recv或者read函数时不用等待,对于非阻塞的socket,如果无数据recv或者
read函数会立即返回,错误码为EWOULDBLOCK。
(5)如何检测连接异常?答:当服务器收到异常事件EPOLLERR或者关闭事件FD_CLOSE,可以关闭socket,此外,recv或者read返回0时也可以关闭socket
(6)如何给客户端发送数据?给客户端发送数据用send函数 服务器不需要时刻侦听可写事件,只是在需要有数据要发送,则先尝试着去发送,如果发送不了或者只发送出去部分,剩下需要将其缓存起
来,然后设置检测该socket上可写事件,下次可写事件产生时,再继续发送,如果还是不能完全发出去,则继续设置侦听可写事件,如此往复,一直到所有数据都发出去为止。一旦所有数据都发出去以
后,服务器要移除侦听可写事件,避免无用的可写事件通知。
(7)如何在给客户端发完数据后关闭连接?连接关闭分为主动关闭和被动关闭,被动关闭是服务器检测到客户端异常或者关闭事件、recv和send立即返回0时被迫关闭连接;主动关闭是动调用
close/closesocket来关闭连接,比如客户端发送非法的数据,比如一些网络攻击的尝试性数据包。
(8)收发缓冲区如何设定?缓冲可以像string、vector一样,设计出一个可以动态增长的缓冲区,按需分配,不够还可以扩展。
(9)协议的设计要点:像TCP/IP协议基于字节的流式数据,需要界定包的大小已经包的界限,比如固定包的大小,使用\r\n作为包结束符,并且为了节省网络带宽和加快数据处理,尽可能使包的长度
比较小。
(10)服务器程序结构的组织:主流的思想是one thread one loop的策略,设定一些线程在一个循环里面做网络通信相关的事情,另外设定一些线程去处理接收到的数据,并解包处理业务逻辑。如果
多线程设定无法提高提高程序性能,浪费CPU时间片进行上下文切换,可以将网络线程与业务逻辑线程合并。