接上一篇《基于.NET技术的监控系统应用分析》中所描述的数据通信协议设计,我们来看一下在C#中是怎么对自定义协议进行封包的?我们知道基于流的数据协议的特点:发送和接收到的数据都是连续的流。每次网络I/O操作的流长度不确定,也就是无法知道每次接收到的数据是一个完整的数据包。同样,主机发送一个数据包也会根据网络的实际情况执行若干次。所以我们对这类消息的编解码过程需要进行一个统一的封装。
重新回顾一下每个消息的结构:消息头 + 消息体。每次先发送出去的是消息头,然后是消息体。消息头里描述了这个数据包的类型,长度,序列号等信息。消息头的长度是固定的,消息体的长度是根据每个消息类型会有所的区别。
消息头的定义:
| 字段 | 长度(字节) | 类型 | 说明 |
| Length | 4 | Int | 消息的总长度(字节) |
| Command ID | 4 | Int | 命令ID |
| NodeID | 4 | Int | 结点ID |
| TimeID | 4 | Int | 时间戳 |
| SequenceID | 4 | Int | 递增序列号 |
对应的封装代码:
上面只是一个消息头,要成为一个完整的消息,一般还必须包含消息体(当然你也可以根据需要仅发送一个消息头的数据,作为特殊用途,例如自定义的心跳包)。举个例子:客户机与服务器连接上后,它通常会发送一个绑定(Bind) 消息给服务器端。例如:验证确认客户端的合法性。那么此时的Bind消息的格式是:
| 字段 | 长度(字节) | 类型 | 说明 |
| HEAD | | | 上面的消息头部 |
| loginName | 16 | string | 用户名(固定16位,不足用空格填充) |
| LoginPassword | 16 | string | 密码(固定16位,不足用空格填充) |
对应的封装代码:
}
}
}
}
除了这种协议封装方法外,还有一种直接利用 .NET 的字节流操作类来编解码,例如 ICMP 协议的封包代码:
以上介绍了用C#是如何对自定义的通信协议封装的过程。 如有不同的处理方法的朋友,欢迎评论,一起探讨一下。