我的公司为特殊目的发布了一个特殊的TCP堆栈,我的任务是实现符合RFC793的关闭序列。其中一个单元测试有一个服务器在与常规Linux TCP客户端通信的特殊TCP堆栈上工作,并且我遇到了一些奇怪的行为,我不确定这是由我的编程错误引起还是由可以预料到。
在我的工作开始之前,我们曾经在用户应用程序调用close()时发送RST数据包。我已经实现了FIN握手,但是我注意到,在同时进行TCP终止(两端为FIN_WAIT_1-> CLOSING-> TIME_WAIT,请参见图片)的情况下,标准Linux TCP客户端无法连接到相同的目标地址和端口再次,connect()返回EADDRNOTAVAIL,直到TIME_WAIT传递到CLOSED。
现在,标准的Linux客户端应用程序设置选项 SO_REUSEADDR ,每次将套接字绑定到端口8888,并连接到目标端口6666。我的问题是,为什么bind()成功,为什么? connect()是否失败?我本以为SO_REUSEADDR可以接管本地的TIME_WAIT端口,但是这样做了,但是connect()有什么功能来阻止再次与destination-ip:6666对话?
我的代码是在做不应该做的事情还是这种预期的行为?
我可以确认失败的connect()完全没有SYN数据包使之脱离客户端计算机。我已经附上了以上会话的FIN握手的屏幕截图。
答案 0 :(得分:1)
您先前的实现使用RST
来结束连接。收到RST
数据包会立即从活动连接表中删除该连接。那是因为不再有可能在该连接上接收到有效的数据包:对等方刚刚告诉您的系统,该会话无效。
另一方面,如果您用FIN
进行了正确的会话终止,则存在最后一个数据包问题:如何确定对等方是否实际上收到了您发送给其的最后一个确认 FIN
(这是TCP的TIME_WAIT
状态的定义)?并且,如果对等方没有收到,他们可能会有效地发送FIN
数据包的另一份副本,然后您的计算机应重新发送ACK
。
现在,您的bind
成功,因为您正在使用SO_REUSEADDR
,但是您仍然无法使用两端完全相同的端口创建新连接,因为该条目是仍处于活动连接表中(处于TIME_WAIT
状态)。四元组(IP1,端口1,IP2,端口2)必须始终唯一。
正如@EJP在评论中建议的那样,客户端指定端口是不寻常的,并且通常没有理由。我会改变你的测试。