TCP 和 UDP 可以使用同一个端口吗?
一、TCP 和 UDP 可以同时绑定相同的端口吗?


在数据链路层中,通过MAC地址来寻找局域网中的主机。在网络层中,通过IP地址来寻找网络中互联的主机或路由器。在传输层中,需要通过端口来寻址,来识别同一机器中同时通信的不同应用程序。
所以,传输层的端口号的作用,是为了区分同一主机上不同应用程序的数据包。
在传输层有两个传输协议分别是TCP和UDP,在内核中是两个完全独立的软件模块。
当主机收到数据包后,可以在IP包头的协议号字段知道该数据包是TCP/UDP,所以可以根据这个信息确定送给那个模块(TCP/UDP)处理,送到传输层中的TCP/UDP模块中的报文根据端口号确定送给哪个应用程序处理。
因此,TCP/UDP各自的端口号也相互独立。
二、多个TCP服务器进程可以绑定同一个端口号吗
如果两个TCP服务进程同时绑定的IP地址和端口号都相同,那么执行bind()时候就会出错,提示“Address already in use”。
注意,必须IP地址和端口号都相同。
如果进程绑定了0.0.0.0地址,这代表了任意地址,则此时只要有另一进程绑定了一致的端口号,则都会报错。
三、重启 TCP 服务进程时,为什么会有“Address in use”的报错信息?
在实践中,我们可能会经常碰到一个问题,当 TCP 服务进程重启之后,总是碰到“Address in use”的报错信息,TCP 服务进程不能很快地重启,而是要过一会才能重启成功。
这是为什么呢?
这是因为当我们重启TCP服务进程时,意味着通过服务器端发起了关闭连接操作,于是就会经过四次挥手,而对于主动关闭方,会在TIME_WAIT这个状态里停留一段时间,这个时间大约为2MSL。
在TIME_WAIT状态下的IP+PORT组合仍然被认为是一个有效的组合,此时相同机器就不能够在该IP+PORT组合上进行绑定。
对于此种情况,我们可以在调用bind前,对socket设置SO_REUSEADDR属性。
int on = 1; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
SO_REUSEADDR 作用是:如果当前启动进程绑定的 IP+PORT 与处于TIME_WAIT 状态的连接占用的 IP+PORT 存在冲突,但是新启动的进程使用了 SO_REUSEADDR 选项,那么该进程就可以绑定成功。
对于前面提到的问题:如果TCP服务进程A绑定了0.0.0.0:8080,而此时TCP服务进程去绑定192.168.1.100:8080会出错。
这个问题也可以使用SO_REUSEADDR拒绝,因为它的另外一个作用是:绑定的IP地址+端口时,只要IP地址不是正好(exactly)相同,那么允许绑定。
四、客户端的端口可以重复使用吗
客户端在执行 connect 函数的时候,会在内核里随机选择一个端口,然后向服务端发起 SYN 报文,然后与服务端进行三次握手。
所以客户端的端口选择发生在connect函数,内核在选择端口的时候,会从net.ipv4.ip_local_port_range这个内核参数指定的范围来选取一个端口作为客户端端口。
TCP连接是由四元组(源IP地址,源端口,目的IP地址,目的端口)唯一确认的,那么只要四元组中其中一个元素发生了变化,那么就表示不同的TCP连接的。所以如果客户端已使用端口a与服务端A建立了连接,那么客户端要与服务端B建立连接,还是可以使用端口a的,因为内核是通过一个四元组来定位一个TCP连接的,并不会因为客户端的端口号相同,而导致连接冲突的问题。
但是,如果多个客户端bind同一个IP地址+PORT组合,还是会出错的。一般而言,客户端也不建议使用bind函数,因为客户端的端口通常没什么意义。
五、客户端连接TIME_WAIT状态过多,会导致端口资源耗尽而无法建立新的连接吗
针对这个问题要看,客户端是否都是与同一个服务器(IP地址+PORT一样)建立连接。
如果客户端都是与同一服务器(IP地址+PORT一样)建立连接,那么TIME_WAIT状态过多,当端口资源被耗尽,就无法与这个服务器再建立连接了。
但是,只要客户端连接的服务器不同,端口资源是可以重复使用的(四元组)。
六、解决客户端TCP连接TIME_WAIT过多,导致无法与同一个服务器建立连接的问题
针对这个问题,解决方法是打开net.ipv4.tcp_tw_reuse这个内核参数。
因为这个开启这个内核参数后,客户端调用connect函数时,如果选择到的端口,已经被相同四元组的连接占用时,会判断该连接是否处于TIME_WAIT状态且TIME_WAIT状态持续时间超过1秒,如果是,就会重用这个连接,然后就可以正常使用该端口了。
-----------------------------------------------------------------
------------------------------------------------------------------
知识来源:小林coding
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战