| 方式 | 命名空間 | 格式 | 資料大小 | 相容性 | 適用場景 |
| Xml 序列化 | System.Xml.Serialization | XML | 中 | 高 (跨平台) | 異構系統整合 |
| Binary 序列化 | System.Runtime.Serialization.Formatters.Binary | 二進位 | 最小 | 低 (.NET 專用) | .NET 內部高效通訊 |
| SOAP 序列化 | System.Runtime.Serialization.Formatters.Soap | XML (SOAP) | 大 | 高 (標準協定) | Web Service、跨平台 |
本文以 SOAP 序列化為例示範,其他方式僅需更換 Formatter。
實體類別定義
[Serializable]
public class SocketData
{
public OperateType OperateType { get; set; }
public OperateInfo OperateInfo { get; set; }
public string ConnString { get; set; }
public string ClientIP { get; set; }
public string ServerIP { get; set; }
public SocketData(OperateType type, OperateInfo info, string conn, string clientIP, string serverIP)
{
OperateType = type; OperateInfo = info; ConnString = conn; ClientIP = clientIP; ServerIP = serverIP;
}
}
public enum OperateType { Web = 0, Upgrade, Transfer }
public enum OperateInfo { Send = 0, Error, Success, SendAgain }
關鍵:類別必須標記 [Serializable] 屬性。
訊息邊界處理:長度前綴法
TCP 是串流協定,無訊息邊界。採用「先傳 4 位元組長度,再傳資料」策略:
// 發送端
int msgSize = (int)mem.Length;
byte[] sizeBytes = BitConverter.GetBytes(msgSize); // int → 4 bytes
stream.Write(sizeBytes, 0, 4); // 先傳長度
stream.Write(data, 0, msgSize); // 再傳資料
// 接收端
byte[] sizeBuf = new byte[4];
stream.Read(sizeBuf, 0, 4);
int msgSize = BitConverter.ToInt32(sizeBuf, 0); // 讀取長度
// 循環讀取完整資料
byte[] buffer = new byte[1024];
int offset = 0;
while (msgSize > 0)
{
int read = stream.Read(buffer, offset, Math.Min(buffer.Length, msgSize));
mem.Write(buffer, offset, read);
offset += read;
msgSize -= read;
}
完整發送/接收實作
發送方法
public static void Send(NetworkStream stream, SocketData data)
{
IFormatter formatter = new SoapFormatter();
using (MemoryStream mem = new MemoryStream())
{
formatter.Serialize(mem, data); // 序列化到記憶體流
byte[] payload = mem.GetBuffer();
int size = (int)mem.Length;
byte[] sizeBytes = BitConverter.GetBytes(size);
stream.Write(sizeBytes, 0, 4); // 寫入長度 (4 bytes)
stream.Write(payload, 0, size); // 寫入資料
stream.Flush();
}
}
接收方法
public static SocketData Receive(NetworkStream stream)
{
// 讀取長度
byte[] sizeBuf = new byte[4];
if (stream.Read(sizeBuf, 0, 4) != 4) return null;
int size = BitConverter.ToInt32(sizeBuf, 0);
if (size <= 0) return null;
// 讀取完整資料
using (MemoryStream mem = new MemoryStream())
{
byte[] buffer = new byte[1024];
int remaining = size;
while (remaining > 0)
{
int read = stream.Read(buffer, 0, Math.Min(buffer.Length, remaining));
if (read == 0) throw new IOException("連線中斷");
mem.Write(buffer, 0, read);
remaining -= read;
}
// 反序列化
mem.Position = 0;
IFormatter formatter = new SoapFormatter();
return (SocketData)formatter.Deserialize(mem);
}
}
切換序列化方式
// Binary 序列化 (最小、最快)
IFormatter formatter = new BinaryFormatter();
// Xml 序列化 (需無參數建構函式、public 屬性)
XmlSerializer serializer = new XmlSerializer(typeof(SocketData));
實務建議
- 效能優先:.NET 內部通訊用
BinaryFormatter
- 相容性優先:異構系統、Web Service 用
SoapFormatter 或 XmlSerializer
- 安全性:
BinaryFormatter 有反序列化風險,不可信資料請用 Json.NET 或 protobuf-net
- 版本容忍:加入
[OptionalField]、實作 ISerializable 控制版本演進
原始碼下載
本文配套原始碼
- 上一篇文章: Socket 網路程式設計學習筆記 (6):使用執行緒池提升效能 (完結篇)
- 下一篇文章: Socket 網路程式設計學習筆記 (4):TCP 訊息邊界處理三大策略
Copyright ©
程式碼之家 All Rights Reserved