什么是网络套接字(Socket)?和端口有什么区别

套接字和端口经常被混为一谈,这里按步骤从基础开始理清两者的区别。

  1. 什么是套接字

    套接字是由IP地址、端口号和协议(TCP或UDP)三者组合而成的一个通信端点。打个电话的比方:IP地址相当于一栋楼的地址,端口号相当于楼里的分机号,而套接字则相当于实际接通的那一条通话线路。

  2. 端口和套接字的区别

    端口号只是一个数字(0~65535),用来指明要连接哪个服务;而套接字则更具体,是把这个端口号和实际的IP地址、协议组合起来形成的一次具体连接。同样是80端口,不同客户端IP连接进来会形成不同的套接字,这正是一台服务器能同时服务多个用户的原因。

  3. 用套接字对来区分连接

    一条TCP连接由"本机IP:本机端口"和"对端IP:对端端口"组成的套接字对唯一标识。正因如此,服务器即使在同一个80端口上同时接受成千上万个连接,也能准确分辨每一份数据到底属于哪个客户端。

  4. TCP套接字和UDP套接字的区别

    TCP套接字会像打电话一样先建立连接(三次握手),然后保证数据按顺序、完整地送达;UDP套接字则不预先建立连接,像寄明信片一样随时发送数据。像访问网页这种对准确性要求高的场景通常用TCP,而像在线游戏、视频通话这种对速度要求更高的场景则常用基于UDP的套接字。

  5. 知名端口与临时端口

    服务器使用的80端口(HTTP)、443端口(HTTPS)等属于约定俗成、人人皆知的"知名端口"。相反,你自己的设备(客户端)在连接服务器时使用的端口,是操作系统每次自动分配的临时端口(通常在49152~65535范围内),用户完全不需要操心。

  6. 套接字地址的写法

    套接字地址通常写成"IP地址:端口号"的形式。例如192.168.0.10:8080就表示192.168.0.10这台设备上的8080端口,这种写法在服务器配置文件和开发者工具中很常见。

  7. 监听套接字与已建立套接字

    服务器程序在某个端口等待新连接接入的状态称为监听套接字,而已经和客户端实际建立连接、正在收发数据的状态称为已建立(Established)套接字。用netstat之类的网络诊断命令,可以直接查看当前哪些套接字处于监听状态、哪些已经建立连接。

  8. 防火墙控制的本质也是套接字

    防火墙规则里所说的"封锁某个端口",实际上是阻止在这个端口上建立新的套接字连接。理解了套接字的概念后,端口转发、防火墙规则、代理配置这些看似不同的网络概念,其实都建立在同一个原理之上。

  9. 端口号的三个范围

    IANA把端口号分为知名端口(0~1023)、注册端口(1024~49151)和动态/私有端口(49152~65535)。知名端口分配给基础服务,如SSH的22、邮件传输的25、DNS的53、网页的80和443。客户端使用的临时端口由操作系统从动态范围里选取,Linux默认使用的是32768~60999,而不是IANA的范围。

  10. TCP服务端套接字的一生

    典型的服务器先创建套接字,绑定地址和端口(bind),调用listen开始接受连接,然后每来一个客户端就调用accept,它会返回一个专门用于这次会话的新套接字,而原来的监听套接字继续等待。通信结束后双方各自关闭套接字,连接经过关闭状态后消失。

  11. 客户端的步骤

    客户端创建套接字,必要时绑定,然后用服务器的地址和端口调用connect。对TCP来说,这会触发三次握手。如果没有指定源端口,由操作系统选一个。连接建立后,双方可以收发数据,直到一方关闭。使用UDP时,客户端可以省略connect,直接向某个地址发送数据报,没有握手。

  12. TIME_WAIT:关闭后的套接字为何还会停留

    TCP连接关闭后,先主动关闭的一方会让套接字停留在TIME_WAIT状态一段时间,以免旧连接中姗姗来迟的数据包被误认为属于新连接。繁忙的系统里这样的套接字可能积累很多。这是一种安全机制,随意缩短等待时间可能引起难以排查的错误。

  13. 同一台机器上的本地套接字

    并不是所有套接字都使用IP地址。Unix域套接字用文件系统路径而非网络地址连接同一台计算机上的程序,省去了网络协议栈的不少处理,所以数据库和Web服务器常用它做本地通信。Windows也有类似机制。编程接口很相似,所以同样的思路可以直接迁移。

  14. 套接字API的由来

    套接字编程接口于1980年代初随Berkeley系的Unix引入,后来几乎被所有操作系统采用。因此socket、bind、listen、accept、connect、send、recv这些函数名在C、Python和许多其他语言里几乎一样,Windows则采用了兼容的版本Winsock。

