网络与 IP

TCP 端口号和 UDP 端口号的区别:同一个端口能否同时使用

TCP 端口号和 UDP 端口号是两套互不相干的编号空间,各有 0–65535。系统先按 IP 首部的协议号分出 TCP 和 UDP,再到各自的端口表里找程序,所以 TCP 53 和 UDP 53 可以同时被占用、互不冲突。也正因为是两套编号,放行端口时必须写明协议,只开了 TCP 53 的 DNS 服务器,绝大多数查询照样进不来。

作者 顶联产品团队发布于 6 分钟阅读

TCP 端口号和 UDP 端口号的区别,一句话说完:它们是两套互相独立的编号,各自从 0 到 65535。一个包到达服务器,内核先看 IP 首部里的协议号,6 交给 TCP,17 交给 UDP,然后才在对应协议的端口表里查找是哪个程序在等这个包。所以 TCP 的 53 号和 UDP 的 53 号是两个不同的端口,可以同时被两个程序占用,也可以由同一个程序分别占用。DNS 就是这样用的:UDP 53 处理普通查询,TCP 53 处理大应答和区域传送。反过来,这也意味着防火墙、安全组放行时必须写明协议,查监听端口时要用 ss -tulnp 把两类都列出来。

TCP 端口和 UDP 端口为什么不冲突

端口号写在传输层首部里,而决定用哪个传输层协议解析的,是更外层 IP 首部里的协议号。内核收包的顺序是:

步骤 看哪个字段 例子
1 IP 首部的目的地址:是不是发给本机的 203.0.113.53
2 IP 首部的协议号(8 位):交给哪个协议模块 6 是 TCP,17 是 UDP,132 是 SCTP
3 传输层首部的目的端口(16 位):在该协议的端口表里查找 53
4 找到对应的套接字,把数据交给持有它的程序 named、unbound 等 DNS 服务

第 2 步已经把 TCP 和 UDP 分开了,第 3 步只在各自的表里查,两张表互不相干。可以把它理解为两栋楼里的门牌号:A 栋 53 号和 B 栋 53 号是两户人家。由此可以推出几条常被问到的结论:

  • TCP 和 UDP 端口可以相同。同一台机器上 TCP 53 和 UDP 53 同时监听,不会报冲突。
  • “端口被占用”只发生在同一个协议内。TCP 8080 被占了,UDP 8080 照样能用;Address already in use 报的都是同一协议内的冲突。
  • 两套编号的数量互不影响。TCP 用掉多少端口,与 UDP 还剩多少无关,各自都是 65536 个。
  • 测试方法不能混用。telnet、nc -z 测的是 TCP 握手,测通了只能说明 TCP 端口通,不能说明同号的 UDP 端口也通。

动手验证:同一个端口号同时监听 TCP 和 UDP

在一台 Linux 上开三个终端,用 nc 分别在 TCP 和 UDP 的 9000 端口上监听(以 Debian、Ubuntu 的 netcat-openbsd 为例;RHEL 系装的是 ncat,把 nc 换成 ncat,参数相同):

# 终端 1:TCP 9000
nc -lk 9000
# 终端 2:UDP 9000
nc -lu 9000
# 终端 3:查看 9000 端口上的监听
ss -tulnp 'sport = :9000'

终端 3 会看到两行,协议列不同,端口相同(示例输出):

Netid State  Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp   UNCONN 0      0            0.0.0.0:9000      0.0.0.0:*     users:(("nc",pid=4122,fd=3))
tcp   LISTEN 0      1            0.0.0.0:9000      0.0.0.0:*     users:(("nc",pid=4107,fd=3))

这时让第二个程序再去绑定 TCP 9000,就会失败。用 Python 试最直观:python3 -c 'import socket; socket.socket().bind(("0.0.0.0", 9000))' 会报 OSError: [Errno 98] Address already in use,而 UDP 9000 上的 nc 丝毫不受影响。没装 nc 时,同样用 Python 一行就能验证“一个程序同时占用两个同号端口”:

