微网协议去掉尾部的TTL和Retry,由标识位来承载

Zigbee有粘包情况,但是因为TTL/Retry的存在,导致后半截指令解析错误
This commit is contained in:
nnhy 2015-10-07 14:50:50 +00:00
parent 21f824ddad
commit eed7f61d93
4 changed files with 37 additions and 55 deletions

View File

@ -84,10 +84,22 @@ uint Controller::Dispatch(ITransport* port, ByteArray& bs, void* param, void* pa
Stream ms(buf, len);
while(ms.Remain() >= control->MinSize)
{
#if MSG_DEBUG
uint p = ms.Position();
buf = ms.Current();
len = ms.Remain();
#endif
// 如果不是有效数据包,则直接退出,避免产生死循环。当然,也可以逐字节移动测试,不过那样性能太差
if(!control->Dispatch(ms, NULL))
{
#if MSG_DEBUG
// 兼容性处理如果0x00 0x01 0x02开头则重新来一次
if(buf[0] == 0x00 || buf[0] == 0x01 || buf[0] == 0x02 || buf[0] == 0x03)
{
ms.SetPosition(p + 1);
continue;
}
msg_printf("TinyNet::DispatchError[%d] ", len);
// 输出整条信息
Sys.ShowHex(buf, len, '-');

View File

@ -348,7 +348,7 @@ void SerialPort::OnRxHandler()
_task->NextTime = Time.Current() + 1;
_task->Enable = true;
// 如果系统调度器处于Sleep让它立马退出
//Task::Scheduler()->Sleeping = false;
Task::Scheduler()->Sleeping = false;
}
}

View File

@ -24,10 +24,6 @@ TinyMessage::TinyMessage(byte code) : Message(code)
memset(&Dest, 0, MinSize);
Crc = 0;
TTL = 0;
#if MSG_DEBUG
Retry = 1;
#endif
}
// 分析数据,转为消息。负载数据部分将指向数据区,外部不要提前释放内存
@ -36,7 +32,7 @@ bool TinyMessage::Read(Stream& ms)
// 消息至少4个头部字节、2字节长度和2字节校验没有负载数据的情况下
if(ms.Remain() < MinSize) return false;
const byte* p = ms.Current();
byte* p = ms.Current();
ms.Read(&Dest, 0, HeaderSize);
// 占位符拷贝到实际数据
@ -60,25 +56,15 @@ bool TinyMessage::Read(Stream& ms)
// 读取真正的校验码
Checksum = ms.Read<ushort>();
// 计算Crc之前需要清零TTL和Retry
byte fs = p[3];
TinyMessage* msg = (TinyMessage*)p;
msg->TTL = 0;
msg->Retry = 0;
// 连续的可以直接计算Crc16
Crc = Crc::Hash16(p, HeaderSize + Length);
// 后面可能有TTL
if(UseTTL)
{
// 必须严格检查,否则可能成为溢出漏洞
if(ms.Remain() > 0)
TTL = ms.Read<byte>();
else
TTL = 0;
}
#if MSG_DEBUG
// 调试诊断模式下该字段表示第几次重发
if(ms.Remain() > 0)
Retry = ms.Read<byte>();
else
Retry = 0;
#endif
// 还原数据
p[3] = fs;
return true;
}
@ -102,15 +88,19 @@ void TinyMessage::Write(Stream& ms) const
ms.Write((byte*)&Dest, 0, HeaderSize);
if(Length > 0) ms.Write(Data, 0, Length);
// 计算Crc之前需要清零TTL和Retry
byte fs = buf[3];
TinyMessage* msg = (TinyMessage*)buf;
msg->TTL = 0;
msg->Retry = 0;
p->Checksum = p->Crc = Crc::Hash16(buf, HeaderSize + Length);
// 还原数据
buf[3] = fs;
// 写入真正的校验码
ms.Write(Checksum);
// 后面可能有TTL
if(UseTTL && ms.Position() < 24) ms.Write(TTL);
#if MSG_DEBUG
if(ms.Position() < 24) ms.Write(Retry);
#endif
}
void TinyMessage::ComputeCrc()
@ -141,10 +131,6 @@ bool TinyMessage::Valid() const
uint TinyMessage::Size() const
{
uint len = MinSize + Length;
if(UseTTL) len++;
#if MSG_DEBUG
len++;
#endif
return len;
}
@ -304,11 +290,8 @@ bool TinyController::Valid(const Message& msg)
{
// 快速响应确认消息,避免对方无休止的重发
if(!tmsg.NoAck) AckResponse(tmsg);
#if MSG_DEBUG
msg_printf("重复消息 Reply=%d Ack=%d Src=0x%02x Seq=%d Retry=%d\r\n", tmsg.Reply, tmsg.Ack, tmsg.Src, tmsg.Sequence, tmsg.Retry);
#else
msg_printf("重复消息 Reply=%d Ack=%d Src=0x%02x Seq=%d\r\n", tmsg.Reply, tmsg.Ack, tmsg.Src, tmsg.Sequence);
#endif
return false;
}
_Ring.Push(seq);
@ -371,11 +354,7 @@ void TinyController::AckRequest(const TinyMessage& msg)
else
msg_printf("收到Reply确认 ");
#if MSG_DEBUG
msg_printf("Src=%d Seq=%d Cost=%dus Retry=%d\r\n", msg.Src, msg.Sequence, cost, msg.Retry);
#else
msg_printf("Src=%d Seq=%d Cost=%dus\r\n", msg.Src, msg.Sequence, cost);
#endif
return;
}
}
@ -402,10 +381,7 @@ void TinyController::AckResponse(const TinyMessage& msg)
#endif
bool rs = Controller::Send(msg2);
msg_printf("发送Ack确认包 Dest=0x%02x Seq=%d ", msg.Src, msg.Sequence);
#if MSG_DEBUG
msg_printf("Retry=%d ", msg.Retry);
#endif
msg_printf("发送Ack确认包 Dest=0x%02x Seq=%d Retry=%d ", msg.Src, msg.Sequence, msg.Retry);
if(rs)
msg_printf(" 成功!\r\n");
else
@ -483,8 +459,9 @@ void TinyController::Loop()
//debug_printf("重发消息 Dest=0x%02X Seq=%d Times=%d\r\n", node.Data[0], node.Sequence, node.Times);
// 第6个字节表示长度
TinyMessage* msg = (TinyMessage*)node.Data;
msg->Retry++;
// 最后一个附加字节记录第几次重发
if(node.Length > TinyMessage::MinSize + msg->Length) node.Data[node.Length - 1] = node.Times;
//if(node.Length > TinyMessage::MinSize + msg->Length) node.Data[node.Length - 1] = node.Times;
//ByteArray bs(node.Data, node.Length);
//bs.Show(true);
#endif

