Java SocketChannel 吃掉我的字节

发布于 2024-08-11 06:55:10 字数 1822 浏览 10 评论 0原文

我创建了一个到远程服务器的 SocketChannel,以便在 Tomcat 上发送和接收消息。为了从远程计算机接收消息,我使用了一个专用于任务的线程(只有这个线程将从套接字读取,没有其他线程)。

当 SocketChannel 接收到一些字节时(我不断在非阻塞模式下轮询 SocketChannel 以获取新数据),我首先读取 4 个字节以获取下一条消息的长度,然后从 SocketChannel 分配并读取 x 个字节,即然后解码并重构为消息。

下面是我的接收线程代码:

@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;
        }
    }

}

我从 SocketChannel 收到的第一个字节很好,并且我成功解码了消息。然而,下次我从 SocketChannel 读取时,套接字向前跳过了大约 100 个字节,这导致读取错误的字节并将其解释为长度,从而导致所有内容都损坏。

代码有什么问题吗?没有其他线程正在从 SocketChannel 读取数据。

I created a SocketChannel to a remote server to send and receive messages on 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.

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. 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.

What is wrong with the code? No other thread is reading from the SocketChannel.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

仙气飘飘 2024-08-18 06:55:10

你的括号被关闭,代码是:

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

它总是0(与<<16和<<24相同),我猜你的意思是:

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

这会导致读取没有足够的消息字节,也会导致不匹配同步(而不是阅读太多。)

编辑:现在您修复了上述问题,我看不出有什么问题。你能告诉我们第一条消息的长度和被吃掉的确切字节数之间的关系吗?

根据显示的代码,我唯一的猜测是您从显示的示例中编辑了一些可能影响 schannel 的行为,schannel 是否在其他地方引用?

如果行:

ByteBuffer buf = ByteBuffer.allocate(4);

位于 while 之外,则会导致您描述的行为,但在示例代码中却并非如此。

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:

((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?

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.

无人接听 2024-08-18 06:55:10

我想当您说您正在以非阻塞模式轮询套接字时,您的意思是您正在使用“标准”Selector.select() 方法?

当 select 返回并指示有数据可供从套接字读取时,您应该只读取在重新输入对 select() 的调用之前可用的字节。如果 read() 返回 -1,则表示缓冲区中没有更多字节可用于立即读取 - 这并不意味着套接字已关闭。因此我怀疑您在返回之前尝试完全填充缓冲区是不正确的。即使它确实有效,你的 I/O 线程也会在数据到达时不断旋转。特别是,看起来您只是忽略了返回值 -1。

考虑重新构建代码以使用有限状态机方法。例如,我过去使用 3 状态模型实现了这一点:IDLE、READ_MESSAGE_LENGTH 和 READ_MESSAGE。

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?

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(). 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. 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. In particular, it looks like you're simply ignoring a return value of -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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文