客户端(客户端套接字)如何被识别?

17
据我理解,通过 serverSocket = new ServerSocket(portNumber) 我们创建了一个对象,它可以潜在地"监听"指定的端口。通过clientSocket = serverSocket.accept(),我们强制服务器套接字 "监听" 其端口,并从试图通过与服务器关联的端口连接到服务器的任何客户端 "接受" 连接。当我说 "客户端试图连接到服务器" 时,我的意思是客户端程序执行 "nameSocket = new Socket(serverIP,serverPort)"。
如果客户端正在尝试连接服务器,则服务器会 "接受" 此客户端(即创建一个与此客户端关联的 "客户端套接字")。
如果新的客户端试图连接到服务器,则服务器将创建另一个客户端套接字(与新的客户端相关联)。但是,服务器如何知道这是一个 "新" 的客户端还是已经有其套接字的 "旧" 客户端?或者换句话说,客户端如何被识别?通过他们的 IP?通过他们的 IP 和端口?通过某些 "标识符"?
如果一个 "旧" 的客户端再次尝试使用 Socket(serverIP,serverIP),会发生什么?服务器会为该客户端创建第二个关联的套接字吗?
5个回答

33

服务器监听一个地址和端口。例如,你的服务器IP地址是10.0.0.1,它正在监听8000端口。

你的客户端IP地址是10.0.0.2,客户端在10.0.0.1端口8000上“连接”到服务器。在TCP连接中,你需要提供要连接的服务器端口。你的客户端实际上会得到自己的端口号,但你无法控制它,并且每次连接时都会不同。客户端选择要连接的服务器端口,而不是连接的客户端端口。

例如,在第一次连接时,你的客户端可能会得到客户端端口12345。它从10.0.0.2端口12345连接到服务器10.0.0.1端口8000。通过在连接的一侧调用getpeername,你的服务器可以看到客户端连接的端口。

当客户端第二次连接时,端口号将不同,比如说是端口12377。服务器可以通过在第二个连接上调用getpeername来查看这一点--它将在客户端一侧看到一个不同的端口号。(getpeername还显示客户端的IP地址。)

此外,每次在服务器上调用accept时,你都会得到一个新的套接字。你仍然拥有原始的监听套接字,在每个accept上你都会得到一个新的套接字。在已接受的套接字上调用getpeername,以查看连接来自哪个客户端端口。如果两个客户端连接到你的服务器,那么你现在有三个套接字--原始监听套接字和每个客户端的套接字。

您可以在同一时间将许多客户端连接到服务器端口8000。并且,许多客户端可以从同一客户端端口(例如端口12345)连接,但不能从相同的IP地址连接。对于来自同一客户端IP地址(例如10.0.0.2),连接到服务器端口8000的每个客户端连接都将来自于一个唯一的客户端端口,例如12345、12377等。您可以通过它们的IP地址和端口组合来区分不同的客户端。

同一客户端也可以同时与服务器建立多个连接,例如同时从客户端端口12345和12377建立连接。这里的客户端指的是源IP地址,而不是特定的软件对象。您只会看到两个具有相同客户端IP地址的活动连接。

此外,随着时间的推移,客户端地址和客户端端口的组合可能会被重复使用。也就是说,最终您可能会看到一个新的客户端从10.0.0.2端口12345进入,长时间之后,第一个客户端已经从10.0.0.2端口12345断开连接。


1
最后一段略有错误。一个新的客户端可以立即在相同的IP和端口上连接,而不是必须要等很久。对于TCP,服务器可以通过SYN/ACK/FIN数据包(由SOCKETS层处理)来判断是否是一个新连接。对于RAW或UDP套接字,应用程序需要确定什么是新连接,什么是现有连接。 - Jason R. Coombs
1
@JasonR.Coombs,您的评论忽略了TIME_WAIT状态,该状态有意防止了您所描述的情况,这也使得本答案的最后一段是正确的。 - user207421

6
每个 TCP 连接都有一个四元组 (源端口号,源地址,目标端口号,目标地址) 作为其标识符。
每当您的服务器接受一个新的客户端,就会创建一个新的 Socket,并且它是独立于到目前为止创建的所有其他 Socket 的。客户端的识别不会以某种隐式方式处理。
您不必将 Socket 视为与“客户端”相关联,它们与 IP 和端口相关联,但这两者之间没有直接关联。
如果同一客户端尝试通过创建新连接来打开另一个 Socket,则您将拥有两个不相关的 Socket(因为端口肯定不同)。这是因为客户端无法使用相同的端口来打开新连接,因此四元组将不同,客户端 IP 相同,服务器 IP 相同,服务器端口相同,但客户端端口不同。
针对您的问题进行编辑:
1. 客户端不指定端口,因为随机从底层操作系统的空闲端口(>1024,如果我没记错的话)中选择一个端口。 2. 客户端不能使用相同的端口打开连接,操作系统不允许您这样做(实际上您根本不指定任何端口),在任何情况下,它都会告诉您该端口已绑定到 Socket,因此不可能发生此问题。 3. 当服务器接收到新的连接请求时,它被视为新的连接,因为即使 IP 相同,端口也肯定不同(在旧数据包重新发送或类似情况下,我认为该请求将被丢弃)。
顺便说一下,所有这些情况都在 TCP RFC 中得到了明确解释,请参见此处