View File

@ -23,8 +23,8 @@ public:
byte Dest; // 目的地址
byte Src; // 源地址
byte _Code; // 功能代码
byte Flags:3; // 标识位。也可以用来做二级命令
byte UseTTL:1; // 使用TTL。具体TTL值位于数据包最后
byte Retry:2; // 标识位。也可以用来做二级命令
byte TTL:2; // 路由TTL。最多3次转发
byte NoAck:1; // 是否不需要确认包
byte Ack:1; // 确认包
byte _Error:1; // 是否错误
@ -34,12 +34,6 @@ public:
byte _Data[32]; // 数据部分
ushort Checksum;// 16位检验和
// 可选的附加数据紧跟在头数据后面,可能直接读取内存区域
byte TTL; // 路由生命周期。为方便路由不参与Crc校验
#if DEBUG
byte Retry; // 调试诊断模式下该字段表示第几次重发
#endif
// 负载数据及校验部分,并非内存布局。
ushort Crc; // 整个消息的Crc16校验计算前Checksum清零
@ -95,7 +89,6 @@ public:
uint Ack; // 总成功。有多少消息收到确认,每条消息仅计算一次确认
uint Bytes; // 总字节数。成功发送消息的字节数
uint Cost; // 总开销ms。成功发送消息到收到确认所花费的时间
//uint Retry; // 总重试次数
int Receive;// 收到消息数
TinyStat()