我正在为一个能够传输文件和结构化消息的协议写一个netty管道。文件传输是通过一个结构化的消息(握手)开始的,然后是一个代表该文件的字节流。为什么我在netty管道中解码期间正好丢失了5个字节?
传入文件的确切消息流看起来像这样(服务器是我的网状实现,客户其他软件):
+---------+ +---------+
| Client | | Server |
+---------+ +---------+
| |
| Connect (1) |
|--------------------------------------------->|
| |
| Handshake to announce incoming file (2) |
|--------------------------------------------->|
| |
| Acknowledge file transfer (3) |
|<---------------------------------------------|
| |
| Send file (4) |
|--------------------------------------------->|
协议的消息是这样的:
+---------+----------------+----------------+
| Length | Type | Actual Message |
| 4 bytes | 1 byte | N bytes |
+---------+----------------+----------------+
在在握手消息的情况下,Actual Message
只包含单个Long
值,即token
。
这里的ReplayingDecoder:
public class ProtocolDecoder extends ReplayingDecoder<State> {
private Integer msgType;
private Long token;
public enum State {
LENGTH,
MSG_TYPE,
TOKEN
}
public ProtocolDecoder() {
super(State.LENGTH);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
switch (state()) {
case LENGTH:
// (2) read the message length from the handshake
long messageLength = in.readUnsignedIntLE();
checkpoint(State.MSG_TYPE);
case MSG_TYPE:
// (2) read the message type from the handshake
msgType = in.readBoolean();
checkpoint(State.TOKEN);
case TOKEN:
try {
// (2) read the token from the handshake
token = in.readUnsignedIntLE();
// (3) write back the acknowledgement
ctx.channel().writeAndFlush(new Acknowledgement(token));
// (4) done reading the protocol message
// now switch to the file protocol
ctx.pipeline().addLast(new FileInboundHandler());
// write everything that is still in the buffer to the
// modified pipeline
ByteBuf rest = in.readBytes(super.actualReadableBytes());
out.add(rest);
// remove the protocol handshake decoder and pass
// the rest of this channel to the `FileInboundHandler`
ctx.pipeline().remove(this);
} finally {
reset();
}
break;
default:
throw new Error("Shouldn't reach here.");
}
}
private void reset() {
token = null;
msgType = null;
}
的FileInboundHandler
简单地创建一个文件,并把所有ByteBuf
s到它。
原则上这是工作的,唯一的问题是每个文件在开始时精确地错过5个字节。
我有2个问题:
1)如果我把一个LoggingHandler
作为第一个处理的管道,我可以肯定的是,插座上的所有通信将被记录,不管我的解码器是越野车?
2)当调用ctx.channel().writeAndFlush()
时,它是否只刷新“出站”缓冲区,这意味着我可以确定没有刷新我没有从入站缓冲区消耗的任何字节吗?
我已经在解码器的先前版本中尝试过了(在每种情况下放一个'break;'),但行为是一样的。另外[ReplayingDecoder文档](https://github.com/firebase/netty/blob/master/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java#L149-L199)确实使用通过机制也落下 - 我认为这是检查点背后的想法。 – pulse00
@ pulse00嗯,似乎有道理。毕竟,您只需一次读取标题,然后将解码器从流水线中移除。那个文件的处理程序呢,它包含了这个协议头文件吗? –