我有两组相同的Java程序[Server.java和Client.java]和[ServerTest.java和ClientTest.java]。它们都做同样的事情,客户端连接到服务器并将整数对发送到服务器以进行相乘,结果返回到客户端,然后打印出来。每次执行100次。
但是,在Test版本中,我为每次传递整数对及其乘法创建并关闭一个新套接字(执行100次乘法)。在正常版本中,我打开一个持久套接字并执行与客户端的所有交互,然后关闭。
直观地说,我认为创建一个持久套接字的方法比创建,接受和关闭套接字的速度快一点 - 实际上,创建,接受和关闭新套接字的方法明显更快。平均而言,持久套接字方法大约需要8秒,而每次创建新套接字的方法大约需要0.4秒。
我检查了两者的系统调用活动,并注意到两者之间没有任何不同。然后我在另一台计算机(macOS Sierra)上测试了相同的程序,两者之间存在着可忽略的差异。因此,似乎问题甚至不在于应用程序代码,而在于它与操作系统的交互方式(我正在运行Ubuntu LTS 16.04)。
有谁知道为什么这里的性能有这么大差异,或者如何进一步调查问题?我还在执行程序时检查了系统范围的指标(内存使用情况和CPU使用情况),似乎有足够的内存,CPU有足够的空闲时间。
请参阅以下两种方法的代码摘要:
每次创建新套接字的方法:
// this is called one hundred times
public void listen() {
try {
while (true) {
// Listens for a connection to be made to this socket.
Socket socket = my_serverSocket.accept();
DataInputStream in = new DataInputStream(socket
.getInputStream());
// Read in the numbers
int numberOne = in.readInt();
int numberTwo = in.readInt();
int result = numberOne * numberTwo;
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeInt(result);
// tidy up
socket.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (SecurityException se) {
se.printStackTrace();
}
}
持久套接字方法:
public void listen() {
try {
while (true) {
// Listens for a connection to be made to this socket.
Socket socket = my_serverSocket.accept();
for (int i = 0; i < 100; i++) {
DataInputStream in = new DataInputStream(socket
.getInputStream());
// Read in the numbers
int numberOne = in.readInt();
int numberTwo = in.readInt();
int result = numberOne * numberTwo;
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeInt(result);
}
// tidy up
socket.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (SecurityException se) {
se.printStackTrace();
}
}
您没有向我们展示发送乘法的整数代码。你碰巧有一个循环,在每次迭代中你发送一对并接收结果吗?如果是这样,请务必关闭Nagle的算法。
Nagle的算法试图克服“小包问题”,即当应用程序以小块重复发送数据时。这导致巨大的开销,因为数据包标头通常比数据本身大得多。该算法基本上组合了许多小的外发消息并一次性发送它们。如果没有收集到足够的数据,那么算法仍然可以发送消息,但仅在某个超时已经过去时才会发送。
在您的情况下,您正在将小块数据写入客户端和服务器端的套接字中。数据没有立即传输。相反,套接字等待更多的数据(没有),所以每次超时都要过去。
实际上,这两段代码之间的唯一区别不在于它们如何处理传入连接(通过有一个持久性套接字),不同之处在于,在你称之为“持久性”的那个中,100对数字成倍增加,而在另一个中,只有1对数字相乘然后返回。这可以解释时间上的差异。