当我不得不接受许多连接时,我在客户端/服务器类型的应用程序中遇到了Java套接字问题

首先,感谢阅读。这是我第一次在stackoverflow中作为用户,虽然我总是阅读它并找到有用的解决方案:D。顺便说一句,抱歉,如果我不清楚解释自己,我知道我的英语不是很好。 我的基于套接字的程序有一个奇怪的行为,以及一些性能问题。客户端和服务器通过以多线程方式将序列化对象读/写到对象输入和输出流中来相互通信。让我向您展示代码基础知识。我已将其简化为更易读,并且例如有意地省略了完整的异常处理。服务器的工作方式如下: 服务器:
// (...)

public void serve() {
    if (serverSocket == null) {
        try {
            serverSocket = (SSLServerSocket) SSLServerSocketFactory
                                    .getDefault().createServerSocket(port);
            serving = true;
            System.out.println("Waiting for clients...");
            while (serving) {
                SSLSocket clientSocket = (SSLSocket) serverSocket.accept();
                System.out.println("Client accepted.");
                //LjServerThread class is below
                new LjServerThread(clientSocket).start();
            }
        } catch (Exception e) {
            // Exception handling code (...)
        }
    }
}

public void stop() {
    serving = false;
    serverSocket = null;
}

public boolean isServing() {
    return serving;
}
LjServerThread类,每个客户端创建一个实例:
private SSLSocket clientSocket;
private String IP;
private long startTime;

public LjServerThread(SSLSocket clientSocket) {
        this.clientSocket = clientSocket;
        startTime = System.currentTimeMillis();
        this.IP = clientSocket.getInetAddress().getHostAddress();
}

public synchronized String getClientAddress() {
    return IP;
}

@Override
public void run() {
    ObjectInputStream in = null;
    ObjectOutputStream out = null;
    //This is my protocol handling object, and as you will see below,
    //it works processing the object received and returning another as response.
    LjProtocol protocol = new LjProtocol();
    try {
        try {
            in = new ObjectInputStream(new BufferedInputStream(
                                     clientSocket.getInputStream()));
            out = new ObjectOutputStream(new BufferedOutputStream(
                                    clientSocket.getOutputStream()));
            out.flush();
        } catch (Exception ex) {
            // Exception handling code (...)
        }
        LjPacket output;
        while (true) {
            output = protocol.processMessage((LjPacket) in.readObject());
            // When the object received is the finish mark, 
            // protocol.processMessage()object returns null.
            if (output == null) {
                break;
            }
            out.writeObject(output);
            out.flush();
            out.reset();
        }
        System.out.println("Client " + IP + " finished successfully.");
    } catch (Exception ex) {
        // Exception handling code (...)
    } finally {
        try {
            out.close();
            in.close();
            clientSocket.close();
        } catch (Exception ex) {
            // Exception handling code (...)
        } finally {
            long stopTime = System.currentTimeMillis();
            long runTime = stopTime - startTime;
            System.out.println("Run time: " + runTime);
        }
    }
}
而且,客户端是这样的:
    private SSLSocket socket;

    @Override
    public void run() {
        LjProtocol protocol = new LjProtocol();
        try {
            socket = (SSLSocket) SSLSocketFactory.getDefault()
                     .createSocket(InetAddress.getByName("here-goes-hostIP"),
                                                                       4444);
        } catch (Exception ex) {

        }
        ObjectOutputStream out = null;
        ObjectInputStream in = null;
        try {
        out = new ObjectOutputStream(new BufferedOutputStream(
                                         socket.getOutputStream()));
        out.flush();
        in = new ObjectInputStream(new BufferedInputStream(
                                          socket.getInputStream()));
        LjPacket output;
        // As the client is which starts the connection, it sends the first 
        //object.
        out.writeObject(/* First object */);
        out.flush();
        while (true) {
            output = protocol.processMessage((LjPacket) in.readObject());
            out.writeObject(output);
            out.flush();
            out.reset();
        }
        } catch (EOFException ex) {
            // If all goes OK, when server disconnects EOF should happen.
            System.out.println("suceed!");
        } catch (Exception ex) {
            // (...)
        } finally {
            try {
                // FIRST STRANGE BEHAVIOUR:
                // I have to comment the "out.close()" line, else, Exception is
                // thrown ALWAYS.
                out.close();
                in.close();
                socket.close();
            } catch (Exception ex) {
                System.out.println("This shouldn't happen!");
            }
        }
    }
}
好吧,如你所见,处理服务器端接受客户端的LjServerThread类测量它所需的时间...通常需要75到120毫秒(其中x是IP): 客户端x成功完成。 运行时间:82 客户端x成功完成。 运行时间:80 客户端x成功完成。 运行时间:112 客户端x成功完成。 运行时间:88 客户端x成功完成。 运行时间:90 客户端x成功完成。 运行时间:84 但突然之间,没有可预测的模式(至少对我而言): 客户端x成功完成。 运行时间:15426 有时达到25秒! 偶尔会有一小部分线程变慢,但这并不会让我担心: 客户端x成功完成。 运行时间:239 客户端x成功完成。 运行时间:243 为什么会这样?这可能是因为我的服务器和我的客户端在同一台机器上,使用相同的IP吗? (为了做这个测试,我在同一台机器上执行服务器和客户端,但它们通过互联网与我的公共IP连接)。 这就是我测试它的方法,我在main()中向服务器发出这样的请求:
    for (int i = 0; i < 400; i++) {
        try {
            new LjClientThread().start();
            Thread.sleep(100);
        } catch (Exception ex) {
            // (...)
        }
    }
