我正在为能够传输文件和结构化消息的协议(protocol)编写 netty 管道。文件传输通过结构化消息(握手)启动,后跟代表文件的字节流。
传入文件的确切消息流如下所示(服务器是我的 netty 实现,客户端是另一个软件):
+---------+ +---------+
| Client | | Server |
+---------+ +---------+
| |
| Connect (1) |
|--------------------------------------------->|
| |
| Handshake to announce incoming file (2) |
|--------------------------------------------->|
| |
| Acknowledge file transfer (3) |
|<---------------------------------------------|
| |
| Send file (4) |
|--------------------------------------------->|
协议(protocol)消息如下所示:
+---------+----------------+----------------+
| 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
写入其中。
这在原则上是可行的,唯一的问题是每个文件在开头恰好错过了 5 个字节。
我有两个问题:
1) 如果我将 LoggingHandler
作为管道中的第一个处理程序,我能否确保记录套接字上的所有流量,无论我的解码器是否有问题?
2) 当调用 ctx.channel().writeAndFlush()
时,它是否只刷新“出站”缓冲区,这意味着我可以确定我没有刷新任何我没有消耗的字节来自入站缓冲区了吗?
最佳答案
会不会是您忘记在每个 switch case 的语句末尾放置一个 break;
?现在来看,从第一个 LENGTH
读取开始,它会下降到类型和 token 读取,获得 5 个字节太多: boolean 值(一个字节)和 token (读取为 unsigned int,4字节)。
关于java - 为什么我在我的 netty 管道解码期间恰好丢失了 5 个字节?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43518543/