简体   繁体   English

从TCP套接字读取的最有效方法

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

I have a TCP connection to a server, implemented by socket and streams. 我有一个到服务器的TCP连接,由socket和流实现。 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. 但实际上,它效率不高 - 它使CPU处于高负荷状态,并且某些字节与前一个答案结合在一起。

What is the most effective and elegant way to resolve this situation? 解决这种情况最有效,最优雅的方法是什么?

TCP is not message-oriented, it's stream-oriented. TCP不是面向消息的,而是面向流的。 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). 这意味着如果您发送两条消息AA和BB,则很有可能在不同的场合读取值AABB,AABB,A ABB,AAB B,AA BB(其中空格表示不同的读取尝试)。

You will need to handle either message size or message delimiters on your own, thus no longer needing in.available(). 您需要自己处理消息大小或消息分隔符,因此不再需要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(). 此外,您的代码将相同的数据至少复制3次到不同的缓冲区,并考虑在socket.getInputStream()上使用BufferedInputStream。

Remove the available() call. 删除available()调用。 InputStream.available() is not a valid check for end of stream, and it says so in its documentation. InputStream.available()不是对流结束的有效检查,它在文档中也这样说。 Also it can never return a negative value. 它也永远不会返回负值。 Also the readChunk() method is the one that should be doing the reading. readChunk()方法也是应该进行读取的方法。 Also there are no messages in TCP, so using available() or any other technique to identify them is invalid. TCP中也没有消息,因此使用available()或任何其他技术来识别它们是无效的。

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(): 使用DataInputStream.readInt()读取它,分配该大小的byte[]数组,并用DataInputStream.readFully():填充它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. 如果您需要Java CRC32类我可以给它,它符合PKZIP和以太网标准。

EDIT: Note: If a packet length is bigger that 4096 this method will not work. 编辑:注意:如果数据包长度大于4096,则此方法不起作用。

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

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