TCP状态以及相关问题分析 -由面试问题想到的
以前做项目的时候,没有深入的考虑过这些问题,只是和同学在讨论问题的时候稍微摄入过TCP状态的一些问题。最近找工作国内两个比较大的互联网公司都问了相关问题。突然间感觉到TCP在网络编程中还是很重要,包括他的原理以及具体函数的意义。所以今天就整理了下TCP三次握手,以及相关的问题。

连接建立:
我们从上面的图就可以看到,黑色加粗线是客户端的正常变迁过程。其中就是主动打开发动SYN以后,接受SYN,ACK后进入连接状态。
还有看黑色虚线是服务器的正常变迁过程。其中listen被动打开,指定一个接受队列,等收到SYN后发送ACK,SYN,然后收到ACK以后进入连接状态,通过accept函数返回客户端信息以及连接句柄。
断开连接:
由主动发起方发起FIN(注:通常但不是总是客户端发起断开连接),被动关闭方收到FIN发ACK,主动发起方收到ACK进入fin_wait2状态,如果收到FIN以后再次发送ACK就进入了TIME_WAIT状态,TIME_WAIT状态比较特殊,要等一个2MSL时间,就是一个二倍的网络延迟。
被动发起方发送FIN以后进入LAST_ACK,收到ACK后退出连接。
这里有个问题。主动发起方为什么要有个2MSL呢?如果没有会怎么样呢?我们可以看到,2MSL这个时候,连接基本上算关闭了,因此可以停止通信了,LAST_ACK状态的如果收不到ACK怎么办?一直等待?他应该是重发FIN,这个时候如果TIME_WAIT不存在,被动发起方就会收到一个RST,影响稳定性。如果2MSL存在,就会有一个问题。如果有人利用这个问题造成服务器出现大量的TIME_WAIT状态,因此这个是一个稳定性和效率的均衡。
2.另外再次讨论一下listen以后服务器协议栈做了那些工作:
Listen(int sockfd,int backlog)//0-成功, -1失败
由上面的示意图,可以看出,当Listen以后就可以接受对方的连接。当时面试的时候就遇到了这个问题,hr问我,Listen之后能接受客户端连接吗?如果能的话,是什么情况?连接以后客户端发送一系列信息会出现什么情况?呵呵,从上面的原理,我们应该看到比较清楚了,可以接受连接,并且理论上可以接受buffer大小的数据。
3.相关问题讨论,这些问题是我在面试以及在学习中,自己思考或者和同学讨论的结果。
A.当连接成功建立后,客户端拔去网线,客户端电脑重启后插入网线,启动客户端进程,会出现什么情况?
分析:首先如果TCP打开了keeplive选项,那么客户端关机时间久了以后,服务器就能感知到,如果keeplive关闭,或者客户端快速重启,那么这个时候,服务器没有发现异常。客户端重启过后是一个全新状态,由TCP状态转换图可知,客户端会发起SYN,而服务器收到客户端发来的SYN的话,发现在ESTABLISHED状态收到SYN是异常的,于是,返回RST,自己也复位改连接。客户端收到RST后,复位,重新连接。
B. 当连接成功建立后,如果send函数发送数据立即返回后,拔掉客户端网线,请问数据会在那些地方存留?
分析:send是glib函数通过系统调用传入内核协议栈的buffer中,然后队列等待发送。如果当前没有数据等待的话,该数据部分可能已经发送到网络上,但是也有可能正在buffer队列中等待发送。
C.当连接成功建立后,如果send函数发送数据立即返回后,且保证数据已经正常发送到网络上,这个时候拔掉服务器网线,会出现什么情况?
分析:首先服务器如果没有发送数据,只是等待接受的话,那么服务器可能等到了数据,也有可能永远等不到数据。如果网络状态非常好,在拔去网线之前已经收到了数据,或者网络不太好,数据还没有到达服务器。那么数据还是会正常传输,至少第一个数据包会正常传输,传到和服务器直接相连的交换机这里。交换机发现ip不通,通过arp广播寻求服务器mac,失败后,返回ICMP数据包:目的地址不可达。客户端收到数据包后感知服务器不可达。
D.在处于TIME_WAIT状态下,2MSL前,对方再次简历连接,请问连接可以简历成功吗?
分析:这个问题我就不打字了,网上分析的不错:书中给的图里面,有一个TIME_WAIT等待状态,这个状态又叫做2MSL状态,说的是在TIME_WAIT2发送了最后一个ACK数据报以后,要进入 TIME_WAIT状态,这个状态是防止最后一次握手的数据报没有传送到对方那里而准备的(注意这不是四次握手,这是第四次握手的保险状态)。这个状态在很大程度上保证了双方都可以正常结束,但是,问题也来了。由于插口的2MSL状态(插口是IP和端口对的意思,socket),使得应用程序在2MSL时间内是无法再次使用同一个插口的,对于客户程序还好一些,但是对于服务程序,例如httpd,它总是要使用同一个端口来进行服务,而在2MSL时间内,启动httpd就会出现错误(插口被使用)。为了避免这个错误,服务器给出了一个平静时间的概念,这是说在2MSL时间内,虽然可以重新启动服务器,但是这个服务器还是要平静的等待2MSL时间的过去才能进行下一次连接。

谢谢分享
这个好复杂啊,专业人士才能看懂吧
不懂,得好好学学网络编程了
呵呵,多看下网络编程和TCP就好了
你好!你在文中写到“客户端会发起SYN,而服务器收到客户端发来的SYN的话,发现在ESTABLISHED状态收到SYN是异常的,于是,返回RST,自己也复位改连接。客户端收到RST后,复位,重新连接”,这个情况是不是只会发生在客户端创建新TCP连接的时候系统分配的socket端口号正好和上次的一样的情况下才会发生?如果socket号不一样,服务器只会认为是多TCP连接吧?
看不懂,好复杂的说,还是谢谢诶
要好好学编程了啊我