1. TCP 和 UDP 可以同时绑定相同的端口吗?
可以的。
TCP 和 UDP 传输协议,在内核中是由两个完全独立的软件模块实现的。
当主机收到数据包后,可以在 IP 包头的「协议号」字段知道该数据包是 TCP/UDP,所以可以根据这个信息确定送给哪个模块(TCP/UDP)处理,送给 TCP/UDP 模块的报文根据「端口号」确定送给哪个应用程序处理。
TCP UCP 协议栈各自维护着一张【端口-进程】映射表,收到请求后根据 IP 报文中的协议号区分不同的传输层协议(TCP 是 6,UDP 是17),这两张表是独立的,所以不会有冲突。
因此, TCP/UDP 各自的端口号也相互独立,互不影响。
2. 多个 TCP 服务进程可以同时绑定同一个端口吗?
如果两个 TCP 服务进程同时绑定的 IP 地址和端口都相同,那么执行 bind() 时候就会出错,错误是“Address already in use”。
如果两个 TCP 服务进程绑定的端口都相同,而 IP 地址不同,那么执行 bind() 不会出错。
3. 如何解决服务端重启时,报错“Address already in use”的问题?
当我们重启 TCP 服务进程的时候,意味着通过服务器端发起了关闭连接操作,于是就会经过四次挥手,而对于主动关闭方,会在 TIME_WAIT 这个状态里停留一段时间,这个时间大约为 2MSL。
当 TCP 服务进程重启时,服务端会出现 TIME_WAIT 状态的连接,TIME_WAIT 状态的连接使用的 IP+PORT 仍然被认为是一个有效的 IP+PORT 组合,相同机器上不能够在该 IP+PORT 组合上进行绑定,那么执行 bind() 函数的时候,就会返回了 Address already in use 的错误。
要解决这个问题,我们可以对 socket 设置 SO_REUSEADDR 属性。
这样即使存在一个和绑定 IP+PORT 一样的 TIME_WAIT 状态的连接,依然可以正常绑定成功,因此可以正常重启成功。
4. 客户端的端口可以重复使用吗?
在客户端执行 connect 函数的时候,只要客户端连接的服务器不是同一个,内核允许端口重复使用。
TCP 连接是由四元组(源IP地址,源端口,目的IP地址,目的端口)唯一确认的,那么只要四元组中其中一个元素发生了变化,那么就表示不同的 TCP 连接的。
所以,如果客户端已使用端口 64992 与服务端 A 建立了连接,那么客户端要与服务端 B 建立连接,还是可以使用端口 64992 的,因为内核是通过四元祖信息来定位一个 TCP 连接的,并不会因为客户端的端口号相同,而导致连接冲突的问题。
5. 客户端 TCP 连接 TIME_WAIT 状态过多,会导致端口资源耗尽而无法建立新的连接吗?
要看客户端是否都是与同一个服务器(目标地址和目标端口一样)建立连接。
如果客户端都是与同一个服务器(目标地址和目标端口一样)建立连接,那么如果客户端 TIME_WAIT 状态的连接过多,当端口资源被耗尽,就无法与这个服务器再建立连接了。即使在这种状态下,还是可以与其他服务器建立连接的,只要客户端连接的服务器不是同一个,那么端口是重复使用的。
6. 如何解决客户端 TCP 连接 TIME_WAIT 过多,导致无法与同一个服务器建立连接的问题?
打开 net.ipv4.tcp_tw_reuse 这个内核参数。
因为开启了这个内核参数后,客户端调用 connect 函数时,如果选择到的端口,已经被相同四元组的连接占用的时候,就会判断该连接是否处于 TIME_WAIT 状态。
如果该连接处于 TIME_WAIT 状态并且 TIME_WAIT 状态持续的时间超过了 1 秒,那么就会重用这个连接,然后就可以正常使用该端口了。
问题:重用连接时需要重新三次握手建立连接吗?