python3 -c 'import socket,time; t=socket.socket(); t.bind(("0.0.0.0",9000)); t.listen(); u=socket.socket(type=socket.SOCK_DGRAM); u.bind(("0.0.0.0",9000)); print("TCP 和 UDP 的 9000 都已绑定"); time.sleep(60)'

53 端口是 TCP 还是 UDP:DNS 两个都用

DNS 是“同一个端口号、两种协议都要用”的典型,各自分工明确:

场景 协议 说明
普通查询,应答较小 UDP 53 一问一答,不用握手,绝大多数查询走这里
应答超过 UDP 能承载的大小 先 UDP,再改 TCP 53 服务器回一个置上 TC(截断)标志的应答,客户端看到后用 TCP 重新查询
区域传送(AXFR;IXFR 通常也走 TCP) TCP 53 主从 DNS 服务器之间同步整个区的记录
DNS over TLS TCP 853 加密 DNS,端口不同
DNS over QUIC UDP 853 同样是 853,换成 UDP

UDP 能承载多大的应答,经历过几次变化:最早的 RFC 1035 把 UDP 上的 DNS 报文限制在 512 字节;EDNS(0)(RFC 6891)允许客户端在查询里声明自己能接收更大的 UDP 报文;2020 年的 DNS Flag Day 建议把这个值设为 1232 字节,以避开 IP 分片。超过声明大小的应答,就要截断后改走 TCP。RFC 7766 进一步要求所有通用的 DNS 实现都必须同时支持 UDP 和 TCP,TCP 不再是可有可无的备用通道。

用 dig 可以亲眼看到切换过程。关闭 EDNS、要求 dig 遇到截断不要自动重试,应答一旦超过 512 字节,返回的头部标志里就会出现 tc(把 example.com 换成一个 TXT 记录较多、应答超过 512 字节的域名):

# 只用 UDP、不带 EDNS,并忽略截断:大应答会显示 flags: qr tc ...
dig @203.0.113.53 example.com TXT +noedns +ignore
# 去掉 +ignore,dig 会提示 ;; Truncated, retrying in TCP mode. 然后改用 TCP 重查
dig @203.0.113.53 example.com TXT +noedns
# 直接用 TCP 查询,验证 TCP 53 是否放行
dig @203.0.113.53 example.com +tcp

这套机制对放行规则的要求很直接。自己运行权威 DNS 的服务器,入方向要同时放行 UDP 53 和 TCP 53:区里签了 DNSSEC、某个名字下挂着很多 A 记录或很长的 TXT 记录时,应答很容易超过 1232 字节,只开 UDP 的话,这部分查询会在截断之后失败,而且只有特定域名、特定解析器出问题,很难一眼看出原因。主从同步也走 TCP:从服务器主动连接主服务器的 TCP 53 拉取区数据,主服务器只放行 UDP 的话,从服务器永远同步不到。

同一个号码,在 TCP 和 UDP 上不一定是同一个服务

IANA 给大多数知名服务分配端口时,会把 TCP 和 UDP 的同号一起登记给它,哪怕实际只用其中一种;但也有少数号码,TCP 和 UDP 登记的是完全不同的服务。下表按“同号端口在两种协议上分别是什么”整理,放行时据此决定开哪一边:

端口 TCP 上 UDP 上 放行建议
22 SSH IANA 也登记给 SSH,实际不用 只放 TCP
53 DNS 大应答、区域传送 DNS 普通查询 权威 DNS 两边都放
80 HTTP 登记了,实际没有服务使用 只放 TCP
88 Kerberos,报文较大时 Kerberos 域控两边都放
123 登记了,实际不用 NTP 只放 UDP
389 LDAP CLDAP,Active Directory 定位域控时使用 域控两边都放
443 HTTPS(HTTP/1.1、HTTP/2) HTTP/3(QUIC) 启用了 HTTP/3 才放 UDP
512 exec(rexec) biff(comsat) 两个不同的老服务,都不应对外开放
513 login(rlogin) who(rwhod) 同上
514 shell(rsh) syslog 收日志一般只放 UDP;rsyslog 走 TCP 时习惯上也用 514
520 efs RIP 路由协议 按实际用途放行
3389 RDP 会话 RDP 8.0 起的 UDP 传输 对外开 RDP 时两边都放,网络较差时更流畅
5060 SIP SIP 两边都放;SIP over TLS 用 TCP 5061

