服务器套接字 accept() 方法

13

当我使用ServerSocket类的accept方法时,谁知道端口是如何选择的?是否可以定义一个端口范围让该方法能够选择?我能否依次“取走”端口?

ServerSocket sSocket = new ServerSocket(5050);
Socket socket = sSocket.accept();

来自这本书


尝试使用 getLocalPort 并查看是否为真。 - Codemwnci
5
你正在阅读一本好书吗?“plain old socket”这个词组对我来说听起来有些奇怪。 - Roland Illig
是的,它是《Head First Java》这本面向初学者的书。 - Eugene
4
我认为那张图片至少让人感到困惑,如果不是完全错误的话。 - thejh
图片是错误的。已接受的套接字具有与侦听套接字相同的本地端口号。 - user207421
显示剩余3条评论
5个回答

27

这张图是错误的(在O'Reilly网站上列出了未经确认的勘误表)。

客户端随机选择它的端口(在Java中不需要特别操作),并连接到您指定的任何端口的服务器。使用命令行工具netstat,您可以看到这一过程。

首先,只有监听服务器套接字而没有客户端:

simon@lucifer:~$ netstat -n -a
Active Internet connections (including servers)
Proto Recv-Q Send-Q  Local Address          Foreign Address     (state)
...
tcp46      0      0  *.5050                 *.*                 LISTEN
...

(还有很多其他条目,我只删除了无关的内容)

现在有一个从本地主机(127.0.0.1)连接的客户端:

simon@lucifer:~$ netstat -n -a
Active Internet connections (including servers)
Proto Recv-Q Send-Q  Local Address          Foreign Address     (state)
...
tcp4       0      0  127.0.0.1.64895        127.0.0.1.5050      ESTABLISHED <- 1
tcp4       0      0  127.0.0.1.5050         127.0.0.1.64895     ESTABLISHED <- 2
tcp46      0      0  *.5050                 *.*                 LISTEN      <- 3
...

因为客户端是从同一台计算机连接的,所以我们可以看到两个已建立的连接-一个从客户端到服务器(1),另一个从服务器到客户端(2)。它们具有相反的本地和外部地址(因为它们正在互相通信),并且您可以看到服务器仍在使用端口5050,而原始服务器套接字(3)继续监听同一端口。

(这个输出是来自Mac的,但Windows/Linux也有netstat给出类似的输出)


嗨,SimonJ,你能解释一下为什么服务器端可以使用相同的套接字端口吗?谢谢! - WoooHaaaa
@MrROY 因为客户端会随机选择其本地端口,正如他在回答中所说。 - user207421
1
客户端选择本地端口没问题。但问题是服务器已在5050上建立连接,在其他条目中仍在同一端口5050上侦听。为什么要使用相同的套接字端口? - c.sankhala
5
我不确定你在问什么。服务器套接字继续使用5050端口,以便新客户端可以连接到它。新连接在服务器端使用5050端口,因为协议是这样定义的。之所以可能,是因为连接的“身份”基于五元组:(源IP、源端口、目标IP、目标端口、协议(TCP或UDP))。更改一个字段(通常是源端口)就足以产生一个新的、不同的连接。 - SimonJ

7
您选择了端口,当您输入 new ServerSocket(5050) 时。有关在接受的套接字上使用不同端口的所有内容都是胡说八道。请注意保留HTML标记。

3
TCP连接由四个部分组成:
  • 客户端IP
  • 客户端端口号
  • 服务器IP
  • 服务器端口号
例如,可以有多个客户端连接到同一个服务器端口 - 只要这些客户端没有相同的IP和端口号,就可以了。而操作系统会负责处理这一部分。
因此,只监听一个端口是完全可以的。

谢谢您的澄清。我刚刚上传了一张书中的图片,您能看一下吗? - Eugene

0

ServerSocket定义了端口作为构造函数的一部分。 如果您没有指定端口,则套接字未绑定(即无法访问)。

要获取连接套接字的端口,请使用getPort()而不是getLocalPort()。第二个将给出您服务器上的端口。


我已经在我的问题中添加了一条注释。请看第二行。谢谢。 - Eugene
1
我不太确定你的意思。连接到您的服务器的100个套接字都将通过端口5050进行连接。如果您指的是连接到您的机器的端口,则使用getPort()函数。 - Codemwnci

0

您可以将0作为端口号传递,以在任何空闲端口上创建服务器套接字,或者创建一个类似于以下方法的方法,以在给定范围内创建任何空闲端口的服务器套接字:

public java.net.ServerSocket createServerSocket(int rangeStart, int rangeEnd)
                            throws java.io.IOException {
  for(int port=rangeStart; port<=randeEnd; port++) {  
    try {
      return new ServerSocket(port);
    } catch(java.net.BindException be) {
      // debug/warning here
      continue;
    }
  }
  throw new java.io.IOException("Failed to create a server socket, all ports between " +
                                rangeStart + " - " + rangeEnd + " are already in use.");
}

循环不处理其他异常(例如SecurityException),但您可以添加它。

ServerSocket(i)? 你是指 ServerSocket(port) 吗? - Eugene
这样不行,因为当它第一次返回ServerSocket时,它会停止函数的执行。 - Aaron Esau

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