1

我正在使用 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 字节的描述。这正是我在以下位置创建变量好友的原因:public synchronized void readInStatus(ByteBuffer headBuffer) 这基本上是我的好友列表的长度,在读取每个字段之前,我正在检查是否还有足够的字节,如果没有的话,我会read(). 但是描述之前的最后一个字段是带有传入描述长度的整数。但在完成某些处理之前,无法确定包裹的长度。@robert 你认为我应该在那种情况下再次尝试切换到非阻塞 SocketChannel 吗?

4

2 回答 2

1

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

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

于 2011-09-05T12:43:31.190 回答
0

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

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

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

于 2011-09-07T09:27:44.897 回答