Java链路层通信协议简单实现

玩无线模块有点上瘾了,如果能实现TCP通信应该很有成就感吧,因为几年前用 tun/tap 做过小工具,想当然地以为把无线收发的数据直接传输给 tun/tap 就可以了,结果往tun write数据时一直报错Invalid argument

结合TCP/IP协议思考了一下,我认为往tun设备write数据要求是一个完整的ip包,但是无线模块一次只能读到1个字节, tun不认识这种格式,所以Invalid argument !

为了验证我的想法,很快就写了个有完整性校验的收包发包工具,原理是发包时在包头加上MAGIC BYTES,接着是数据长度数据,最后是数据的校验和长度校验和,接收方如果接收到MAGIC BYTES,便知道这是一个包的开头,接着根据数据长度接收完整的包,最后校验,如果校验结果与包里的校验和不一致,则认为该包在传输过程中被破坏,直接丢弃整个包。其实这和TCP/IP中的包完整性校验几乎如出一辙。

数据结构

1
2
3
4
5
6
7
8
9
10
public class PacketFrame {
public static final byte[] MAGIC = { 5, -3, 126, 120, -18, 24, 92, 1 }; //可自定义,通讯双方必须一致
private final byte[] magic = new byte[MAGIC.length];
private short datalength; // 数据长度
private byte[] data; // 数据
private byte chksumlength; // 校验和长度
private byte[] chksum; // 校验和
}

发包代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void write(OutputStream out) throws IOException {
if (data.length == 0 || data.length > (Short.MAX_VALUE - Short.MIN_VALUE + 1))
throw new RuntimeException("data length must be 1-65536, current " + data.length);
if (chksumlength < 2 || chksumlength > 16)
throw new RuntimeException("chksumlength must be 2-16, current " + chksumlength);
System.arraycopy(MAGIC, 0, magic, 0, MAGIC.length);
out.write(magic);
datalength = (short) data.length;
out.write(ByteBuffer.allocate(2).putShort(datalength).array());
out.write(data);
out.write(new byte[]{chksumlength});
out.write(chksum = ArrayUtils.subarray(DigestUtils.md5(data), 0, chksumlength));
}

收包稍微复杂些,因为收到的字节可能乱序的,用ByteBuffer解析字节流过滤出有效数据。

完整的代码已开源, 结合 tun/tap 可实现TCP通信(已测试433MHz 315MHz zigbee) https://github.com/binaryer/rawip4j

效果

####433MHz模块9600波特率距离20米,隔2墙1窗1门,ping丢包率5%, wget 600bytes/s


激光

可见光