简体   繁体   English

Java SocketChannel吃我的字节

[英]Java SocketChannel Eating my bytes

I created a SocketChannel to a remote server to send and receive messages on Tomcat. 我创建了一个到远程服务器的SocketChannel,以在Tomcat上发送和接收消息。 To receive messages from a remote computer, I used a thread dedicated to task (only this thread will read from the socket, nothing else). 为了接收来自远程计算机的消息,我使用了一个专用于任务的线程(只有该线程将从套接字读取,其他都不会)。

When some bytes are received at the SocketChannel (I keep polling the SocketChannel on non-blocking mode for new data), I first read 4 bytes to get the length of the next message, then allocate and read x bytes from the SocketChannel, which is then decoded and reconstructed into a message. 当在SocketChannel上收到一些字节时(我一直在非阻塞模式下轮询SocketChannel以获取新数据),我首先读取4个字节以获取下一条消息的长度,然后从SocketChannel分配和读取x个字节,即然后解码并重构为一条消息。

Below is my code for the receiving thread: 以下是我的接收线程代码:

@Override
public void run() {

    while (true) { //Don't exit thread

        //Attempt to read the size of the incoming message
        ByteBuffer buf = ByteBuffer.allocate(4);

        int bytesread = 0;
        try {
            while (buf.remaining() > 0) {
                bytesread = schannel.read(buf);

                if (bytesread == -1) { //Socket was terminated

                } 

                if (quitthread) break;
            }

        } catch (IOException ex) {

        }

        if (buf.remaining() == 0) {
            //Read the header
            byte[] header = buf.array();
            int msgsize = (0xFF & (int)header[0]) + ((0xFF & (int)header[1]) << 8)
                    + ((0xFF & (int)header[2]) << 16) + ((0xFF & (int)header[3]) << 24);

            //Read the message coming from the pipeline
            buf = ByteBuffer.allocate(msgsize);
            try {
                while (buf.remaining() > 0) {
                    bytesread = schannel.read(buf);

                    if (bytesread == -1) { //Socket was terminated

                    }

                    if (quitthread) break;
                }
            } catch (IOException ex) {

            }

            parent.recvMessage(buf.array());
        }

        if (quitthread) {
            break;
        }
    }

}

The first bytes I received from the SocketChannel is fine, and I successfully decoded the message. 我从SocketChannel接收到的第一个字节很好,并且我成功解码了该消息。 However, the next time I read from the SocketChannel, the socket skipped ahead about 100 bytes, which caused the wrong bytes to be read and interpreted as length, causing everything to become corrupted. 但是,下一次我从SocketChannel读取时,套接字向前跳过了大约100个字节,这导致读取错误的字节并将其解释为长度,从而导致所有内容损坏。

What is wrong with the code? 代码有什么问题? No other thread is reading from the SocketChannel. 没有其他线程正在从SocketChannel读取。

Your parenthesis are off, the code is: 您的括号已关闭,代码为:

(0xFF & ((int)header[1] << 8))

which is always 0 (same with << 16 and << 24), my guess is you meant: 始终为0(与<< 16和<< 24相同),我的猜测是您的意思是:

((0xFF & ((int)header[1])) << 8)

This would lead to reading not enough message bytes, also leading to a mismatch in synchronisation (as opposed to reading too many.) 这将导致读取的消息字节不足,也导致同步不匹配(而不是读取太多)。

Edit : now you fixed the above, I cannot see anything wrong. 编辑 :现在您已解决上述问题,我看不到任何错误。 Could you tell us the relation between the length of the first message and the exact number of bytes that are eaten? 您能告诉我们第一条消息的长度与被吃掉的确切字节数之间的关系吗?

Based on the code shown, my only guess is that you edited some of the behaviour out of the sample shown which might influence the schannel, is the schannel referenced elsewhere? 根据显示的代码,我唯一的猜测是您从显示的示例中编辑了一些可能会影响schannel的行为,该schannel是否在其他地方引用?

If the line: 如果行:

ByteBuffer buf = ByteBuffer.allocate(4);

would be outside of the while that would result in behaviour you describe, but in your sample code it isn't. 将是外while这将导致您所描述的行为,但在你的示例代码并非如此。

I presume when you say you're polling the socket in non-blocking mode you mean you're using a the "standard" Selector.select() approach? 我猜想,当您说要以非阻塞模式轮询套接字时,意味着要使用“标准” Selector.select()方法?

When select returns and indicates that there's data available for reading from the socket you should only read the bytes that are available before re-entering the call to select(). 当select返回并指示有可从套接字读取的数据时,应仅在重新进入对select()的调用之前读取可用的字节。 If read() returns -1 it indicates that no more bytes are available for immediate reading in the buffer - It does not mean that the socket has been closed. 如果read()返回-1,则表明缓冲区中没有更多字节可立即读取-这并不意味着套接字已关闭。 Hence I suspect your of attempting to completely fill the buffer before returning is incorrect. 因此,我怀疑您尝试在返回之前完全填充缓冲区是不正确的。 Even if it does work your I/O thread will be constantly spinning whilst data arrives. 即使工作正常,在数据到达时,您的I / O线程也会不断旋转。 In particular, it looks like you're simply ignoring a return value of -1. 特别是,看起来您只是在忽略返回值-1。

Consider re-architecting your code to use a finite state machine approach. 考虑重新构造代码以使用有限状态机方法。 For example, I've implemented this in the past using a 3-state model: IDLE, READ_MESSAGE_LENGTH and READ_MESSAGE. 例如,我过去使用3状态模型来实现此功能:IDLE,READ_MESSAGE_LENGTH和READ_MESSAGE。

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

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