简体   繁体   中英

Most effective way to read from a TCP socket

I have a TCP connection to a server, implemented by socket and streams. During the session, the server can send any number of messages - I must read and process them all.

I created a thread, which checks and reads data in an infinite cycle:

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

But actually, it's not efficient - it puts the CPU under heavy load, and somehow bytes stick together with the previous answer.

What is the most effective and elegant way to resolve this situation?

TCP is not message-oriented, it's stream-oriented. This means that if you send two messages AA and BB, it's quite possible to read on different occasions the values AABB, AABB, A ABB, AAB B, AA BB (where spaces indicate different read attempts).

You will need to handle either message size or message delimiters on your own, thus no longer needing in.available(). Also, your code copies the same data at least 3 times to different buffers, and consider using a BufferedInputStream on the socket.getInputStream().

Remove the available() call. InputStream.available() is not a valid check for end of stream, and it says so in its documentation. Also it can never return a negative value. Also the readChunk() method is the one that should be doing the reading. Also there are no messages in TCP, so using available() or any other technique to identify them is invalid.

EDIT

You say in other commends you have a count prefix. Use that. Read it with DataInputStream.readInt() , allocate a byte[] array of that size, and fill it with DataInputStream.readFully():

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

According to what you've said about the message, this is a way to do it:

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*/);
}

If you need a Java CRC32 class I can give it to you, it conforms to PKZIP and Ethernet standard.

EDIT: Note: If a packet length is bigger that 4096 this method will not work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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