【发布时间】:2012-05-23 19:18:34
【问题描述】:
我创建了一个远程桌面控制应用程序。显然,它由客户端和服务器部分组成:
服务器:
- 从客户端接收鼠标/键盘操作;
- 将桌面截图发送给客户端。
客户:
- 从服务器接收截图;
- 发送鼠标/键盘动作;
考虑发送屏幕截图。当我将家用 PC 用作服务器时,我最终会得到 1920x1080 的屏幕截图尺寸。通过使用JAI Image I/O Tools 并将其编码为PNG,我能够为如此大的图像实现以下统计数据:
- 写入时间 ~0.2 秒; (不是进入套接字,而是进入一些“常规”输出流,即编码时间)
- 读取时间 ~0.05 s; (不是来自套接字,而是来自一些“常规”输入流,即解码时间)
- 大小约为 250 KB;
- 完美的品质。
因此,根据 #1 - 理想的可能 FPS 应该是 ~5。
不幸的是,我什至无法达到 5 FPS,甚至 2 FPS。我搜索了瓶颈,发现向/从 socket I/O 流 写入/读取需要大约 2 秒(请参阅附录 1 和 2 以了解说明)。这当然是不可接受的。
我对该主题进行了一些研究 - 并在两侧添加了套接字 I/O 流的缓冲(使用 BufferedInputStream 和 BufferedOutputStream)。我从 64 KB 大小开始。这确实提高了性能。但仍然不能有至少 2 FPS!另外,我用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 - 他让我明白了一些事情。如果您像我一样 - 寻求有关如何调整套接字性能的答案,您绝对应该检查 TCP/IP Sockets in Java, Second Edition: Practical Guide for Programmers,尤其是第 6 章“幕后”,它描述了 *Socket 类的幕后发生的事情,发送和接收缓冲区是如何管理和利用(这是性能的主要关键)。
【问题讨论】:
-
您确定套接字设置是您的限制因素吗?您的客户端和服务器之间有什么样的连接?您的服务器的上行速度是多少?
-
你能把你从客户端套接字读取的代码贴出来吗?
-
@Roger Lindsjö 你所说的“什么样的连接”是什么意思?如何测量我的服务器的速度?
-
为什么要测试 null?并继续?您是否希望将空值写入
ObjectOutputStream?
标签: java performance sockets networking