Tcp Server Design

绝大多数的TCP服务都是支持并发的。当一个连接请求到达时,服务端接收这个连接,然后创建一个新的线程(或进程)来处理这个连接。

listen状态

在本地启动Go的服务,使用netstat查看:

netstat -an -f inet

可以看到listen状态的请求连接。其中Local Address*表示请求会被本地的任意地址处理(如果有多重地址的话)。Foreign Address*.*表示客户端的ipport都是未知的。

Active Internet connections (including servers)
Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp46      0      0  *.3900                 *.*                    LISTEN

当新的请求到达,并被接收时,服务器内核中会创建一个ESTABLISHED状态的连接。而listen继续去接收新的连接。

Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp4       0      0  127.0.0.1.3900         127.0.0.1.51133        ESTABLISHED
tcp46      0      0  *.3900                 *.*                    LISTEN

request queue

listening状态的应用正在忙于处理新的连接,同时有其他的请求进来时,服务器是如何处理的呢?引入另一个概念:请求队列。

  1. 每一个监听状态的终端都有一个固定长度的队列,用来存放TCP三次握手完成,但还没有被应用接收的连接。client会认为该连接已经创建成功,所以它此时发送的数据也会被缓存起来。如果queue中的连接长时间不被应用读取,便会导致client超时。
  2. 当队列满了后,TCP会直接忽略进来的SYN,而非回复RST报文头。这样做便是要client稍后重新发送SYN。因为服务器比较繁忙的状况,可能马上就会恢复。
  3. 如果TCP三次握手完成,连接也就被创建成功了。如果此时服务端不想为该ip提供服务,服务端要么发送FIN关闭这个连接,或者发送RST中断这个连接。整个过程中,TCP没有权限去限制client端。