探索服务器内部——服务器概览

1、服务器程序的结构:

服务器和客户端在网络相关的部分,如网卡、协议栈、Socket库等功能都是差不多的。无论硬件和OS如何变化,TCP和IP的功能都是一样的,或者说这些功能规格都是统一的。但是服务器需要同时和多个客户端通信,所以服务器必须把握每一个客户端的操作状态。因此一般每有一个客户端连接进来,就启动一个新的服务器程序,确保服务器程序和客户端是一对一的状态(如下图所示)。

当然上面这种方法比较耗时,响应时间也会相应增加。因此一般都是事先启动几个客户端通信模块,当客户端发送连接时,从空闲的模块中挑选一个出来将套接字移交给它处理。

2、服务器端的套接字和端口号:

现在大多数应用都是由客户端去访问服务器,但其实应用的形态不止这一种。所以在数据收发层面不需要区分客户端和服务器,而是能够以左右对称的方式自由收发数据。

不过其中还存在一个无法做到左右对称的部分,那就是连接操作。连接操作需要在有一方等待连接的情况下,另一方才能发起连接,如果双方同时发起连接是不行的,因为在对方没有等待连接的状态下,无法单方面进行连接。因此,只有这个部分必须区分发起连接和等待连接这两个不同的角色。从数据收发的角度来看,这就是客户端和服务器的区别,也就是说,发起连接的一方是客户端,等待连接的一方是服务器。

在调用Socket库上,客户端的数据收发需要经过下面4个阶段:

(1)创建套接字(创建套接字阶段)

(2)用管道连接服务器端的套接字(连接阶段)

(3)收发数据(收发阶段)

(4)断开管道并删除套接字(断开阶段)

相对地,服务器的数据收发如下:

(1)创建套接字(创建套接字阶段)

(2-1)将套接字设置为等待连接状态(等待连接阶段)

(2-2)接受连接(接受连接阶段)

(3)收发数据(收发阶段)

(4)断开管道并删除套接字(断开阶段)

以伪代码的形式如下图所示:

值得注意的是上图中调用accept接受连接的操作,由于等待连接的模块在服务器程序启动时就已经在运行了,所以在刚启动时,应该还没有客户端的连接包到达。包都还没来就调用accept接受连接,可能会有点奇怪,不过没关系,因为如果包没有到达,就会转为等待包到达的状态,并在包到达的时候继续执行接受连接操作。因此,在执行accept的时候,一般来说服务端都是处于等待包到达的状态,这时应用程序会暂停运行。在这种状态下,一旦客户端的包到达,就会返回响应包并开始接受连接操作。接下来,协议栈会给等待连接的套接字复制一个副本,然后将连接对象等控制信息写入新的套接字中,此时就创建了一个新的套接字,并和客户端套接字连接在一起了。

在创建新套接字时端口号也是一个关键点,新创建的套接字副本必须和原来等待连接的套接字具有相同的端口号。那么协议栈在确定某个套接字时,不仅使用服务器端套接字对应的IP地址和端口号,还同时使用客户端的端口号再加上IP地址,总共使用4种信息进行判断。

Guess you like

Origin blog.csdn.net/qq_38386085/article/details/104442212