据我了解,当客户端发出连接请求时会发生以下情况:
- 服务器将绑定到特定的端口号。端口号始终与侦听进程绑定。由于只有服务器正在侦听传入连接,因此我们不需要在客户端进行绑定
- 服务器将继续侦听该端口号。
- 客户端将发送
connect()
请求。 - 服务器将使用 接受请求
accept()
。一旦服务器接受了客户端请求,内核就会为服务器分配一个随机端口号,用于进一步send()
和receive()
,因为服务器上的同一端口号不能用于发送和侦听,并且之前的端口仍然是监听新连接
考虑到所有这些,服务器如何找出客户端正在哪个端口上接收数据?我知道客户端将发送带有源端口和目标端口的 TCP 报文段,因此服务器将使用该报文段的源端口作为其目标端口,但是服务器调用什么函数来查找该端口呢?是吗accept()
?
答案1
它是数据包中 TCP(或 UDP 等)标头的一部分。所以服务器发现是因为客户端告诉它。这类似于查找客户端 IP 地址(IP 标头的一部分)的方式。
例如,每个 TCP 数据包都包含一个 IP 标头(至少具有源 IP、目标 IP 和协议 [TCP])。然后是 TCP 标头(包含源端口和目标端口等)。
当内核收到远程 IP 为 10.11.12.13(在 IP 标头中)和远程端口为 12345(在 TCP 标头中)的 SYN 数据包(TCP 连接的开始)时,它就知道远程 IP 和端口。它发回 SYN|ACK。如果收到 ACK,则listen
调用将返回一个为该连接设置的新套接字。
TCP套接字由四个值(远程IP、本地IP、远程端口、本地端口)唯一标识。您可以有多个连接/套接字,只要其中至少有一个不同即可。
通常,与服务器进程的所有连接的本地端口和本地 IP 都是相同的(例如,与 sshd 的所有连接都在 local-ip:22 上)。如果一台远程计算机进行多个连接,则每个连接将使用不同的远程端口。因此,除了远程端口之外的所有内容都将相同,但这很好——四个端口中只有一个必须不同。
您可以使用,例如,wirehsark 来查看数据包,它会为您标记所有数据。这是突出显示的源端口(请注意它在解码的数据包中突出显示,以及底部的十六进制转储):
答案2
“连接请求(connect()
通常是客户端程序的系统调用)会导致三次握手。 3 次握手(从客户端到服务器)的第一个数据包设置了 SYN 标志,并包含客户端程序内核分配给它的 TCP 端口号。
您可以在关于 Nmap 与自然 SYN 数据包的文章。 Nmap SYN 数据包解码具有短语“source.60058 > dest.22”。 “合法 SYN 数据包”解码中包含短语“source.35970 > dest.80”。这两个 SYN 数据包告诉远程内核这些数据包分别来自 TCP 端口 60058 和端口 35970。
答案3
TCP套接字是面向流的套接字。两个套接字描述符(由您和您的同伴拥有)可靠连接。所以你不必担心客户端的端口——只需编写你的套接字描述符!
另外,如果你真的想知道(也许是为了记录),请随意使用 getsockname(2) 。
答案4
连接由元组(源 IP、源端口、目标 IP、目标端口)定义。答案恰恰相反。