简体   繁体   English

性能问题。 使用java从套接字读取字节时速度太慢

[英]performance issue. The Speed is too slow while reading bytes from socket using java

these days I'm confused about the Tcp performance while using java socket. 这些天我在使用java socket时对Tcp性能感到困惑。 In fact the java code is very simple. 实际上java代码非常简单。 details as below: 详情如下:

  1. server open a port and begin to listen. 服务器打开一个端口并开始监听。
  2. client request and after connect to server, client begin to write to socket. 客户端请求和连接到服务器后,客户端开始写入套接字。
  3. after server got the request, it will open a new thread to handle this connection. 在服务器获得请求后,它将打开一个新线程来处理此连接。 (this connection is a long connection which will not time out). (此连接是一个长时间连接,不会超时)。
  4. the server will keep reading until it got the end separator, then give a response to the client and continue to keep reading again. 服务器将继续读取,直到它到达结束分隔符,然后给客户端回复并继续继续阅读。
  5. after client get the response, it will send another request again. 在客户端获得响应后,它将再次发送另一个请求。

I find if the client write the whole message (including the end separator) one time, the communication speed is good satisfactorily, the speed can reach to 50000 messages per minute. 我发现客户端一次写入整个消息(包括结束分隔符),通信速度是否令人满意,速度可以达到每分钟50000条消息。 How ever, if the client write the bytes to socket in separated times, the speed cut down quickly, just almost 1400 messages per minute, it is 1/40 times compared with the original speed. 但是,如果客户端在不同的时间内将字节写入套接字,则速度会快速下降,每分钟只有1400条消息,这是原始速度的1/40倍。 I'm quite confused about it. 我很困惑。 Any one could give me a hand? 任何人都可以帮我一臂之力? Any comments is appreciated! 任何评论表示赞赏!

my simulated server side is as below: 我模拟的服务器端如下:

public class ServerForHelp {

    final static int BUFSIZE = 10240;
    Socket socket;
    String delimiter = "" + (char) 28 + (char) 13;

    public static void main(String[] args) throws IOException {

            ServerSocket ss = new ServerSocket(9200);
            System.out.println("begin to accept...");
            while (true) {
                Socket s = ss.accept();
                Thread t = new Thread(new SocketThread1(s));
                t.start();
            }
    }

    public String readUntilDelimiter() throws Exception {
        StringBuffer stringBuf = new StringBuffer();
        InputStream stream = socket.getInputStream();
        InputStreamReader reader = null;
        reader = new InputStreamReader(stream);

        char[] buf = new char[BUFSIZE];

        while (true) {
            int n = -1;
                n = reader.read(buf, 0, BUFSIZE);
            if (n == -1) {
                return null;  // it means the client has closed the connection, so return null.
            } else if (n == 0) {
                continue; // continue to read the data until got the delimiter from the socket.
            }

            stringBuf.append(buf, 0, n);
            String s = stringBuf.toString();

            int delimPos = s.indexOf(delimiter);
            if (delimPos >= 0) {
                // found the delimiter; return prefix of s up to separator and
                // To make the thing simple, I have discarded the content after the delimiter.
                String result = s.substring(0, delimPos);
                sendTheResponse(socket);
                return result;
            }
        }
    }

    private void sendTheResponse(Socket socket) throws IOException {
        Writer writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        writer.write("Hi, From server response");
        writer.flush();
    }

}

class SocketThread1 implements Runnable {

    Socket socket;

    public SocketThread1(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        ServerForHelp server = new ServerForHelp();
        server.socket = socket;
        while (true) {
            try {
                if (server.readUntilDelimiter() == null) // it means that the client has closed the connection, exist
                    break;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

It is a normal socket programming. 这是一个正常的套接字编程。

and the following is my client side: 以下是我的客户方:

public void execute() throws Exception{

        int msgCnt = 0;
        Socket socket = null;
        byte[] bufBytes = new byte[512];
        long start = 0;
        final char START_MESSAGE = 0x0B;
        final char END_MESSAGE = 0x1C;
        final char END_OF_RECORD = 0x0D;//\r
        String MESSAGE = "HELLO, TEST";
        socket = new Socket("192.168.81.39", 9200);
        OutputStream os = socket.getOutputStream();
        InputStream is = socket.getInputStream();

        while (System.currentTimeMillis() - start < 60000)
        {

            // If you send the total message at one time, the speed will be improved significantly  

            // FORMAT 1
            StringBuffer buf = new StringBuffer();
            buf.append(START_MESSAGE);
            buf.append(MESSAGE);
            buf.append(END_MESSAGE);
            buf.append(END_OF_RECORD);
            os.write(buf.toString().getBytes());
            // FORMAT 1 END

            //FORMAT 2
//        os.write(START_MESSAGE);
//        os.write(MESSAGES[port].getBytes());
//        os.write(END_MESSAGE);
//        os.write(END_OF_RECORD);
            //FORMAT 2 END
            os.flush();
            is.read(bufBytes);
            msgCnt++;

            System.out.println(msgCnt);
        }
        System.out.println( msgCnt + " messages per minute");
    }

If I use the "FORMAT 1", to send the message, the speed could reach to 50000 messages per minute, but If use "FORMAT 2", the speed is down to 1400 messages per minute. 如果我使用“格式1”,发送消息,速度可以达到每分钟50000条消息,但如果使用“格式2”,则速度降至每分钟1400条消息。 Who is clear about the reason? 谁清楚原因?

I'm trying to describe as detail as I can and any help will be appreciated very much. 我试图尽可能详细地描述,任何帮助都将非常感激。

Multiple very short writes to a socket in rapid succession followed by a read can trigger a bad interaction between Nagle's algorithm and TCP delayed acknowledgment ; 快速连续多次向套接字写入非常短的后续读取可能会触发Nagle算法TCP延迟确认之间的错误交互; even if you disable Nagle's algorithm, you'll cause an entire packet to be sent per individual write call (with 40+ bytes of overhead, whether the write is one byte or a thousand). 即使您禁用Nagle的算法,也会导致每个写入调用发送一个完整的数据包(开销超过40个字节,无论写入是一个字节还是一千个)。

Wrapping a BufferedOutputStream around the socket's output stream should give you performance similar to "FORMAT 1" (precisely because it holds things in a byte array until it fills or is flushed). 在套接字的输出流周围包装BufferedOutputStream应该会提供类似于“FORMAT 1”的性能(正是因为它在字节数组中保存,直到填充或刷新为止)。

As John Nagle explained on Slashdot : 正如John Nagle在Slashdot上解释的那样

The user-level solution is to avoid write-write-read sequences on sockets. 用户级解决方案是避免套接字上的写 - 读 - 读序列。 write-read-write-read is fine. write-read-write-read很好。 write-write-write is fine. 写 - 写 - 写得很好。 But write-write-read is a killer. 但写 - 写 - 读是一个杀手。 So, if you can, buffer up your little writes to TCP and send them all at once. 所以,如果可以的话,将你的小写入缓冲到TCP并立即发送它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM