簡體   English   中英

性能問題。 使用java從套接字讀取字節時速度太慢

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

這些天我在使用java socket時對Tcp性能感到困惑。 實際上java代碼非常簡單。 詳情如下:

  1. 服務器打開一個端口並開始監聽。
  2. 客戶端請求和連接到服務器后,客戶端開始寫入套接字。
  3. 在服務器獲得請求后,它將打開一個新線程來處理此連接。 (此連接是一個長時間連接,不會超時)。
  4. 服務器將繼續讀取,直到它到達結束分隔符,然后給客戶端回復並繼續繼續閱讀。
  5. 在客戶端獲得響應后,它將再次發送另一個請求。

我發現客戶端一次寫入整個消息(包括結束分隔符),通信速度是否令人滿意,速度可以達到每分鍾50000條消息。 但是,如果客戶端在不同的時間內將字節寫入套接字,則速度會快速下降,每分鍾只有1400條消息,這是原始速度的1/40倍。 我很困惑。 任何人都可以幫我一臂之力? 任何評論表示贊賞!

我模擬的服務器端如下:

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();
            }
        }
    }

}

這是一個正常的套接字編程。

以下是我的客戶方:

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");
    }

如果我使用“格式1”,發送消息,速度可以達到每分鍾50000條消息,但如果使用“格式2”,則速度降至每分鍾1400條消息。 誰清楚原因?

我試圖盡可能詳細地描述,任何幫助都將非常感激。

快速連續多次向套接字寫入非常短的后續讀取可能會觸發Nagle算法TCP延遲確認之間的錯誤交互; 即使您禁用Nagle的算法,也會導致每個寫入調用發送一個完整的數據包(開銷超過40個字節,無論寫入是一個字節還是一千個)。

在套接字的輸出流周圍包裝BufferedOutputStream應該會提供類似於“FORMAT 1”的性能(正是因為它在字節數組中保存,直到填充或刷新為止)。

正如John Nagle在Slashdot上解釋的那樣

用戶級解決方案是避免套接字上的寫 - 讀 - 讀序列。 write-read-write-read很好。 寫 - 寫 - 寫得很好。 但寫 - 寫 - 讀是一個殺手。 所以,如果可以的話,將你的小寫入緩沖到TCP並立即發送它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM