2011-09-05 37 views
1

我正在使用Java 1.6的即时通讯工作。 IM使用多线程 - 主线程,接收和ping。对于TCP/IP通信,我使用SocketChannel。从服务器接收更大的包似乎有问题。服务器,而不是一个发送一些包,这就是问题开始的地方。每前8个字节是告诉什么是什么类型的包,它有多大。这是我如何管理阅读:ReadableByteChannel挂在读取(bytebuffer)

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)它只是停下来,我不知道为什么,没有错误没有什么,甚至在一些时间和Im出于想法。我整个星期都在和这场战斗搏斗,我没有得到任何解决方案。我会感谢任何建议。谢谢。


感谢您的回复。是的,我正在使用阻塞SocketChannel,我尝试了非阻塞,但它疯狂和失控,所以我跳过了这个想法。关于我期望的字节数 - 这有点奇怪,因为它的头只给我一次大小,但是它的大小不是整个包的第一部分,其他部分根本不包含头字节。我无法预测它将有多少字节,原因是 - 具有255字节容量的描述。这就是为什么我创建变量好友在:public synchronized void readInStatus(ByteBuffer headBuffer) 这基本上是我的好友列表的长度和阅读每个字段之前我检查是否有足够的剩余字节,如果它不是,我做阅读() 。但是描述之前的最后一个字段是具有传入描述长度的整数。但是不可能确定封装的时间长度,直到完成一些处理。 @robert你认为我应该再次尝试在这种情况下切换到非阻塞SocketChannel吗?

回答

1

问题很可能是您发送的字节数比您要读取的字节数少。你可能错过了写点东西,按错误的顺序写东西,误读了尺寸字段或类似的东西。

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

+0

感谢您的快速回复。不过,我认为我做得很对,一切都按照它的想法进行。我在发送前检查了缓冲区的大小及其正确性。发送只需要两个变量int和byte,所以它非常简单。联系人存储在Hashtable中,我从上到下迭代。 –

+0

问题是接收缓冲区的限制。您可以清除它,当您将其设置为“大小”大小时,请阅读,清除,然后根据大小设置缓冲区限制,然后以相同方式再次读取数据。我是BTW网络工程师。 –

0

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

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

只要我能解决问题,如果你必须使用一个阻塞的SocketChannel,并且你不知道你期望有多少字节,并且服务器不会发送流结束,那么就没有解决方案。

+0

目前无法使方法(ii)正常工作。即使当我移动位置时仍然阻塞,使得要读取的可用字节数不超过buffer.remaining() –