Java SocketChannel吃我的字节

时间:2009-11-16 16:01:15

标签: java sockets nio tcpclient socketchannel

我创建了一个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读取。

2 个答案:

答案 0 :(得分:1)

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

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

始终为0(与&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;

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

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

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

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

如果该行:

ByteBuffer buf = ByteBuffer.allocate(4);

会超出导致您描述的行为的while,但在您的示例代码中却不是。

答案 1 :(得分:0)

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

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

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