Java ReadableByteChannel在读取时挂起(bytebuffer)

Java ReadableByteChannel在读取时挂起(bytebuffer),java,multithreading,connection,bytebuffer,socketchannel,Java,Multithreading,Connection,Bytebuffer,Socketchannel,我正在使用Java1.6开发Instant messenger。IM使用多线程-主线程、接收和ping。对于tcp/ip通信,我使用了SocketChannel。从服务器接收更大的包似乎有问题。服务器(而不是一个服务器)发送了几个包,这就是问题的开始。每个前8个字节都说明了包的类型和大小。我就是这样管理阅读的: public void run(){ while(true){ try{ Headbuffer.clear(); bytes =

我正在使用Java1.6开发Instant messenger。IM使用多线程-主线程、接收和ping。对于tcp/ip通信,我使用了SocketChannel。从服务器接收更大的包似乎有问题。服务器(而不是一个服务器)发送了几个包,这就是问题的开始。每个前8个字节都说明了包的类型和大小。我就是这样管理阅读的:

public void run(){
    while(true){
        try{
        Headbuffer.clear();
        bytes = readChannel.read(Headbuffer); //ReadableByteChannel
        Headbuffer.flip();
        if(bytes != -1){
            int head = Headbuffer.getInt();
            int size = Headbuffer.getInt();
            System.out.println("received pkg: 0x" + Integer.toHexString(head)+" with size "+ size+" bytes);
            switch(head){
            case incoming.Pkg1: ReadWelcome(); break;
            case incoming.Pkg2: ReadLoginFail();break;
            case incoming.Pkg3: ReadLoginOk();break;
            case incoming.Pkg4: ReadUserList();break;
            case incoming.Pkg5: ReadUserData();break;
            case incoming.Pkg6: ReadMessage();break;
            case incoming.Pkg7: ReadTypingNotify();break;
            case incoming.Pkg8: ReadListStatus();break;
            case incoming.Pkg9: ChangeStatus();break;
            }
        }
        }catch(Exception e){
            e.printStackTrace();
        }
    }   
}
在测试期间,一切都很好,直到我登录我的帐户并导入我的buddylist。我向服务器发送状态请求,他将80个联系人中的10个发送给我。所以我想出了这样的办法:

public synchronized void readInStatus(ByteBuffer headBuffer){

    byteArray.add(headBuffer); //Store every buffer in ArrayList
    int buddies = MainController.controler.getContacts().getSize();
    while(buddies>0){

          readStuff();
          readDescription();
        --buddies; 
    }       
}
每个readStuff()和readDescription()都在用缓冲区中的剩余字节检查每个参数大小:

 if(byteArray.get(current).remaining() >= 4){

      uin = byteArray.get(current).getInt();
      }else{
          byteArray.add(Receiver.receiver.read());
          current = current +1;
          uin = byteArray.get(current).getInt(); 
      }
而Receiver.Receiver.read()是:

所以,应用程序被午餐,记录,然后发送联系人。服务器把我的名单上的一部分发给我。但是在方法readInStatus(bytebufferheadbuffer)中,我试图强制列表的其余部分。现在有趣的部分是,经过一段时间,它到达了Receiver.Receiver.read()bytes=readChannel.read(bb)它只是停止了,我不知道为什么,没有错误,甚至过了一段时间,什么都没有,我也不知道。这一周我一直在努力,但我没有找到解决办法。如有任何建议,我将不胜感激。谢谢


谢谢你的回复。是的,我正在使用阻塞SocketChannel,我尝试了非阻塞,但它变得疯狂和失控,所以我跳过了这个想法。关于我期望的字节-这有点奇怪,因为它只在头中给了我一次大小,但它的第一部分的大小不是整个包的大小,其他部分根本不包含头字节。我无法预测它会有多少字节,原因是-描述有255字节的容量。这正是我在:
public synchronized void readInStatus(ByteBuffer headBuffer)

wich基本上是我好友列表的长度,在读取每个字段之前,我会检查是否还有足够的字节,如果没有,我会执行read()。但描述之前的最后一个字段是整数,长度为传入描述的长度。但在完成某些处理之前,无法确定包的长度@robert在这种情况下,您认为我应该再次尝试切换到非阻塞SocketChannel吗?

问题很可能是您发送的字节数比尝试读取的字节数少。您可能没有写入某些内容、以错误的顺序写入内容、误读大小字段或类似内容


我想我应该通过添加跟踪代码来计算和记录读写的字节数、概念上的packect大小等等来解决这个问题。然后运行,并比较跟踪,查看哪里开始出现不同步。

问题很可能是发送的字节数比试图读取的字节数少。您可能没有写入某些内容、以错误的顺序写入内容、误读大小字段或类似内容


我想我应该通过添加跟踪代码来计算和记录读写的字节数、概念上的packect大小等等来解决这个问题。然后运行并比较跟踪,以查看从何处开始不同步。

如果您使用的是阻塞SocketChannel,则read将阻塞,直到缓冲区填满或服务器交付流结束。对于连接保持活动状态的服务器,服务器不发送流的结尾-它将简单地停止发送数据,读取将无限期挂起或直到超时

你可以: (i) 尝试使用非阻塞SocketChannel,重复读取,直到读取传递0字节(但注意,0字节并不一定意味着流的结束-它可能意味着中断)或 (ii)如果您必须使用阻塞版本,并且您知道当剩余的读取字节数小于buffer.capacity()时,您期望从服务器(例如从标头)读取多少字节,请移动缓冲区的位置和/或限制,以便在读取之前只在缓冲区中留有所需的空间。我现在正在研究这个解决方案。如果对你有效,请让我知道


据我所知,如果您必须使用阻塞SocketChannel,而您不知道需要多少字节,并且服务器不发送流结束,则没有解决方案。

如果您使用的是阻塞SocketChannel,则read将阻塞,直到缓冲区填满或服务器交付流结束。对于连接保持活动状态的服务器,服务器不发送流的结尾-它将简单地停止发送数据,读取将无限期挂起或直到超时

你可以: (i) 尝试使用非阻塞SocketChannel,重复读取,直到读取传递0字节(但注意,0字节并不一定意味着流的结束-它可能意味着中断)或 (ii)如果您必须使用阻塞版本,并且您知道当剩余的读取字节数小于buffer.capacity()时,您期望从服务器(例如从标头)读取多少字节,请移动缓冲区的位置和/或限制,以便在读取之前只在缓冲区中留有所需的空间。我现在正在研究这个解决方案。如果对你有效,请让我知道


据我所知,如果您必须使用阻塞SocketChannel,并且您不知道需要多少字节,并且服务器不发送流结束,则没有解决方案。

感谢您的快速回复。然而,我认为我做得很好,一切都按预期的方式进行。在发送之前,我已经检查了缓冲区的大小及其正确性。发送只需要两个变量int和byte,所以它的数学运算相当简单。联系人存储在哈希表中,我从上到下进行迭代。问题是接收缓冲区的限制。当您应该将其设置为“大小”si时,您可以清除它
public ByteBuffer read(){
    try {
        ByteBuffer bb = ByteBuffer.allocate(40000);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bytes = readChannel.read(bb);
        bb.flip();
        return bb;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}