ReadableByteChannel挂起读取(bytebuffer)

时间:2011-09-05 12:02:50

标签: java multithreading connection bytebuffer socketchannel

我正在使用java 1.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();
        }
    }   
}

在测试期间一切正常,直到我登录我的帐户并导入我的好友列表。我向服务器发送请求以获取状态,并且他向我发送了大约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字节容量的描述。这正是我在public synchronized void readInStatus(ByteBuffer headBuffer)中创建变量好友的原因 这基本上是我的好友列表的长度,在阅读每个字段之前,我正在检查是否有足够的字节,如果不是,我会读取()。但是描述之前的最后一个字段是整数,其中包含传入描述的长度。但是在完成某些处理之前,无法确定包的长度。 @robert你认为在那种情况下我应该再次尝试切换到非阻塞的SocketChannel吗?

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

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

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

到目前为止,我可以解决,如果你必须使用阻塞的SocketChannel并且你不知道你期望有多少字节,并且服务器没有发送流结束,那么就没有解决方案。