我正在使用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吗?
答案 0 :(得分:1)
问题很可能是您发送的字节数少于您尝试读取的字节数。你可能错过了写错东西,写错了顺序的东西,误读了大小字段或类似的东西。
我想我会通过添加跟踪代码来计算和记录读写的字节数,名义包装大小等来攻击这个问题。然后运行,并比较跟踪以查看事情开始不同步的地方。
答案 1 :(得分:0)
如果您正在使用阻塞SocketChannel,则read将阻塞,直到填充缓冲区或服务器传递流结束。对于具有连接保持活动状态的服务器,服务器不会发送流结束 - 它只会停止发送数据,并且读取将无限期挂起或直到超时。
你可以: (i)尝试使用非阻塞SocketChannel,重复读取,直到读取传递0个字节(但要注意0个字节不一定意味着流的结束 - 它可能意味着中断)或 (ii)如果您必须使用阻止版本,并且您知道服务器期望的字节数,例如从头部开始,当要读取的字节数小于buffer.capacity()时,移动缓冲区上的位置和/或限制,以便在读取之前仅在缓冲区中留下所需的空间。我现在正在使用这个解决方案。如果它适合您,请告诉我!
到目前为止,我可以解决,如果你必须使用阻塞的SocketChannel并且你不知道你期望有多少字节,并且服务器没有发送流结束,那么就没有解决方案。