TCP是一个全双工(Full-Duplex)协议,因此标题里的"半"字就是相对于全双工的"全"来说的。
半开连接
从协议定义的角度来说,TCP的半开连接是指:TCP连接的一端异常崩溃,或者在未通知对端的情况下关闭连接,这种情况下不可以正常收发数据,否则会产生RST。比如一个常见的情况是TCP连接的一端异常断电,就会导致TCP的半开连接。如果没有数据传输,对端就不会知道本端的异常而一直处于ESTABLISHED状态(暂不考虑TCP存活检测机制)。
另外从linux实现的角度来说,因为linux内部有个半连接队列,TCP半开连接是指发送了TCP连接请求,等待对方应答的状态。此时连接并没有完全建立起来,双方还无法进行通信交互的状态,此时就称为半连接。由于一个完整的TCP连接需要经过三次握手才能完成,这里把三次握手之前的连接都称之为半连接。
通过单台笔记本的双无线网卡测试tcp连接的半开,步骤如下:
1. server绑定网卡A的地址(172.168.18.240)
2. client绑定网卡B的地址(172.168.52.224)并连接server,对应截图中的No1~No3包
3. client发送"hello"消息,对应截图中的No4包
4. server正常接收到后"hello"消息后,拔掉网卡A
5. kill掉server进程,使server的FIN消息不能发送到client
6. 插上网卡A,注意在路由器中绑定IP地址和MAC地址,使得网卡A的地址和之前是一致的,此时client和server即处于半开连接状态
7. client向server发送"world"消息,对应截图中的No6包
8. server回复rst消息
半关连接
TCP的半关连接是指:TCP连接只有一方发送了FIN,另一方没有发出FIN包,仍然可以在一个方向上正常发送数据。这种场景并不常见,一般来说Berkeley sockets API调用shutdown()接口时候就会进入半关闭状态(调用常规的close()一般是期待完整的双向关闭这个TCP连接),shutdown()接口相当指示程序,本端已经没有数据待发送,所以我发送一个FIN到对端,但是我仍然想要从对端接收数据,直到对端发送一个FIN指示关闭连接为止。如下图所示,在红色背景文本框标注的数据传输场景下就是TCP的半关连接。
测试过程:
1. 正常建立连接后,client首先发送"hello"消息给server,对应截图中的No1~No5包
2. 然后server发送FIN给client,关闭了server到client方向的数据传输,对应截图中的No6~No7包
3. 但是client仍然可以向server传输数据,此时client和server之间的连接即处于半关连接的状态,接下来client向server发送"world"消息,对应截图中的No8~No9包
4. 然后发送FIN给server关闭client到server方向的数据传输,对应截图中的No9~No10包。
扩展:半连接
发生在TCP3次握手中。如果客户端向服务端发起TCP请求,服务端也按照正常情况进行响应了,但是客户端不进行第3次握手,这就是半连接。半连接,会造成服务端分配的内存资源就一直这么耗着,直到资源耗尽(也就是常听说的SYN洪水攻击的原理)。
参考文章:
http://www.cnblogs.com/lshs/p/6038472.html