名字相似但完全不同的WebSocket

尽管名字里也带"Socket",WebSocket并不是本页讲的操作系统层面的套接字,而是专门为浏览器和服务器之间保持持续双向通信而设计的一种独立的应用层协议,常用于聊天应用和实时更新功能。WebSocket连接底层实际上仍然是建立在普通的TCP套接字之上的。

套接字、防火墙和端口转发其实是同一件事的不同侧面

防火墙规则封锁某个端口,本质上是阻止在该端口上建立新的套接字;而端口转发的原理,则是把路由器收到的套接字连接转发给内网中的特定设备。一旦理解了套接字这个概念,这些原本看似要分别记忆的规则,其实都是同一套原理的不同表现。

如何读懂netstat或ss的输出

在Windows上运行netstat -an,或在Linux上运行ss -tan,会看到一份套接字列表,包含本地地址、远端地址和状态。状态为LISTEN的行是监听套接字,显示服务器在哪个地址上等待;0.0.0.0或[::]表示所有网卡,127.0.0.1则表示只有本机能访问。ESTABLISHED的行是正在进行的会话,每一条都是本地与远端端点的唯一组合。TIME_WAIT和CLOSE_WAIT表示连接正在结束。如果某个程序持有大量CLOSE_WAIT套接字,往往说明它没有正确关闭连接,这是服务端代码里常见的缺陷。掌握这些状态,就更容易判断服务到底有没有在监听,这也是连不上时首先要回答的问题。

使用套接字时的常见错误

初学者常把连接失败和端口关闭混为一谈:连接被拒绝说明有机器作出了应答但那里没有程序在监听,而超时通常意味着防火墙丢弃了数据包,或主机不可达。另一个陷阱是把服务器绑定到127.0.0.1,然后纳闷为什么别的机器连不上。忘记TCP是字节流而不是消息流,会导致一次发送被分成两次读取,或两次发送被合并成一次到达的缺陷。最后,端口已被占用的错误通常是另一个程序,或上一次运行遗留的TIME_WAIT占着这个地址。

常见问题

套接字的端口号需要自己指定吗?

不需要。服务器一侧使用的知名端口(如80、443)由程序或管理员设定,但客户端连接时使用的临时端口是操作系统自动分配的,用户不需要自己配置。

服务器会出现套接字不够用的情况吗?

会的。在极短时间内建立和断开大量连接的服务器上,可能会出现临时端口暂时不够用的"套接字耗尽"现象。这种情况通常可以通过调整服务器端的连接复用(Keep-Alive)设置来缓解。

一台设备有多少个端口?

TCP和UDP各有65536个端口号(0~65535)。0号端口是保留的,不用于正常通信。设备并不会用到全部端口,大多数端口在任意时刻都是空闲的。

两个程序能监听同一个端口吗?

在同一地址和协议上通常不行,后启动的程序会收到地址已被使用的错误。也有让多个进程在同一端口上接受连接来分担负载的选项,但必须显式开启。TCP和UDP的端口空间是分开的,所以一个程序可以用TCP 53,另一个用UDP 53。

套接字就是连接吗?

很接近,但不完全相同。套接字是程序持有的一个端点,连接则是两个端点的配对。监听套接字在客户端到来之前没有任何连接。