关于TCP与Socket对端口使用的一些理解

本文探讨了TCP与Socket在端口使用上的几个关键问题,包括服务器端口占用、TIME_WAIT状态的影响、服务器如何处理多连接以及端口冲突。在服务器崩溃与端口占用的关系上,指出TIME_WAIT状态可能导致端口不可用,但大多数服务器实现允许在同一端口建立新连接。服务器通过端口和应用程序绑定来区分不同连接,避免冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在复习计算机网络上的一些知识,产生了几点困惑。

  1. 首先是一道面试题,之前蚂蚁金服的面试官问到的:服务器突然崩溃了,然后重启服务器的时候80端口被占用了,什么原因,提示我与四次挥手有关。
  2. 之前看四次挥手的时候,有说到服务器上会有大量的time_wait浪费资源,就想到一个问题:服务器是上层是使用Http协议传输数据的。那么使用http协议传输数据的时候,谁先主动断开TCP连接呢?(只有主动断开的一方才会有timewait状态)
  3. 还有一个一直以来的疑惑这次终于搞明白了:服务器只监听了一个80端口,怎么处理这么多连接的,端口不会被占用吗?

带着这些问题查了一些资料,对这些问题也算是基本上搞明白了。
先说下socket。TCP/IP本身仅仅是一个协议,需要具体的实现。scoket就是对其实现之一。是支持TCP/IP协议的网络通信的一个基本操作单元。一个Socket连接是对一对TCP连接的抽象表示。socket由且只由一个五元组表示(客户端IP,客户端端口,服务器IP,服务器端口,使用的协议)。

首先第一个问题:其实现在我的观点还是四次挥手的时候服务器崩溃是不会导致端口被占用的。查到与四次挥手时端口占用相关的资料很少。但是在TCP/IP详解书上看到了一段描述,大致是说:当TCP连接处于TIME_WAIT状态时,通信双方将该连接(socket)定义为不可用的状态,也就是说只是不能建立一个完全相同的五元组socket。但是有一些系统在实现的时候加上了更严格的约束,一个端口号被一个处于TIME_WAIT的socket使用,那么这个端口号将不能再被使用,这也就意味着一个不能再使用这个端口建立socket了。
仔细想想,如果有一个连接处于TIME_WAIT状态整个服务器就不能在这个端口上进行任何的TCP连接了(服务器对端口的使用后面会提到),服务器显然是不可能使用这种方式的(在服务器上使用netstat -ant可以看到大量的处于TIME_WAIT的连接,而且使用的是统一端口号)。大多数服务器的实现方式是使用优先级高于默认的方式,对于处于TIME_WAIT用到的端口还是允许在其上建立连接的只是不能建立同一个scoket五元组。
这就比较困惑了,不是这个原因的话,处于挥手阶段的TCP连接崩溃,还有别的原因会导致端口号的占用吗?

然后是第二个问题:使用Apache或者Nginx做服务器的话,在服务器上看一下建立的TCP连接,会有很多的处于TIME_WAIT状态,但是只有主动断开TCP连接的一方才会处于TIME_WAIT状态,所以一定有不少HTTP连接是服务其主动断开连接的。但是在服务器上可能还会看到一些处于CLOSE_WAIT或者LAST_LACK状态的TCP连接。所以应该也有不少是客户端主动断开的。那么什么时候客户端主动什么时候服务器主动呢?主要是看两点,首先是看connection请求头与响应头,如果是keep-alive,先到达限定时间的一方会主动断开连接。只有一方是keep-alive,没有keep-alive选项的一方会主动断开连接。如果没有keep-alive头,那么要看服务器发送的响应头中有没有能够标识数据长度的信息(content-length或者transfer-encoding:chunked),有的话客户端知道什么时候数据传输完了就主动断开连接。没有的话服务器在数据传输完成之后主动断开连接表示没啥要传的了。

然后是最后一个问题:服务器到底是怎么使用端口的。首先要理解端口是一个虚拟的概念,并没有实际的硬件与其匹配。ip数据报根据主机的ip信息找到主机并发送到网卡上,但是并没有发送到某个端口,对于链路层来说也没有端口。端口是操作系统来实现的,目的是为了标识将到来的数据报转发给哪个应用程序。想象一下,加入没有端口的概念,链路层发来数据包到了操作系统那里,A应用需要传来的数据,OS把数据报发给它,B应用也需要,OS也转发给B。但是OS并不知道A要哪些B要哪些,就只能一股脑都发过去。这就造成了很大的浪费,而且不安全。所以OS需要知道哪些数据报发给哪些应用。于是就有了端口的概念,应用程序监听端口(就是与一个端口绑定),OS收到数据包后将数据报发送到与数据包中指定端口绑定的应用程序那里去。
但是还有一个问题,为什么一个端口上建立了那么多的TCP连接呢,它为什么没有端口冲突的问题,而我自己用一个新应用监听端口就会冲突?
(下面仅仅是我自己的理解,操作系统的底层未必这么实现但是思想应该没有问题)
首先是操作系统怎么建立TCP连接的。应用程序本身并没有直接进行TCP连接的握手操作,它拿到的是连接完成的SOCKET。一个SYN连接请求包到达操作系统之后,系统并不直接把它发送给监听相应端口的应用程序,而是回发SYN+ACK包,并将其加入操作系统维护的SYN对列。收到对之前消息的ACK之后,将这个连接从SYN队列中取出,并将其加入ESTABLISHED队列,这时候已经建立了一个socket(ip1,port1,ip2,port2,TCP)。所以ESTABLISHED队列中的都是建立好连接的SOCKET,如果应用程序还有能力处理新的连接,就将这个Socket转交给应用程序来处理。后面关于这个socket的数据处理都由应用程序来完成。,每有一个新的连接,新建一个socket即可(因为远端的ip和端口不同,建立的socket也是不同的),端口本身还仅仅是一个标识的作用,这样无论有多少个连接试图建立,就建立多少个Socket来处理就好了。而使用不同应用程序来监听相同的端口是会发生冲突的,来了标识给这个端口的数据包,发送给那个应用程序呢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值