512–514、520 这几个号码说明了一个事实:只说“开 514 端口”,可能被理解成开 rsh,也可能是收 syslog,工单里必须写清协议。本机的服务名数据库里能直接查到这种差别:

getent services 514/tcp     # 输出 shell  514/tcp  cmd
getent services 514/udp     # 输出 syslog  514/udp

防火墙、安全组放行:协议必须写清楚

几乎所有防火墙规则都把“协议 + 端口”作为一个整体来匹配。以一台权威 DNS 服务器 203.0.113.53 为例,各种工具的写法如下。

iptables 和 nftables:

# iptables:--dport 必须跟在 -p tcp 或 -p udp 后面,不写协议会报 unknown option "--dport"
iptables -A INPUT -p udp --dport 53 -j ACCEPT
iptables -A INPUT -p tcp --dport 53 -j ACCEPT
# nftables:一条规则同时匹配两种协议
nft add rule inet filter input meta l4proto { tcp, udp } th dport 53 accept

firewalld 和 ufw:

# firewalld:两个端口分别写协议;预定义的 dns 服务已经包含 53/tcp 和 53/udp
firewall-cmd --permanent --add-port=53/tcp --add-port=53/udp && firewall-cmd --reload
firewall-cmd --permanent --add-service=dns && firewall-cmd --reload
# ufw:写协议只放一种,不写协议则 TCP、UDP 一起放行
ufw allow 53/udp
ufw allow 53

ufw 的“不写协议就两种都放”很方便,也容易放多了:ufw allow 3306 会顺带放行 UDP 3306,虽然 MySQL 并不监听它,但规则比实际需要宽。能写协议的时候就写上。

Windows 防火墙每条规则只能选一种协议,TCP、UDP 各建一条:

New-NetFirewallRule -DisplayName "DNS UDP 53" -Direction Inbound -Protocol UDP -LocalPort 53 -Action Allow
New-NetFirewallRule -DisplayName "DNS TCP 53" -Direction Inbound -Protocol TCP -LocalPort 53 -Action Allow

托管机房没有安全组,靠边界交换机或路由器的 ACL 放行时,同样是两条规则。华为和思科的写法:

# 华为高级 ACL
acl number 3001
 rule 5 permit udp destination 203.0.113.53 0 destination-port eq 53
 rule 10 permit tcp destination 203.0.113.53 0 destination-port eq 53
# 思科扩展 ACL
ip access-list extended DNS-IN
 permit udp any host 203.0.113.53 eq 53
 permit tcp any host 203.0.113.53 eq 53

云平台的安全组也是同样的逻辑:规则里的协议一栏要么选 TCP,要么选 UDP,同一个端口两种都要时就建两条。不要图省事选“全部协议”,那会把这个来源对这台服务器的所有端口一起打开。

用 ss -tulnp 同时查看 TCP 和 UDP 监听端口

ss -tulnp 是排查端口问题的第一条命令,五个参数各管一件事:-t 列 TCP,-u 列 UDP,-l 只看监听中的,-n 端口显示数字不转换成服务名,-p 显示占用端口的进程(需要 root)。一台跑着 DNS 服务的机器,输出类似这样(示例):

Netid State  Recv-Q Send-Q    Local Address:Port  Peer Address:Port Process
udp   UNCONN 0      0             127.0.0.1:323        0.0.0.0:*     users:(("chronyd",pid=702,fd=5))
udp   UNCONN 0      0          203.0.113.53:53         0.0.0.0:*     users:(("named",pid=1180,fd=512))
tcp   LISTEN 0      10         203.0.113.53:53         0.0.0.0:*     users:(("named",pid=1180,fd=514))
tcp   LISTEN 0      128             0.0.0.0:22         0.0.0.0:*     users:(("sshd",pid=905,fd=3))

