我创建了一个SocketChannel到远程服务器以在Tomcat上发送和接收消息。为了从远程计算机接收消息,我使用了一个专用于任务的线程(只有这个线程将从套接字读取,没有别的)。
当在SocketChannel接收到一些字节时(我继续在非阻塞模式下轮询SocketChannel以获取新数据),我首先读取4个字节以获取下一个消息的长度,然后从SocketChannel分配和读取x个字节然后将其解码并重建为一条消息。
以下是接收线程的代码:
@Override
public void run() {
while (true) { //Don't exit thread
//Attempt to read the size of the incoming message
ByteBuffer buf = ByteBuffer.allocate(4);
int bytesread = 0;
try {
while (buf.remaining() > 0) {
bytesread = schannel.read(buf);
if (bytesread == -1) { //Socket was terminated
}
if (quitthread) break;
}
} catch (IOException ex) {
}
if (buf.remaining() == 0) {
//Read the header
byte[] header = buf.array();
int msgsize = (0xFF & (int)header[0]) + ((0xFF & (int)header[1]) << 8)
+ ((0xFF & (int)header[2]) << 16) + ((0xFF & (int)header[3]) << 24);
//Read the message coming from the pipeline
buf = ByteBuffer.allocate(msgsize);
try {
while (buf.remaining() > 0) {
bytesread = schannel.read(buf);
if (bytesread == -1) { //Socket was terminated
}
if (quitthread) break;
}
} catch (IOException ex) {
}
parent.recvMessage(buf.array());
}
if (quitthread) {
break;
}
}
}
我从SocketChannel收到的第一个字节很好,我成功解码了该消息。但是,下次我从SocketChannel读取时,套接字会跳过大约100个字节,导致读取错误的字节并将其解释为长度,导致所有内容损坏。
代码有什么问题?没有其他线程正在从SocketChannel读取。
答案 0 :(得分:1)
您的括号已关闭,代码为:
(0xFF & ((int)header[1] << 8))
始终为0(与&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;
((0xFF & ((int)header[1])) << 8)
这会导致读取的消息字节数量不足,导致同步不匹配(而不是读取太多。)
编辑:现在你修复了上面的内容,我看不出有什么不妥。你能告诉我们第一条消息的长度和吃掉的确切字节数之间的关系吗?
根据显示的代码,我唯一的猜测是你编辑了可能影响schannel的示例中的一些行为,是否是其他地方引用的schannel?
如果该行:
ByteBuffer buf = ByteBuffer.allocate(4);
会超出导致您描述的行为的while
,但在您的示例代码中却不是。
答案 1 :(得分:0)
我认为当你说你在非阻塞模式下轮询套接字时,你的意思是你正在使用“标准”Selector.select()
方法吗?
当select返回并指示可以从套接字读取数据时,您应该只读取重新输入select()之前可用的字节数。如果read()返回-1,则表示没有更多字节可用于在缓冲区中立即读取 - 这并不意味着套接字已被关闭。因此我怀疑你在返回之前尝试完全填充缓冲区是不正确的。即使它确实有效,您的I / O线程也会在数据到达时不断旋转。特别是,看起来你只是忽略-1的返回值。
考虑重新设计代码以使用有限状态机方法。例如,我过去使用三态模型实现了这一点:IDLE,READ_MESSAGE_LENGTH和READ_MESSAGE。