1

我为远程服务器创建了一个 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 读取。

4

2 回答 2

1

您的括号已关闭,代码为:

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

始终为 0(与 << 16 和 << 24 相同),我猜你的意思是:

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

这将导致读取的消息字节不足,还会导致同步不匹配(而不是读取太多。)

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

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

如果该行:

ByteBuffer buf = ByteBuffer.allocate(4);

将超出while会导致您描述的行为的范围,但在您的示例代码中并非如此。

于 2009-11-16T16:15:46.460 回答
0

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

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

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

于 2009-11-16T22:39:07.930 回答