调整 Java 套接字的性能
我创建了一个远程桌面控制应用程序。显然,它由客户端和服务器部分组成:
服务器:
- 从客户端接收鼠标/键盘操作;
- 将桌面的屏幕截图发送到客户端。
客户:
- 从服务器接收屏幕截图;
- 发送鼠标/键盘操作;
考虑发送屏幕截图。当我使用我的家用PC作为服务器时 - 我最终得到一个1920x1080的屏幕截图尺寸。通过使用JAI图像I / O工具并将其编码为PNG,我能够为这样的大图像实现以下统计信息:
- 写入时间 ~0.2 s;(不是进入套接字,而是进入一些“常规”输出流,即编码时间)
- 读取时间 ~0.05 s;(不是来自套接字,而是来自一些“常规”输入流,即解码时间)
- 大小 ~250 KB;
- 完美的品质。
因此,根据#1 - 理想的可能FPS应该是~5。
不幸的是,我甚至无法实现那些~5 FPS,甚至无法实现2 FPS。我搜索了瓶颈,发现写入/读取到/读取套接字 I/O 流最多需要大约 2 秒(有关说明,请参阅附录 1 和 2)。这当然是不可接受的。
我对这个主题进行了一些研究 - 并在两端添加了套接字的I / O流(带有和)的缓冲。我从64 KB大小开始。这确实提高了性能。但仍然不能有至少2 FPS!此外,我已经尝试过,并且在速度上有一些变化,但我不知道它们的行为究竟如何,因此我不知道该使用哪些值。BufferedInputStream
BufferedOutputStream
Socket#setReceiveBufferSize
Socket#setSendBufferSize
查看初始化代码:
服务器:
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReceiveBufferSize( ? ); // #1
serverSocket.bind(new InetSocketAddress(...));
Socket clientSocket = serverSocket.accept();
clientSocket.setSendBufferSize( ? ); // #2
clientSocket.setReceiveBufferSize( ? ); // #3
OutputStream outputStream = new BufferedOutputStream(
clientSocket.getOutputStream(), ? ); // #4
InputStream inputStream = new BufferedInputStream(
clientSocket.getInputStream(), ? ); // #5
客户:
Socket socket = new Socket(...);
socket.setSendBufferSize( ? ); // #6
socket.setReceiveBufferSize( ? ); // #7
OutputStream outputStream = new BufferedOutputStream(
socket.getOutputStream(), ? ); // #8
InputStream inputStream = new BufferedInputStream(
socket.getInputStream(), ? ); // #9
问题:
- 对于所有这些情况,您会推荐哪些值(以提高性能),为什么?
- 请澄清和行为。
Socket#setReceiveBufferSize
Socket#setSendBufferSize
- 您还建议使用哪些其他方法/技术来提高此类应用程序的性能?
- Skype提供高质量的实时桌面传输 - 他们是如何做到的?
附录1:添加客户端套接字读取的展开伪代码 (@mcfinnigan):
while(true) {
// objectInputStream is wrapping socket's buffered input stream.
Object object = objectInputStream.readObject(); // <--- Bottleneck (without setting proper buffer sizes is ~2 s)
if(object == null)
continue;
if(object.getClass() == ImageCapsule.class) {
ImageCapsule imageCapsule = (ImageCapsule)object;
screen = imageCapsule.read(); // <--- Decode PNG (~0.05 s)
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
repaint();
}
});
}
}
附录2:添加服务器套接字写入的展开伪代码 (@EJP):
while(true) {
// objectOutputStream is wrapping socket's buffered output stream.
BufferedImage screen = ... // obtaining screenshot
ImageCapsule imageCapsule = new ImageCapsule();
imageCapsule.write(screen, formatName()); // <--- Encode PNG (~0.2 s)
try {
objectOutputStream.writeObject(imageCapsule); // <--- Bottleneck (without setting proper buffer sizes is ~2 s)
}
finally {
objectOutputStream.flush();
objectOutputStream.reset(); // Reset to free written objects.
}
}
结论:
感谢您的回答,特别是EJP - 他让我更清楚一点。如果你像我一样 - 寻找如何调整套接字性能的答案,你一定要检查Java中的TCP / IP套接字,第二版:程序员实用指南,特别是第6章“引擎盖下”,它描述了类幕后发生的事情,如何管理和利用发送和接收缓冲区(这是性能的主要键)。*Socket