读法要点:

  • 第一列 Netid 就是协议。53 端口出现了两次,一次 udp、一次 tcp,同属 named 这一个进程,正是“同号端口两种协议都用”。
  • UDP 的状态是 UNCONN,不是 LISTEN。UDP 没有连接,只要绑定了端口就算在“监听”。不加 -l、用 ss -uan 查看时,偶尔会看到状态为 ESTAB 的 UDP 行,那是程序对 UDP 套接字调用了 connect,固定了对端地址,并不代表建立了连接。
  • 地址决定谁能访问。chronyd 的 UDP 323 只绑在 127.0.0.1,只给本机的 chronyc 用,防火墙放不放行都不影响;named 绑在 203.0.113.53,外部能否访问取决于防火墙。

几个常用的变体:

ss -ulnp                     # 只看 UDP
ss -tlnp                     # 只看 TCP
ss -tulnp 'sport = :53'      # 只看 53 端口,两种协议都列出
lsof -nP -i :53              # 用 lsof 查,同样会列出 TCP 和 UDP

Windows 上 TCP 和 UDP 要分开查:

Get-NetTCPConnection -LocalPort 53 -State Listen
Get-NetUDPEndpoint -LocalPort 53
netstat -ano -p UDP | findstr :53

netstat -ano 其实同时列出 TCP 和 UDP,但 UDP 端点没有状态,习惯用 findstr LISTENING 过滤的话,UDP 端口会被全部漏掉。排查“端口开了却连不上”时,先用这些命令确认服务实际监听的是哪种协议、哪个地址,再去对照防火墙规则,能省掉大半时间。

在机房里怎么落地

托管和独立服务器多数没有云平台那样的安全组,端口放行落在两处:服务器自己的防火墙,以及机房边界设备上的 ACL。边界 ACL 按协议写规则,改动频繁时最怕“谁加了一条 permit ip any”说不清。Toplink DCIM 的交换机管理支持在浏览器里打开交换机的 SSH 终端,会话全程录像并记录命令级日志,每条 ACL 规则的增删都能追溯到人和时间。服务器侧防火墙规则写错、把 SSH 的 TCP 22 也挡在外面时,客户可以在客户自助端打开 VNC 控制台登录系统,自己改回规则,不必提工单等工程师进机房。

常见问题

HTTPS 的 TCP 443 和 HTTP/3 的 UDP 443 会冲突吗?

不会,它们是两个端口。Nginx 1.25 起支持 HTTP/3,可以在同一个 server 块里同时写 listen 443 ssl; 和 listen 443 quic reuseport;,前者监听 TCP 443,后者监听 UDP 443。响应头里还要加 Alt-Svc 告诉浏览器可以用 HTTP/3,防火墙和安全组也要另外放行 UDP 443,否则浏览器会退回 TCP 上的 HTTP/2 或 HTTP/1.1,网站照样能打开,只是没用上 HTTP/3。

IPv4 和 IPv6 的同一个端口算一个吗?

要看程序怎么监听。Linux 默认 net.ipv6.bindv6only 为 0,监听 [::]:80 的 IPv6 套接字会同时接收 IPv4 连接,此时再单独监听 0.0.0.0:80 会报地址已被占用;程序给套接字设置了 IPV6_V6ONLY 后,两者才能分别监听。Nginx 的 listen [::]:80 默认就是只收 IPv6,所以要和 listen 80 一起写。

除了 TCP 和 UDP,还有别的协议有端口吗?

有。SCTP(IP 协议号 132)也有自己独立的 16 位端口,常见于电信信令网,IDC 业务很少遇到;放行时要在防火墙里选 SCTP 协议。ICMP 没有端口,ping 走的就是 ICMP,所以 ping 得通和端口通不通没有关系。

想看看在你的机房里怎么用?

从一次产品演示开始,梳理你的业务链路。

查看价格
服务热线 400-112-2951

让我们聊聊你的 IDC 业务

扫码添加企业微信,预约产品演示或申请试用。

Toplink 企业微信二维码

长按保存或使用企业微信 / 微信扫描

也可致电
400-112-2951
Telegram
@TopLink88