Well, Im using netty framework java, to handle messages by concox equipment protocol, the messages received by my application seems to be different than the expected.
The message received is like
EF BF BD EF BF BD EF BF BD 0D 0A 78 78 1F 12 13 0A 1C 0F 12 1D EF BF BD
And the expected should be
78 78 23 12 10 03 1D 0F 17 12 C7 02 6B 6E 38 0C 39 71 00 0B 15 0E 01 CC 00 24 95 00 13 93 00 02 3D 7C 01 09 27 35 0D 0A
78 78 is the start bit 0D 0A is the stop bit
What this could be? We use this application base for a lot of protocols and they work The support says that could be a problem of buffer connection, but i don't know how could it be.
I can handle the problem of the start and stop bit at the wrong position. But the message expected stills much bigger.
The doc link is http://www.iconcox.in/images/tr-06-protocol.pdf
Our code
public abstract class ExtendedObjectDecoder implements ChannelUpstreamHandler {
@SuppressWarnings("rawtypes")
public void handleUpstream(
ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
if (!(evt instanceof MessageEvent)) {
ctx.sendUpstream(evt);
return;
}
MessageEvent e = (MessageEvent) evt;
Object originalMessage = e.getMessage();
System.out.println((String) originalMessage);
}
}
Pileline
@Override
public void initTrackerServers(List<TrackerServer> serverList) {
serverList.add(new TrackerServer(new ServerBootstrap()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(4096, "$", "\0"));
pipeline.addLast("stringEncoder", new StringEncoder());
pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new EquipProtocolDecoder(EquipProtocol.this));
}
});
serverList.add(new TrackerServer(new ConnectionlessBootstrap()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("stringEncoder", new StringEncoder());
pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new EquipProtocolDecoder(EquipProtocol.this));
}
});
}
Netty is designed to be a simplified layer over the actual protocol, and thus inherits many semantics from it.
One important semantic is that TCP is a stream based protocol, meaning that when you read any data, you get all the data in order, but the data might be spread out over multiple packets, or all data might be in the same packet.
Your code does not deal with this properly, and this is causing your issues.
What happened in your case is that the remote send 2 "protocol specific packets", and after reading, these packets where mixed up over "tcp packets"
Aaaaa Bbbbb Cccc Ddddd
Aaa aaaBbbbCccccDdddd
You need something to reconstruct the original messages again.
At the moment, your pipeline exists of a StringDecoder
, followed by your business handler. Unfortunately, StringDecoder
is not suited up to the task of handling raw incoming data, and could actually cause corruption when handling raw incoming data.
Fortunately for you, "tr-06" has a length field in every packet. (Detecting the presence of a start and stop bit is unreliable, since they could also be inside a packet) We can use this length field to decode the "stream of bytes" to "packets again. Lets gather some information the the documentation of the protocol:
iv.Data Packet Format
The communication is transferred asynchronously in bytes.The total length of packets is (10+N) Bytes
...
4.2.Packet Length
Length = Protocol Number + Information Content + Information Serial Number + Error Check, totally (5+N)Bytes, because the Information Content is a variable length field.
We eventually want to use a pre-build solution for "framing" the bytes, so lets read into the documentation of DelimiterBasedFrameDecoder
. We can see that the example of "3 bytes length field at the end of 5 bytes header, do not strip header" closely matches what we need, but isn't quite it. Lets calculate the correct data based on these examples:
We see in the protocol example that the length field comes after the start bit, and the start bit is 2 bytes long, so the offset becomes 2
lengthFieldOffset = 2
According to the documentation, the packet length field is 1, meaning that it supports packets up to 255 bytes in length
lengthFieldLength = 1
This is a hard one to calculate, Netty assumes that the packet length is all the bytes after the length fields is contained inside the packet, so lets calculate it from netties perspective, and see how it lines up.
Netty: 1 + N + 2 + 2 + 2
Protocol: 1 N + 2 + 2
lengthAdjustment = 2
We need to adjust the length field by 2
We don't want to strip any bytes (you might want to enable this later if you are not interested in the start and stop bits)
initialBytesToStrip = 0
Lets put this together:
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(255, 2, 1, 2, 0));
pipeline.addLast("stringEncoder", new StringEncoder());
pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new EquipProtocolDecoder(EquipProtocol.this));
}