一个客户一个子进程,是阻塞式网络编程,适合于长连接,不适合短连接、并发数不大的情况,尤其不适合fork()的开销大于本身服务的情况.
编程模型
(1)迭代服务器,TCP迭代服务器总是完全处理完某个客户的请求之后才会转向为下一个客户服务,因此对下一个用户的响应时间不够好,比较适合短连接服务(一般会有一方主动断开TCP连接);如果在单核处理器上,由于其没有进程控制的时间,可以使用该程序作为基准测试程序,来比较其他模型的服务器程序的运行时间,进而可以得到进程的控制时间;
(2)并发服务器1,它通常在accept阻塞,阻塞返回后调用fork派生一个子进程来处理每个客户,每一个客户一个进程,因此客户数目的限制在于子进程数目的限制;即使在写时复制的情况下,fork的开销的还是很大的,当有数万的连接时,弊端就比较明显了;
(3)并发服务器2,它与并发服务器1类似,只不过预先派生一定数量为N的子进程,同时也监听,这样当各个客户连接到达时,这些子进程就能够立即为它们服务,无需fork开销;但是如果连接数等于N时(注,父进程不参与服务),此时子进程将会完全被使用完,新到的连接需等到下一个子进程可用,但是如果连接队列的数目还未到达listen调用的backlog数,虽然此时客户连接已经被内核完成三次握手,但是无法被服务,需要等到子进程执行到accept才可返回,那么客户端将会明显察觉到服务器在响应时间上的恶化,虽然connect可能会立即返回,第一个请求在一段时间之后才会被服务器处理;当然服务端还有可能会有惊群问题,如果多个子进程都阻塞在accept,当一个客户连接到达时,所有的这些子进程都会被唤醒,这是因为所有的子进程的监听描述符都指向同一个socket结构,此时这些子进程将会争用这个连接,最终所有的子进程被唤醒,内核进程调度到的第一个子进程将会获得这个客户连接,而其他几个子进程将会继续睡眠(有些内核这些子进程可能根本不会唤醒,或直接accept返回一个错误);据说Linux系统已经在内核态已解决惊群问题;
(4)并发服务器3,它与并发服务器2类似,只不过在accpet加上一段锁,使得accept这段代码成为临界区;由原先的accept争用变成了锁争用,最终只有一个进程阻塞在accept上;这样其他进程进入临界区后,将会独占accept的使用;
(5)并发服务器4,它使用主进程分发机制;子进程不进行accept调用,统一由父进程accept然后将连接描述符传递给对应的子进程,然后由由其服务;父进程可使用普通的轮转法来选择子进程来进行连接描述符的传递;
特点
(1)TCP是一个全双工的协议,同时支持read()和write();而阻塞式网络编程中,服务器主进程通常阻塞在accept上,而由子进程具体负责与客户端通信,客户端进程通常阻塞在read系统调用上,等待客户端发来的命令;这样就需要服务端和客户端的编程需要相互配合起来;假设客户端进程由于错误的程序逻辑,双方有可能出现通信死锁情况;此外,某些客户端继续阻塞读连接数据,又需要读键盘输入,如果阻塞的读连接,那么是无法从键盘读输入的;
(2)服务器为每一个连接准备一个进程,一个这样的连接将会独占这样的进程,服务器的开销较大;
(3)一个客户一个进程模型,关键的特点在于fork()后对监听描述符的影响;并发服务器1仅有父进程监听后产生连接描述符,然后由fork后的子进程来处理;而并发服务器2,3中的所有进程都进行监听(父进程可以不监听),因此涉及到对accept的争用;并发服务器4仍是只有父进程进行accept,然后通过文件描述符的传递,使得子进程获得连接描述符;

本文介绍了基于C++实现的单进程单客户编程模型的echo服务器,适用于长连接,不适合短连接和高并发场景。文章详细讲解了编程模型,包括迭代服务器、并发服务器1至4,强调了并发服务器1的特点和实现内容,特别是fork开销问题。服务器为每个连接创建一个进程,导致资源消耗较大,但保证了连接的独占性。文章还讨论了TCP全双工协议、阻塞式网络编程和进程间的配合问题,以及服务器如何处理客户端的连接和数据传输。
最低0.47元/天 解锁文章
2253

被折叠的 条评论
为什么被折叠?



