3

我目前在学校参与一个项目,我们正在构建一个用于 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()

4

3 回答 3

8

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

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

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


至于为什么要这么做,其实应该是根据源码来的。在 OpenJDK6source/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);

这意味着当您创建套接字时,bind listen都在较低级别完成。

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

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

于 2012-10-27T09:30:23.463 回答
2

我认为您对accept. 将 ServerSocket 比作队列和accept阻塞出队操作。套接字在绑定到端口后立即将传入连接入队,并且该accept方法以自己的速度将它们出列。所以,是的,他们本可以accept更好地命名,不那么令人困惑。

于 2012-10-27T09:49:23.327 回答
0

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

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

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

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

于 2020-08-16T06:49:01.420 回答