@Jack,但是客户端如何选择要连接的端口呢?在我提供的客户端代码示例中,客户端尝试通过指定服务器地址和服务器端口来连接服务器。也就是说,服务器不会指定它尝试连接服务器的端口。 - Roman
@Jack,我理解你的意思是,服务器套接字监听特定端口,只有当客户端从以前未使用过的clientIP-clientPort对调用时,才会创建一个新的客户端套接字? - Roman
@Jack,你写道:“每当你的服务器接受一个新客户端,就会创建一个新的Socket。”但是你如何定义“新”客户端呢?服务器如何知道特定客户端是“新”的还是“旧”的?我可以说,如果已经有与客户端所调用的IP端口对应的套接字,则将客户端视为“旧”客户端吗? - Roman
当客户端向HTTP和HTTPS发出请求时有什么区别?客户端在每个请求到HTTPS服务器时是否创建不同的套接字? - manjunath kallannavar
@Roman 服务器应用程序不需要知道。它只有套接字。它不关心它们是新的还是旧的。 - user207421
@manjunathkallannavar 有很多不同之处。首先是不同的端口号。无论是否保持HTTPS连接取决于双方,但在HTTP 1.1中默认情况下会保持连接,在HTTPS中也没有区别。 - user207421

1

我认为这里的问题是,你为什么关心客户端是新的还是旧的。什么是新的和旧的呢? 例如,一个Web浏览器可以连接到Web服务器请求网页。这将创建一个连接,因此serverSocket.accept()将返回一个新的Socket。然后,Web浏览器关闭了连接。

几分钟后,用户在网页中点击链接,浏览器向服务器请求新页面。这将创建一个连接,因此serverSocket.accept()将返回一个新的Socket

现在,Web服务器不关心这是新客户端还是旧客户端。它只需要提供所请求的页面即可。如果服务器确实关心“客户端”是否已经在过去请求过页面,则应使用套接字上使用的某些协议信息来执行此操作。请查看http://en.wikipedia.org/wiki/OSI_model 在这种情况下,ServerSocketSocket在传输层上进行确认。问题“此客户端是否已在服务器上请求过页面”应通过会话甚至应用程序层上的信息来回答。

在Web浏览器/服务器示例中,HTTP协议(它是一个应用程序)协议在请求的参数中保存有关此浏览器的信息(浏览器在每个请求中传输cookie信息)。然后,HTTP服务器可以设置/读取cookie信息以了解浏览器是否连接过,并最终为该浏览器维护服务器端会话。
所以回到你的问题:为什么你要关心它是新客户端还是旧客户端?

1

套接字由以下信息标识:

(本地IP、本地端口、远程IP、远程端口、IP协议(UDP/TCP/SCTP等)

这就是操作系统用来将数据包/数据映射到程序正确句柄/文件描述符的信息。对于某些类型的套接字(例如未连接的UDP套接字),远程端口/远程IP可能是通配符。


-1

根据定义,这不是一个关于Java的问题,而是关于网络的一般性问题,因为Sockets和SeverSockets适用于任何支持网络编程的编程语言。

Socket绑定到本地端口。客户端将通过操作系统/驱动程序/适配器/硬件/线路/.../线路/硬件/适配器/服务器操作系统打开与服务器的连接。当您连接到Internet时,这个“连接”是通过一个叫做IP(Internet Protocol)的协议完成的。当您使用“Sockets”时,它将使用另一种协议,即TCP/IP协议。

Internet协议将通过两个东西来识别网络上的节点:它们的IP地址和端口。TCP/IP协议将使用IP发送消息,并确保消息被正确接收。

现在,回答你的问题:这完全取决于情况!这取决于您的驱动程序、适配器、硬件和线路。当您连接到本地主机时,您将无法超越适配器。硬件并不是必需的,因为实际上没有数据发送到线路上。(尽管通常您需要硬件才能拥有适配器。)

根据定义,互联网协议将连接定义为一对节点(因此有四个要素:两个IP地址和两个端口)。此外,互联网协议规定一个节点只能同时使用一个端口与另一个节点建立连接(注意:这仅适用于客户端,而不是服务器)。

回答您的第二个问题:如果有两个套接字:“新”和“旧”。由于根据互联网协议,连接是一对节点,并且节点只能同时使用一个端口进行连接,“新”和“旧”的端口必须不同。因为这是不同的,所以可以通过端口号的不同来区分“新”客户端和“旧”客户端。


@Pindatjuh,我理解得对吗?如果客户端从相同的IP地址和端口请求套接字,那么监听特定端口的服务器套接字将不会创建新的客户端套接字。 - Roman
作为连接到服务器的客户端,您无法确定自己的端口。只有服务器可以在同一端口上拥有多个“传入连接”,但是客户端不能在同一端口上拥有多个“传出连接”。但是它可以重用连接,但必须首先关闭连接。服务器会收到关闭连接的通知。 - Pindatjuh
答案大多不正确。在IP层中不存在连接或端口。它们是TCP层的功能。您也可以在IP级别使用套接字或其他内容。端口唯一性的讨论也是不正确的:必须唯一的是连接的5元组,而不是单个端口号。您的评论也是不正确的:客户端确实可以定义自己的端口。虽然通常不这样做,但这是可能的。没有重用连接这种事情。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接