基於C#的通訊協議封包(附程式碼)
接上一篇《基於.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位,不足用空格填充) |
對應的封裝程式碼:
using System;
using MonitorLib.Utility;
namespace MonitorLib.Protocol
{
///
/// AbstractBase 的摘要說明。
///
[Serializable]
public abstract class AbstractBase
{
protected byte[] initValue;
public Head header;
public AbstractBase()
{
}
public virtual byte[] ToBytes()
{
return null;
}
}
}
using System;
using System.Text;
using MonitorLib.Utility;
namespace MonitorLib.Protocol
{
///
/// Bind訊息
///
[Serializable]
public class Bind : AbstractBase
{
private string loginName;
private string loginPassword;
///
/// 初始Bind命令的訊息頭
///
/// 序列號
public Bind(Sequence seq)
{
header = new Head(Command.MOT_BIND);
header.NodeID = seq.Node;
header.TimeID = seq.Time ;
header.SequenceID = seq.Value ;
header.Length = Head.HeaderLength + 16 + 16;
}
public Bind(byte[] receive)
{
initValue = new byte[receive.Length];
receive.CopyTo(initValue,0);
}
///
/// 登入名
///
public string LoginName
{
get
{
return Encoding.ASCII.GetString(initValue,20,16);
}
set
{
loginName = value;
}
}
///
/// 密碼
///
public string LoginPassword
{
get
{
return Encoding.ASCII.GetString(initValue,36,16);
}
set
{
loginPassword = value;
}
}
///
/// 把訊息結構轉換成位元組陣列
///
///
public override byte[] ToBytes()
{
byte[] retValue = new byte[this.header.Length];
uint index = 0;
//填充訊息頭
header.ToBytes().CopyTo(retValue,index);
index += Head.HeaderLength;
Encoding.ASCII.GetBytes(loginName).CopyTo(retValue,index);
//移位16位, 填充密碼
index += 16;
Encoding.ASCII.GetBytes(loginPassword).CopyTo(retValue,index);
return retValue;
}
}
///
/// Bind應答結構
///
[Serializable]
public class Bind_Resp : AbstractBase
{
private uint result;
///
/// 建構函式,把接收的位元組陣列複製到initValue
///
/// 從網路上接收到的位元組陣列
public Bind_Resp(byte[] receive)
{
initValue = new byte[receive.Length];
receive.CopyTo(initValue,0);
}
public Bind_Resp(Sequence seq)
{
header = new Head(Command.MOT_BIND_RESP);
header.NodeID = seq.Node;
header.TimeID = seq.Time ;
header.SequenceID = seq.Value ;
header.Length = Head.HeaderLength + 4;
}
///
/// bind 執行命令是否成功,0-成功。其它:錯誤碼。
///
public uint Result
{
get
{
return Convert.ToUInt32(initValue[20].ToString());
}
set
{
result = value;
}
}
public override byte[] ToBytes()
{
byte[] retValue = new byte[header.Length];
header.ToBytes().CopyTo(retValue,0);
BitConverter.GetBytes(result).CopyTo(retValue,20);
return retValue;
}
}
}
除了這種協議封裝方法外,還有一種直接利用 .NET 的位元組流操作類來編解碼,例如 ICMP 協議的封包程式碼:
1 public class ICMPHDR
2 {
3 private byte mType;
4 public byte Type
5 {
6 get{ return mType; }
7 set{ mType = value; }
8 }
9
10 private byte mCode = 0;
11 public byte Code
12 {
13 get{ return mCode; }
14 set{ mCode = value; }
15 }
16
17 private ushort mChecksum = 0;
18 public ushort Checksum
19 {
20 get{ return mChecksum; }
21 set{ mChecksum = value; }
22 }
23
24 private ushort mID;
25 public ushort ID
26 {
27 get{ return mID; }
28 set{ mID = value; }
29 }
30
31 private ushort mSeq;
32 public ushort Seq
33 {
34 get{ return mSeq; }
35 set{ mSeq = value; }
36 }
37
38 private ulong mtmSend;
39 public ulong tmSend
40 {
41 get{ return mtmSend; }
42 set{ mtmSend = value; }
43 }
44
45 private int mnTaskId;
46 public int nTaskId
47 {
48 get{ return mnTaskId; }
49 set{ mnTaskId = value; }
50 }
51
52 public void Encode(BinaryWriter writer)
53 {
54 writer.Write(Type);
55 writer.Write(Code);
56 writer.Write((UInt16)Checksum);
57 writer.Write((UInt16)ID);
58 writer.Write((UInt16)Seq);
59 writer.Write((UInt32)tmSend);
60 writer.Write(nTaskId);
61 }
62
63 public void Decode(BinaryReader reader)
64 {
65 Type = reader.ReadByte();
66 Code = reader.ReadByte();
67 Checksum = reader.ReadUInt16();
68 ID = reader.ReadUInt16();
69 Seq = reader.ReadUInt16();
70 tmSend = reader.ReadUInt32();
71 nTaskId = reader.ReadInt32();
72 }
73
74 public uint Sum()
75 {
76 uint sum = 0;
77 sum += (ushort)(Type + (Code << 8));
78 sum += (ushort)ID;
79 sum += (ushort)Seq;
80 sum += (ushort)tmSend;
81 sum += (ushort)(tmSend >> 16);
82 sum += (ushort)nTaskId;
83 sum += (ushort)(nTaskId >> 16);
84 return sum;
85 }
86 }
以上介紹了用C#是如何對自定義的通訊協議封裝的過程。 如有不同的處理方法的朋友,歡迎評論,一起探討一下。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-608424/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C#實現聯通簡訊Sgip協議程式原始碼C#協議原始碼
- 通訊協議協議
- 基於byte[]的HTTP協議頭分析程式碼HTTP協議
- Redis 通訊協議Redis協議
- HTTP通訊協議HTTP協議
- Mysql通訊協議MySql協議
- MQ通訊協議MQ協議
- web通訊協議Web協議
- 基於 Netty 的可插拔業務通訊協議的實現「1」協議描述及基本訊息物件設計Netty協議物件
- Java:基於TCP協議網路socket程式設計(實現C/S通訊)JavaTCP協議程式設計
- 基於隱私保護技術的DNS通訊協議介紹DNS協議
- Python 基於 TCP 傳輸協議的網路通訊實現PythonTCP協議
- HTTP協議的通訊框架HTTP協議框架
- WLAN常用的通訊協議協議
- 基於Netty實現自定義訊息通訊協議(協議設計及解析應用實戰)Netty協議
- 基於XMPP協議開發Android即時通訊軟體協議Android
- 基於XMPP協議Android即時通訊開源應用協議Android
- Android之基於XMPP協議即時通訊軟體(一)Android協議
- 網路通訊協議協議
- Dubbo-通訊協議協議
- 串列埠通訊協議串列埠協議
- 網路通訊協議基礎(ISIS)——入門協議
- 網路通訊協議-ICMP協議詳解!協議
- 網路通訊協議-TCP協議詳解!協議TCP
- 網路通訊協議-HTTP協議詳解!協議HTTP
- 網路通訊協議-SMTP協議詳解!協議
- C#輕量級通通訊元件StriveEngine —— C/S通訊開源demo(2) —— 使用二進位制協議 (附原始碼)C#元件協議原始碼
- C# wm6透過udp協議和pc通訊C#UDP協議
- HDFS原始碼解析系列一——HDFS通訊協議原始碼協議
- 輕量通訊協議 --- MQTT協議MQQT
- 通訊協議:HTTP、TCP、UDP協議HTTPTCPUDP
- 15. SPI通訊協議協議
- IIC通訊協議筆記協議筆記
- SPI通訊協議筆記協議筆記
- 陌陌通訊協議的學習協議
- 在HTTP協議下使用JSON在flash和C# web程式之間通訊HTTP協議JSONC#Web
- 倍福ADS協議通訊協議
- 通訊協議之序列化協議