【问题标题】:ReadableByteChannel hangs on read(bytebuffer)ReadableByteChannel 挂起读取(字节缓冲区)
【发布时间】:2011-11-10 14:08:21
【问题描述】:

我正在使用 java 1.6 开发即时通讯工具。 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();
        }
    }   
}

在测试期间一切都很好,直到我登录我的帐户并导入我的好友列表。我向服务器发送状态请求,他将 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() 是:

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

所以应用程序被午餐,记录,然后发送联系人。服务器将我的列表中的一部分发回给我。但是在 readInStatus(ByteBuffer headBuffer) 方法中,我尝试强制列表的其余部分。现在有趣的部分 - 一段时间后它到达 Receiver.receiver.read()bytes = readChannel.read(bb) 它只是停止,我没有知道为什么,即使经过一段时间,我也没有任何想法,也没有错误。我整整一周都在与解决方案作斗争,但我没有得到任何解决方案。我将不胜感激任何建议。谢谢。


感谢您的回复。是的,我正在使用阻塞 SocketChannel,我尝试了非阻塞,但它变得疯狂并且失控,所以我跳过了这个想法。关于我期望的字节 - 这有点奇怪,因为它只给了我一次大小,但它的第一部分的大小不是整个包,其他部分根本不包含标题字节。我无法预测它会有多少字节,原因是 - 容量为 255 字节的描述。这正是我在以下位置创建变量 buddies 的原因:public synchronized void readInStatus(ByteBuffer headBuffer) 这基本上是我的好友列表的长度,在读取每个字段之前,我会检查是否还有足够的字节,如果没有,我会执行 read()。但是描述之前的最后一个字段是带有传入描述长度的整数。但是在完成某些处理之前,无法确定包裹的长度。 @robert 你认为我应该在那种情况下再次尝试切换到非阻塞 SocketChannel 吗?

【问题讨论】:

    标签: java multithreading connection bytebuffer socketchannel


    【解决方案1】:

    问题很可能是您发送的字节数少于您尝试读取的字节数。你可能错过了写东西,写东西的顺序错误,误读了大小字段或类似的东西。

    我想我会通过添加跟踪代码来计算和记录读取和写入的字节数、名义数据包大小等来解决这个问题。然后运行,并比较跟踪,看看事情开始不同步的地方。

    【讨论】:

    • 感谢您的快速回复。但是,我认为我做得对,一切都按预期进行。我在发送之前检查了缓冲区的大小及其正确性。发送只需要两个变量 int 和 byte 所以它的数学很简单。联系人存储在 Hashtable 中,我从上到下进行迭代。
    • 问题是接收缓冲区的限制。你清除它,当你应该将它设置为“大小”大小时,读取,清除然后根据大小设置缓冲区限制,然后以相同的方式再次读取数据。顺便说一句,我是一名网络工程师。
    【解决方案2】:

    如果您使用阻塞的 SocketChannel,则 read 将阻塞,直到缓冲区被填满或服务器提供流结束。对于具有连接保持活动的服务器,服务器不会发送流结束 - 它只会停止发送数据,并且读取将无限期挂起或直到超时。

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

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

    【讨论】:

    • 目前无法让方法 (ii) 工作。即使我移动了位置以使要读取的可用字节不超过 buffer.remaining(),仍然会阻塞
    猜你喜欢
    • 2015-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 2012-09-25
    • 1970-01-01
    • 2020-07-30
    • 1970-01-01
    相关资源
    最近更新 更多