Java SocketChannel吃掉我的字节

Java SocketChannel吃掉我的字节,java,sockets,nio,tcpclient,socketchannel,Java,Sockets,Nio,Tcpclient,Socketchannel,我创建了一个到远程服务器的SocketChannel,用于在Tomcat上发送和接收消息。为了从远程计算机接收消息,我使用了一个专用于该任务的线程(只有这个线程会从套接字读取消息,其他什么都不会) 当SocketChannel接收到一些字节时(我一直在非阻塞模式下轮询SocketChannel以获取新数据),我首先读取4个字节以获取下一条消息的长度,然后从SocketChannel分配并读取x个字节,然后将其解码并重构为消息 下面是我的接收线程代码: @Override public void

我创建了一个到远程服务器的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;
        }
    }

}
@覆盖
公开募捐{
while(true){//不退出线程
//尝试读取传入消息的大小
ByteBuffer buf=ByteBuffer.allocate(4);
int字节读取=0;
试一试{
while(buf.remaining()>0){
bytesread=schannel.read(buf);
如果(bytesread==-1){//套接字已终止
} 
如果(退出线程)中断;
}
}捕获(IOEX异常){
}
if(buf.remaining()==0){
//阅读标题
字节[]头=buf.array();

int msgsize=(0xFF&(int)头[0])+((0xFF&(int)头[1])括号已关闭,代码为:

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

将在
之外,而
将导致您描述的行为,但在示例代码中则不是。

括号已关闭,代码为:

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

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

我猜想,当您说您在非阻塞模式下轮询套接字时,您的意思是您正在使用“标准”
选择器。select()
方法

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


考虑重新设计代码,以使用有限状态机方法。例如,我在过去使用3状态模型实现了这一点:空闲、读取消息长度和读取消息。

我猜,当您在非阻塞模式下轮询套接字时,您的意思是使用“标准”
选择器。选择()
方法

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


考虑重新设计代码以使用有限状态机方法。例如,我在过去使用3状态模型实现了这一点:空闲、读取消息长度和读取消息。

您可以简单地调用getInt()而不是将标头缓冲区转换为4个字节的数组如果你想把它当作一个无符号的int,你可以用0xFFFFFFFF将它转换成一个长的、按位的和。另外,我可能可以,尽管我不确定java是否会读取字节[]的arr[4]作为little endian。在任何情况下,我都怀疑这是问题的原因。第一条消息的长度是正确的,我读取的字节数是正确的。检查读取的消息也表明没有错误。只有当我尝试读取标题的下4个字节时才出现问题。与其将标题缓冲区转换为4个字节的数组,您可以简化y调用getInt()读取长度。如果您想将其视为无符号int,则始终可以使用0xFFFFFF将其转换为long和bit。此外,我可能可以,尽管我不确定java是否会读取字节[]的arr[4]作为little endian。在任何情况下,我都怀疑这是问题的原因。第一条消息的长度是正确的,我读取的字节数是正确的。检查读取的消息也表明没有错误。只有当我尝试读取标题的下4个字节时才出现问题。因为我每个线程只有一个套接字,我不知道是否使用选择器。选择()是必需的。在任何情况下,-1意味着流的结尾已到达=应通知父级。我只是暂时忽略了处理。我将尝试使用普通套接字()并查看它的运行情况。由于每个线程只有一个套接字,我不知道是否使用选择器。选择()是必需的。在任何情况下,-1意味着流的结尾已到达=应通知父级。我只是暂时忽略了处理。我将尝试使用普通套接字()并查看它的运行情况。看起来这是解码标头的问题。现在可以正常工作。看起来是解码标头的问题。现在可以正常工作。
ByteBuffer buf = ByteBuffer.allocate(4);