调整Java套接字的性能 [英] Tweaking performance of Java's sockets

查看:99
本文介绍了调整Java套接字的性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经创建了一个远程桌面控制应用程序。显然,它由客户端和服务器部分组成:

I've created a remote desktop control application. Obviously, it consists of client and server parts:

服务器:


  • 从客户端接收鼠标/键盘操作;

  • 将桌面屏幕截图发送给客户端。

客户:


  • 从服务器接收屏幕截图;

  • 发送鼠标/键盘操作;

考虑发送屏幕截图。当我使用家用PC作为服务器时 - 我最终获得了1920x1080的屏幕截图尺寸。使用 JAI Image I / O Tools 并将其编码为PNG我能够为这么大的图像获得以下统计数据:

Consider sending the screenshot. When I use my home PC as a server - I end up with a 1920x1080 screenshot dimension. By using JAI Image I/O Tools and encoding it as PNG I was able to achieve the following stats for such a big image:


  1. 写入时间~0.2秒; (不是插入套接字,而是进入某些常规输出流,即编码时间

  2. 读取时间~0.05 s; (不是来自套接字,而是来自某些常规输入流,即解码时间

  3. 大小~250 KB;

  4. 完美品质。

  1. Write time ~0.2 s; (not into socket, but into some "regular" output stream, i. e. encode time)
  2. Read time ~0.05 s; (not from socket, but from some "regular" input stream, i. e. decode time)
  3. Size ~250 KB;
  4. Perfect quality.

因此,根据#1 - 理想的可能FPS应为~5。

As a result, according to #1 - the ideal possible FPS should be ~5.

不幸的是,我甚至无法达到~5 FPS,甚至不能达到2 FPS。我搜索了瓶颈,发现写入/读取套接字I / O流需要大约2秒(请参阅附录1和2以获得澄清)。当然这是不可接受的。

Unfortunately, I'm not able to achieve even those ~5 FPS, not even 2 FPS. I searched for the bottleneck and found out that writing/reading to/from socket I/O streams takes up to ~2 s (See appendix 1 and 2 for clarification). Surely that is unacceptable.

我对这个话题进行了一些研究 - 并增加了套接字I / O流的缓冲(使用 BufferedInputStream BufferedOutputStream )。我从64 KB大小开始。这确实提高了性能。但仍然不能拥有至少2个FPS!另外,我已经尝试了 Socket #setReceiveBufferSize Socket #setSendBufferSize 并且速度有一些变化,但是我不知道他们的行为究竟如何,因此我不知道要使用哪些值。

I've researched the topic a bit - and added buffering of socket's I/O streams (with BufferedInputStream and BufferedOutputStream) on both sides. I've started with 64 KB sizes. This indeed improved performance. But still can't have at least 2 FPS! In addition, I've experimented with Socket#setReceiveBufferSize and Socket#setSendBufferSize and there were some changes in speed, but I don't know how exactly they behave, therefore I don't know which values to use.

查看初始化代码:

服务器:

    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

问题:


  1. 对于所有
    这些案例,您会推荐哪些值(以提高性能)以及为什么?

  2. 请澄清套接字#setReceiveBufferSize
    套接字#setSendBufferSize 行为。

  3. 其他什么方法/技巧可以建议提高此类应用程序的性能吗?

  4. Skype提供高质量的实时桌面传输 - 他们是如何做到的?

  1. Which values would you recommend (to improve performance) for all these cases and why?
  2. Please clarify Socket#setReceiveBufferSize and Socket#setSendBufferSize behavior.
  3. What other methods/techniques can you advise to improve performance of such application?
  4. Skype provides good-quality real-time desktop transmission - how do they do it?

附录1:添加客户端套接字读取的展开伪代码(@mcfinnigan):

Appendix 1: Adding the unrolled pseudo-code of client socket reading (@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):

Appendix 2: Adding the unrolled pseudo-code of server socket writing (@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套接字在Java,第二版:程序员实用指南,特别是第6章Under the Hood,它描述了 * Socket 类幕后发生的事情,如何发送管理和利用接收缓冲区(这是性能的主要关键)。

Thanks for your answers, specially EJP - he made things a bit more clear to me. If you are like me - seeking answers on how to tweak sockets' performance you should definitely check TCP/IP Sockets in Java, Second Edition: Practical Guide for Programmers, especially chapter 6 "Under the Hood" which describes what happens behind the scenes of *Socket classes, how send and receive buffers are managed and utilized (which are primary keys to performance).

推荐答案



  • 写作时间~0.2秒;

  • 阅读时间~0.05秒;

如果不考虑干预网络的延迟和带宽,这些目标毫无意义。

These objectives are completely meaningless without consideration of the latency and bandwidths of the intervening network.


大小〜 250 KB;

Size ~250 KB;

非主题。图像大小取决于您,它与实际编程无关,这是此网站的目的。

Off topic. Image size is up to you, and it has no relationship to actual programming, which is the purpose of this site.


完美品质。

Perfect quality.

'完美品质'只要求你不要丢弃任何你无法通过TCP获得的位。

'Perfect quality' only requires that you don't drop any bits, which you won't get anyway via TCP.

  serverSocket.setReceiveBufferSize( ? ); // #1

设置所有接受套接字的接收缓冲区大小。设置尽可能大,超过64k,如果可能的话。

That sets the receive buffer size for all accepted sockets. Set it as large as you can afford, over 64k if possible.

socket.setSendBufferSize( ? ); // #6

将此值设置为尽可能大,超过64k(如果可能)。

Set this as large as you can afford, over 64k if possible.

    socket.setReceiveBufferSize( ? ); // #7

由于这是一个已接受的套接字,您已经完成了上述操作。删除。

As this is an accepted socket you have already done this above. Remove.

    OutputStream outputStream = new BufferedOutputStream(
            socket.getOutputStream(), ? ); // #8
    InputStream inputStream = new BufferedInputStream(
            socket.getInputStream(), ? ); // #9

这些的默认值是8k;只要你有足够的套接字缓冲区大小就足够了。

The defaults for these are 8k; as long as you have decent socket buffer sizes that should be enough.


对于所有这些情况,你会推荐哪些值(以提高性能)为什么?

Which values would you recommend (to improve performance) for all these cases and why?

见上文。


请澄清 Socket #setReceiveBufferSize() Socket #setSendBufferSize()行为。

它们控制TCP'窗口'的大小。这是一个相当深奥的话题,但其目的是使大小至少等于网络的带宽延迟乘积,即以秒为单位的带宽(以字节为单位/秒)> =以字节为单位的缓冲区大小。

They control the size of the TCP 'window'. It's a fairly abstruse topic but the idea is to get the size at least equal to the bandwidth-delay product of your network, i.e. bandwidth in bytes/second times delay in seconds >= buffer size in bytes.


您可以提出哪些其他方法/技巧来提高此类申请的表现?

What other methods/techniques can you advise to improve performance of such application?

在发送数据时,不要抱着睡觉和做其他任务。尽可能快地以最紧密的循环发送它。

Don't futz around with sleeps and doing other tasks while sending data. Send it as fast as you can in the tightest possible loop you can arrange.


Skype提供高质量的实时桌面传输 - 如何他们这样做了吗?

Skype provides good-quality real-time desktop transmission - how do they do it?

关闭主题并且可能不可知,除非Skype员工碰巧想在这里泄露公司机密。

Off topic and probably unknowable unless a Skype employee happens to want to give away company secrets here.

这篇关于调整Java套接字的性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