如果我在循环中没有“Thread.sleep(100)”,我会得到一些连接重置异常(7或8个连接重置400个,或多或少),但我想我明白为什么会发生:当serverSocket.accept ()接受连接,必须花费很少的时间再次到达serverSocket.accept()。在此期间,服务器无法接受连接。可能是因为那个吗?如果没有,为什么?很少有400个连接同时到达我的服务器,但它可能会发生。如果没有“Thread.sleep(100)”,时序问题也会更糟。 提前致谢! 更新: 多么愚蠢,我在localhost中测试过它...它没有任何问题!有没有“Thread.sleep(100)”,没关系,它工作正常!为什么!因此,正如我所看到的,我关于为什么连接重置被抛出的理论是不正确的。这让事情变得更加奇怪!我希望有人可以帮助我...再次感谢! :) 更新(2): 我在不同的操作系统中发现了不同的行为。我通常在Linux中开发,我解释的行为是关于我的Ubuntu 10.10中发生的事情。在Windows 7中,当我在连接之间暂停100ms,所有它都很好,并且所有线程都快速点亮,没有人需要超过150ms左右(没有慢速连接问题!)。这不是Linux中发生的事情。但是,当我删除“Thread.sleep(100)”时,而不是只有一些连接获得连接重置异常,它们全部失败并抛出异常(在Linux中只有一些,大约400个中的6个)失败了)。 唷!我只是发现不仅操作系统,JVM环境也有一点影响!没什么大不了的,但值得注意。我在Linux中使用OpenJDK,现在,使用Oracle JDK,我看到当我减少连接之间的休眠时间时,它开始先前失败(50毫秒OpenJDK工作正常,没有异常被抛出,但是Oracle的一个相当有50ms的睡眠时间,而100ms工作正常)。     
已邀请:
服务器套接字有一个队列,用于保存传入的连接尝试。如果该队列已满,客户端将遇到连接重置错误。如果没有
Thread.sleep(100)
语句,所有客户端都会尝试相对同时连接,这会导致其中一些客户端遇到连接重置错误。     
我认为你可以进一步考虑研究两点。对不起有点模糊,但这就是我的想法。 1)引擎盖下,在tcp级别,很少有平台相关的东西控制在套接字上发送/接收数据所需的时间。不一致的延迟可能是因为诸如tcp_syn_retries之类的设置。您可能有兴趣在这里查看http://www.frozentux.net/ipsysctl-tutorial/chunkyhtml/tcpvariables.html#AEN370 2)您计算的执行时间不仅是完成执行所花费的时间,还包括完成最终确定之前的时间,当对象准备好完成时,不能保证立即发生。     

要回复问题请先登录注册