I';当必须接受许多连接时,我在客户机/服务器类型的应用程序中遇到Java套接字问题
首先,谢谢你的阅读。这是我第一次以用户身份使用stackoverflow,尽管我经常阅读它并找到有用的解决方案:D。顺便说一句,如果我解释得不够清楚,我很抱歉,我知道我的英语不是很好 我的基于套接字的程序有一个奇怪的行为和一些性能问题。客户机和服务器通过以多线程方式将序列化对象读/写到对象输入和输出流中来相互通信。让我向您展示代码的基础知识。我已将其简化为更具可读性,并有意添加完整的异常处理(例如)。服务器的工作原理如下:I';当必须接受许多连接时,我在客户机/服务器类型的应用程序中遇到Java套接字问题,java,performance,sockets,objectoutputstream,objectinputstream,Java,Performance,Sockets,Objectoutputstream,Objectinputstream,首先,谢谢你的阅读。这是我第一次以用户身份使用stackoverflow,尽管我经常阅读它并找到有用的解决方案:D。顺便说一句,如果我解释得不够清楚,我很抱歉,我知道我的英语不是很好 我的基于套接字的程序有一个奇怪的行为和一些性能问题。客户机和服务器通过以多线程方式将序列化对象读/写到对象输入和输出流中来相互通信。让我向您展示代码的基础知识。我已将其简化为更具可读性,并有意添加完整的异常处理(例如)。服务器的工作原理如下: private SSLSocket socket; @
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!");
}
}
}
}
服务器:
// (...)
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
- 客户端x已成功完成李>
- 运行时间:239
- 客户端x已成功完成李>
- 运行时间:243
for(int i=0;i<400;i++){
试一试{
新的LjClientThread().start();
睡眠(100);
}捕获(例外情况除外){
// (...)
}
}
如果我在循环中不使用“Thread.sleep(100)”,就会出现一些连接重置异常(400个连接中重置了7个或8个连接,或多或少),但我想我理解了为什么会发生这种情况:当serverSocket.accept()接受连接时,只需花费很少的时间就可以再次访问serverSocket.accept()。在此期间,服务器无法接受连接。可能是因为这个吗?若否,原因为何?很少有400个连接同时到达我的服务器,但这可能发生。如果没有“Thread.sleep(100)”,则计时问题也会更严重
提前谢谢
更新: 多么愚蠢,我在本地主机上测试了它。。。它不会给任何问题!不管有没有“Thread.sleep(100)”,都没关系,它工作得很好!为什么?所以,正如我所看到的,我关于为什么会抛出连接重置的理论是不正确的。这让事情变得更加奇怪!我希望有人能帮我。。。再次感谢!:)
更新(2): 我在不同的操作系统中发现了明显不同的行为。我通常在Linux中开发,我解释的行为是关于我的Ubuntu 10.10中发生的事情。在Windows7中,当我在连接之间暂停100毫秒时,一切正常,所有线程都很快,没有人需要超过150毫秒左右(没有慢连接问题!)。这不是Linux中正在发生的事情。但是,当我删除“Thread.sleep(100)”时,不是只有一些连接得到连接重置异常,而是所有连接都失败并抛出异常(在Linux中,只有部分连接失败,400个连接中大约有6个失败)
呸!我刚刚发现,不仅操作系统,JVM环境也有一点影响!没什么大不了的,但值得注意。我曾在Linux中使用OpenJDK,现在使用Oracle JDK,我发现随着连接之间睡眠时间的减少,它会更早地出现故障(使用50毫秒的OpenJDK可以正常工作,不会引发异常,但是使用Oracle的一个50毫秒的睡眠时间,而使用100毫秒的睡眠时间可以正常工作).服务器套接字有一个队列,用于保存传入的连接尝试。如果队列已满,客户端将遇到连接重置错误。如果没有
Thread.sleep(100)
语句,您的所有客户端都会尝试相对同时地连接,这会导致其中一些客户端遇到连接重置错误。服务器套接字有一个队列,用于保存传入的连接尝试。如果队列已满,客户端将遇到连接重置错误。没有<代码>线程。睡眠(100)语句,所有的客户端都试图同时连接,这导致它们中的一些遇到连接复位错误。 两点,我认为您可以进一步考虑研究。对不起,这里有点含糊,但我是这么想的。 1) 在tcp级别,很少有依赖于平台的东西控制通过套接字发送/接收数据所需的时间。不一致的延迟可能是由于tcp\u syn\u重试等设置造成的。你可能有兴趣看看这里
2)您的计算执行时间不仅是完成执行所花费的时间,而且包括完成定稿之前的时间,当对象已准备好完成时,它不能保证立即发生。 < P2 >我认为您可以进一步考虑研究。对不起,这里有点含糊,但我是这么想的。 1) 在后台,在tcp级别,很少有依赖于平台的东西控制数据量