【问题标题】:ServerSocket listens without accept()ServerSocket 无需 accept() 即可侦听
【发布时间】:2012-10-27 09:13:55
【问题描述】:

我目前在学校参与一个项目,我们正在构建一个用于 Android 手机的通信系统。为此,我们将使用一个服务器,它会向所有客户端打开套接字,让它们进行通信。

我之前做过几个聊天应用程序,在套接字或线程处理方面没有任何问题,但这次,出于某种原因,它让我大吃一惊。

问题是应用程序在我启动ServerSocket 对象serverSocket = new ServerSocket(5000) 后立即开始侦听,而不是serverSocket.accept()

为什么会这样?

只要我使用以下方法:

public void startListen(String port) {
try {
    serverSocket = new ServerSocket(Integer.parseInt(port));
    portField.setEditable(false);
} catch (IOException e) {
    printMessage("Failed to initiate serverSocket: " + e.toString());
}}

端口在命令提示符中显示为正在侦听(使用netstat)。如果我不调用它,则该端口未列为侦听。

TCP 0.0.0.0:5000 计算机:0 监听

那么,我在使用 ServerSocket 对象时有什么遗漏吗?我使用ServerSocket 的旧程序在我调用accept() 之前不会开始收听。

【问题讨论】:

  • 我们中的一些人往往会做得更好,@StephenC,当我们明白为什么某事有效时。有些人觉得这让他们在自己的领域更有价值,因为他们实际上可以向其他人解释它,并将这些概念性知识传递给其他技术。
  • 你错了。所有使用 ServerSocket 的旧程序在您构造或绑定它时立即开始侦听。进入“收听”状态与 accept() 无关。不是一个真正的问题。

标签: java serversocket


【解决方案1】:

如果您谈论的是 Java ServerSocket,那么它没有 listen 方法,大概是因为它与客户端套接字不同。在这种情况下,一旦它有一个端口号(在构造函数中或作为bind 的一部分),它就可以继续自动侦听。

“常规”套接字(类似于 BSD)具有侦听功能的原因是因为客户端 服务器使用相同的类型,因此您需要自己决定如何使用它. ServerSocket 不是这种情况,因为它是一个 server 套接字:-)

说实话,我不知道你为什么会关心在调用 accept 之前监听是否处于活动状态。它是“监听”调用(在这个类中隐含)应该标记你的服务器开放业务。此时,通信层应该开始允许传入呼叫排队等待您呼叫accept。这通常是它们的工作方式,将请求排队,以防您的程序在接受它们时有点慢。


至于为什么这样做,其实应该是根据源码来的。在 OpenJDK6 source/share/classes/java/net/ServerSocket.java 中,构造函数最终都调用了一个构造函数:

public ServerSocket(int port, int backlog, InetAddress bindAddr)
throws IOException {
    setImpl();
    if (port < 0 || port > 0xFFFF)
        throw new IllegalArgumentException(
                   "Port value out of range: " + port);
    if (backlog < 1)
      backlog = 50;
    try {
        bind(new InetSocketAddress(bindAddr, port), backlog);
    } catch(SecurityException e) {
        close();
        throw e;
    } catch(IOException e) {
        close();
        throw e;
    }
}

bind(同一文件)的调用如下:

public void bind(SocketAddress endpoint, int backlog) throws IOException {
    if (isClosed())
        throw new SocketException("Socket is closed");
    if (!oldImpl && isBound())
        throw new SocketException("Already bound");
    if (endpoint == null)
        endpoint = new InetSocketAddress(0);
    if (!(endpoint instanceof InetSocketAddress))
        throw new IllegalArgumentException("Unsupported address type");
    InetSocketAddress epoint = (InetSocketAddress) endpoint;
    if (epoint.isUnresolved())
        throw new SocketException("Unresolved address");
    if (backlog < 1)
      backlog = 50;
    try {
        SecurityManager security = System.getSecurityManager();
        if (security != null)
            security.checkListen(epoint.getPort());
        getImpl().bind(epoint.getAddress(), epoint.getPort());
        getImpl().listen(backlog);
        bound = true;
    } catch(SecurityException e) {
        bound = false;
        throw e;
    } catch(IOException e) {
        bound = false;
        throw e;
    }
}

相关位是:

getImpl().bind(epoint.getAddress(), epoint.getPort());
getImpl().listen(backlog);

意味着bindlisten 在创建套接字时都在较低级别完成。

所以问题不在于“为什么会突然出现在netstat 中?”但是“为什么之前没有出现在netstat?”

我可能会将其归结为您的误读,或者 netstat 的不太好的实现。前者更有可能,除非您专门测试未调用 accept 的套接字,这不太可能。

【讨论】:

    【解决方案2】:

    我认为您对accept 的目的有一个稍微错误的想法。将 ServerSocket 比作队列,将 accept 比作阻塞 dequeue 操作。一旦绑定到端口,套接字就会将传入的连接加入队列,accept 方法会按照自己的节奏将它们出列。所以是的,他们本可以将accept 命名为更好,更容易混淆。

    【讨论】:

      【解决方案3】:

      一个关键原因可能是myServerSocket.setSoTimeout()accept() 调用阻塞 - 除非您在调用它之前定义超时,否则它只会阻塞该持续时间,然后无害地(即 ServerSocket 仍然有效)抛出 SocketTimeoutException

      这样,线程就在您的控制之下……但是当您不在临时阻塞的accept() 调用中时,那几毫秒会发生什么?客户端会找到监听端口吗? - 这就是为什么不需要accept() 调用来监听端口是一件好事。

      问题是应用程序在我一开始就开始监听 启动ServerSocket 对象serverSocket = new ServerSocket(5000),而不是serverSocket.accept()

      感谢这个问题 (-> upvote),因为这正是我想知道的这种行为,而无需进行实验。谷歌术语是'java serversocket 如果尝试连接而不接受会发生什么',这个问题在第一个命中的链接列表中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-08-09
        • 1970-01-01
        • 1970-01-01
        • 2011-02-28
        • 1970-01-01
        相关资源
        最近更新 更多