第60章 SOCKET: 服务器设计

迭代型和并发型服务器
  • 迭代型:服务器每次只处理一个客户端,适用于能够快速响应客户端请求的场景
  • 并发型:服务器同时可以处理多个客户端,适用于每个请求都需要大量处理时间

    echo服务器设计

    UDP:由于服务器一次只处理一条单独的消息,设计为迭代型就足够了
    TCP:客户端可能发生无限量的数据,适合设计为并发型

    并发型服务器的其他设计方案
  • 在服务器预先创建进程或线程:30章有详细描述,核心理念是服务器在启动阶段就立刻创建一定数量的子进程或线程构成服务池,服务池中每个子进程只处理一个客户端,处理完后,子进程并不终止,而是获取下一个待处理的客户端,应该在负载高峰期增加服务池的大小,负载下降后减小服务池的大小

  • 在单个进程中处理多个客户端:必须采用一种能允许单个进程同时监视多个文件描述符上的IO事件,如IO多路复用、信号驱动IO、epoll等,而且服务器需要做一些通常由内核处理的调度任务
  • 服务器集群:DNS轮转负载共享,或服务器负载均衡

    inetd(Internet超级服务器)守护进程

    被设计用来消除运行大量非常用服务器进程的需要,主要两个作用

  • 监视一组指定的套接字端口,并按照需要启动服务,可以降低系统上运行的进程数量

  • 简化了其他服务的编程工作

inetd在Linux中有扩展版本xinetd,通常在系统启动时运行,主要步骤:

  1. 配置文件/etc/inetd.conf中指定的每项服务,都会创建一个恰当的套接字(流式或数据报),然后绑定到指定的端口,每个TCP套接字都会通过listen允许客户端发来连接
  1. 通过select,inetd对上步创建的所有套接字进行监听
  1. select进入阻塞,直到一个UDP套接字上有数据可读或TCP套接字上收到了连接请求,TCP中还会执行accept
  1. 启动套接字上指定的服务,inet调用fork创建一个新的进程,通过exec启动服务,启动服务前,会:
  • 除了用于UDP数据报和接收TCP连接的文件描述符外,从父进程继承的文件描述符全部关闭
  • 在文件描述符0,1,2上复制套接字文件描述符,并关闭套接字文件描述符本身,然后启动的服务器进程就能通过这三个标准的文件描述符同套接字通信
  • 这一步可选,为启动的服务器进程设置用户和组ID
  1. 第三步中,如果在TCP套接字上接受了一个连接,inetd就关闭这个连接套接字
  1. inetd服务跳转回第二步继续执行

/etc/inetd.conf中的字段如下:

  • 服务名称(service name):可以通过/etc/services,结合字段protocol查看监视哪个端口号
  • 套接字类型(socket type):流式还是数据报
  • 协议(protocol):几乎所有的服务都会指定TCP或UDP
  • 标记(flags):如果启动的服务需要管理这个套接字,指定为wait,对于大部分UDP应该是wait
  • 登录名(login name):由/etc/passwd中的用户名组成
  • 服务器程序(server program):指定了被执行的服务器程序的路径名
  • 服务器程序参数(server program arguments):指定了一个或多个参数