- 咱们在须要开发一个服务器程序时,有较多的的程序设计范式可供选择,不一样范式有其自身的特色和实用范围,明了不一样范式的特性有助于咱们服务器程序的开发。
- 客户端程序一般比服务器容易些,固然客户端程序也可使用这些程序设计范式,由于它们蕴含的道理都是相通的。
- 本文所设计的服务器主要是指基于TCP的服务器
迭代TCP服务器 老是在彻底处理某个客户的请求以后才开始下一个客户的请求处理。
这样的服务器实际中比较少见。
基于UDP的大多服务器倒是这样实现。编程
传统并发服务器 调用fork派生一个子进程来处理每一个客户,这使得服务器可以同时为多个客户服务,每一个进程一个客户。
客户数目的惟一限制是操做系统对其可以同时拥有多少子进程的限制。
绝大多数TCP服务器程序都是按这个范式编写。
并发服务器的问题在于为每一个客户现场fork一个子进程比较耗费CPU时间。服务器
不一样于传统意义的并发服务器那样为每一个客户现场派生一个子进程,而是 在启动阶段预先派生必定数量的子进程,当有客户链接到达时,这些子进程就能当即为它提供服务。
这种技术的有点在于无需引入父进程执行fork的开销就能处理新到来的客户。缺点是父进程必须在服务启动阶段猜想须要预先派生多少子进程。若是某个时刻客户数刚好等于子进程总数,那么新到的客户将被忽略,直到至少有一个子进程完成处理从新可用。网络
在多个进程中引用同一个监听套接字的描述符上调用accept的作法在某些系统实现是不被支持的,那么解决办法是让应用进程在调用accept先后安装某种形式的锁(lock),这样任意时刻只有一个子进程阻塞在accept调用中,其余子进程则阻塞在获取保护accept的锁上。
这里使用文件锁来保护,文件锁涉及到文件系统的操做,可能比较耗时。多线程
相比于 预先派生子进程,使用文件上锁保护accept,使用线程锁保护accept,这种方法 不只适用于同一进程内各个线程间的锁保护,并且可以用于不一样进程之间的锁保护。
在不一样进程间的锁保护须要注意的是并发
- 互斥锁变量必须存放在由全部进程共享的内存区中
- 必须告知线程函数库这个锁是用于不一样进程间共享的互斥锁
只让父进程嗲用accept,而后把所接受的已经链接的套接字 传递 给某个子进程。
这样绕过了为全部子进程的accept调用提供上锁保护的需求,可是须要从父进程到子进程进行某种形式的描述符传递。
这种技术会上代码比较复杂,父进程必须跟踪子进程的闲忙状态,以便于给空闲的子进程传递新的套接字。app
相比于多进程模型,若是服务器主机提供支持线程,咱们能够改用线程以取代进程。线程相比于进程的优点这里再也不累述。函数
相比预先派生一个子进程池快于为每一个客户线程fork一个子进程池相似的道理,在有线程支持的系统上,预先建立的线程池取代为每一个客户现场建立一个线程的作法有相似的性能提高。
这种模式的基本设计是预先建立一个线程,并让每一个线程各自调用accept,取代让每一个线程都阻塞在accept调用中的作法,使用互斥锁保证任什么时候刻只有一个线程在调用accept。性能
程序启动阶段建立一个线程池后让主线程调用accept;
主线程把每一个客户链接传递给池中某个可用的线程,相似于进程版本的作法。
这样的设计问题在于主线程如何将一个已链接套接字传递给线程池中某个可用线程spa
咱们有不少实现手段,本可用如前面同样使用描述符传递,可是既然全部线程和全部描述符都在同一个进程中,那么也就没有必要把一个描述符从一个线程传递到另外一个线程。接收线程只须要知道这个已链接套接字描述符的值(传递描述符可不仅是传递这个值,事实上是须要传递这个套接字的引用,所以也将返回一个不一样于原值的描述符,该套接字的引用计数也会增长)操作系统