我已经使用Netty 3.3.1-Final了3周。
我的协议有3个步骤,每个步骤需要一个不同的FrameDecoder:
读取参数
传输一些数据
数据管道相互关闭
我经历了很多我无法理解的“阻塞”问题。终于在我看来,阅读org.jboss.netty.example.portunification示例时,尝试动态更改FrameDecoder时遇到了一些缓冲区问题:一个FrameDecoder的缓冲区(可能)在为下一个更改时不为空。 ..
有没有办法在Netty中轻松地做到这一点?我必须更改我的协议吗?我是否需要编写一个大型FrameDecoder并管理状态?
如果是这样,如何避免具有通用子部分的不同协议之间的代码重复(例如“读取参数”)?
今天,我想到了FrameDecoderUnifier(以下代码)的想法,目的是一种热添加和删除一些FrameDecoder的方法,您怎么看?
谢谢你的帮助!
雷诺
----------- FrameDecoderUnifier类--------------
/**
* This FrameDecoder is able to forward the unused bytes from one decoder to the next one. It provides
* a safe way to replace a FrameDecoder inside a Pipeline.
* It is not safe to just add and remove FrameDecoder dynamically from a Pipeline because there is a risk
* of unread bytes inside the buffer of the FrameDecoder you wan't to remove.
*/
public class FrameDecoderUnifier extends FrameDecoder {
private final Method frameDecoderDecodeMethod;
volatile boolean skip = false;
LastFrameEventHandler eventHandler;
LinkedList<Entry> entries;
Entry entry = null;
public FrameDecoderUnifier(LastFrameEventHandler eventHandler) {
this.eventHandler = eventHandler;
this.entries = new LinkedList<Entry>();
try {
this.frameDecoderDecodeMethod = FrameDecoder.class.getMethod("decode", ChannelHandlerContext.class, Channel.class, ChannelBuffer.class);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
} catch (SecurityException ex) {
throw new RuntimeException(ex);
}
}
public void addLast(FrameDecoder decoder, LastFrameIdentifier identifier) {
entries.addLast(new Entry(decoder, identifier));
}
private Object callDecode(FrameDecoder decoder, ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
return frameDecoderDecodeMethod.invoke(decoder, ctx, channel, buffer);
}
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
if (entry == null && !entries.isEmpty()) {
entry = entries.getFirst();
}
if (entry == null) {
return buffer; //No framing, no decoding
}
//Perform the decode operation
Object obj = callDecode(entry.getDecoder(), ctx, channel, buffer);
if (obj != null && entry.getIdentifier().isLastFrame(obj)) {
//Fire event
eventHandler.lastObjectDecoded(entry.getDecoder(), obj);
entry = null;
}
return obj;
}
/**
* You can use this interface to take some action when the current decoder is changed for the next one.
* This can be useful to change some upper Handler in the pipeline.
*/
public interface LastFrameEventHandler {
public void lastObjectDecoded(FrameDecoder decoder, Object obj);
}
public interface LastFrameIdentifier {
/**
* True if after this frame, we should disable this decoder.
* @param obj
* @return
*/
public abstract boolean isLastFrame(Object decodedObj);
}
private class Entry {
FrameDecoder decoder;
LastFrameIdentifier identifier;
public Entry(FrameDecoder decoder, LastFrameIdentifier identifier) {
this.decoder = decoder;
this.identifier = identifier;
}
public FrameDecoder getDecoder() {
return decoder;
}
public LastFrameIdentifier getIdentifier() {
return identifier;
}
}
}
最佳答案
我认为,应避免使用根据某些状态切换内部解码器并动态添加/删除上层处理程序的帧解码器,因为
难以理解/调试代码
处理程序没有明确定义的职责(这就是为什么要正确删除/添加处理程序?一个处理程序应处理一种或多种(相关)类型的协议消息,而没有许多处理程序处理同一类型的消息)
理想情况下,帧解码器仅提取协议帧,而不基于状态对帧进行解码(此处,帧解码器可以具有内部解码器链来解码帧,并使用已解码的消息触发MessageEvent,上面的处理程序可以对已解码的消息做出反应)。
更新:在这里我考虑了一个协议,其中每个消息可以具有唯一的标记/标识符,并且消息的末尾被清楚地标记(例如Tag Length Value帧格式)