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