簡體   English   中英

從TCP套接字讀取的最有效方法

[英]Most effective way to read from a TCP socket

我有一個到服務器的TCP連接,由socket和流實現。 在會話期間,服務器可以發送任意數量的消息 - 我必須閱讀並處理它們。

我創建了一個線程,它以無限循環檢查和讀取數據:

in = socket.getInputStream();
ByteArrayOutputStream baos = null;
byte[] buf = new byte[4096];
while(!isInterrupted()) {
   baos = new ByteArrayOutputStream();
   for(int s; ( s = in.read(buf)) != -1; ) {
       baos.write(buf, 0, s);
       if(in.available() <= 0 ) {
           readChunk(baos.toByteArray());
       }
   }
}

但實際上,它效率不高 - 它使CPU處於高負荷狀態,並且某些字節與前一個答案結合在一起。

解決這種情況最有效,最優雅的方法是什么?

TCP不是面向消息的,而是面向流的。 這意味着如果您發送兩條消息AA和BB,則很有可能在不同的場合讀取值AABB,AABB,A ABB,AAB B,AA BB(其中空格表示不同的讀取嘗試)。

您需要自己處理消息大小或消息分隔符,因此不再需要in.available()。 此外,您的代碼將相同的數據至少復制3次到不同的緩沖區,並考慮在socket.getInputStream()上使用BufferedInputStream。

刪除available()調用。 InputStream.available()不是對流結束的有效檢查,它在文檔中也這樣說。 它也永遠不會返回負值。 readChunk()方法也是應該進行讀取的方法。 TCP中也沒有消息,因此使用available()或任何其他技術來識別它們是無效的。

編輯

你在其他贊譽中說你有一個計數前綴。 用那個。 使用DataInputStream.readInt()讀取它,分配該大小的byte[]數組,並用DataInputStream.readFully():填充它DataInputStream.readFully():

int len = din.readInt();
byte[] message = new byte[len];
din.readFully(message);

根據您對該消息所說的內容,這是一種方法:

in = socket.getInputStream();
byte[] buff = new byte[4096];
int packLen=0;
int ret=0;
while(!isInterrupted()) {
    int offset=0;
    int bLeft=4;
    // 99% of the times the read will return 4 bytes, 
    // but just in case, put it in a loop.
    while (bLeft > 0) {
        ret = in.read(buff, offset, bLeft);
        if (ret > 0) {
            bLeft-=ret;
            offset+=ret;
        }
        else if (ret == 0) {
            // socket has been closed
        }
        else {
            // soket has an error
        }

    }
    // convert the 4 bytes to an int, depends on the way it's was sent
    // this method is used frecuently
    packLen = (int)((buff[0] & 0xff) << 24) |
                 (int)((buff[1] & 0xff) << 16) |
                 (int)((buff[2] & 0xff) << 8) |
                 (int)(buff[3] & 0xff);

    // if the 4 bytes of the CRC32 is not included in the length, 
    // increment the length
    packLen+=4;
    offset=4;
                if (packLen > 4092)
                {
                   // packet is too big, ignore it or do something else
                   packLen=4092;
                }
    bLeft=packLen;
    // Noew loop until the whole mesage has been read
    while (bLeft > 0) {
        ret = in.read(buff, offset, bLeft);
        if (ret > 0) {
            bLeft-=ret;
            offset+=ret;
        }
        else if (ret == 0) {
            // socket has been closed
        }
        else {
            // soket has an error
        }
    }
    // the readChunk function must be change
    // Need to pass the length of the message. 
    // Is not the buff.length anymore 
    readChunk(buff, packLen+4 /* +4 for the length of the message*/);
}

如果您需要Java CRC32類我可以給它,它符合PKZIP和以太網標准。

編輯:注意:如果數據包長度大於4096,則此方法不起作用。

暫無
暫無

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

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