首先,感谢您的阅读。这是我第一次以用户身份使用 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 通过 Internet 连接)。
这就是我测试的方式,我在 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 中,当我在连接之间暂停 100 毫秒时,一切正常,并且所有线程都快速点亮,没有人花费超过 150 毫秒左右(没有连接速度慢的问题!)。这不是在 Linux 中发生的事情。但是,当我删除“Thread.sleep(100)”时,不是只有一些连接获得连接重置异常,而是所有连接都失败并抛出异常(在 Linux 中只有其中一些,400 个中有 6 个左右)失败了)。
呸!我刚刚发现不仅操作系统,JVM 环境也有一点影响!没什么大不了的,但值得注意。我在 Linux 中使用 OpenJDK,现在使用 Oracle JDK,我发现随着我减少连接之间的睡眠时间,它开始更早地失败(50 毫秒 OpenJDK 工作正常,没有抛出异常,但 Oracle 的一个相当不错很多有 50 毫秒的睡眠时间,而有 100 毫秒的工作正常)。