玩无线模块有点上瘾了,如果能实现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

激光

可见光