在 Socket 應用程式中傳遞複雜資料時,常見做法是將各欄位轉字串、用分隔符 (如 `#`) 連接發送。但這有隱患:若使用者輸入內容包含分隔符,解析端將錯誤切割。本文示範使用 序列化/反序列化 (以 JSON 為例) 封裝強型別物件傳輸,徹底解決分隔符衝突、型別保真、擴充性等問題。
[DataContract(Name = "product")]
public class Product {
[DataMember(Name = "product_no")] public string ProductNo { get; set; }
[DataMember(Name = "product_name")] public string ProductName { get; set; }
[DataMember(Name = "product_price")] public decimal ProductPrice { get; set; }
}
private byte[] SerializeObject(T t) where T : class {
using (var ms = new MemoryStream()) {
var ss = new DataContractJsonSerializer(t.GetType());
ss.WriteObject(ms, t);
return ms.ToArray();
}
}
// 非同步接受連線
listener.BeginAcceptTcpClient(ar => {
var prd = (Product)ar.AsyncState;
var client = listener.EndAcceptTcpClient(ar);
var stream = client.GetStream();
var data = SerializeObject(prd);
var len = BitConverter.GetBytes(data.Length);
stream.Write(len, 0, 4); // 先寫長度
stream.Write(data, 0, data.Length); // 再寫資料
client.Close();
listener.BeginAcceptTcpClient(...); // 繼續監聽
}, prd);
private T DeserializeObject(byte[] buffer) where T : class {
using (var ms = new MemoryStream(buffer)) {
ms.Position = 0;
var ss = new DataContractJsonSerializer(typeof(T));
return (T)ss.ReadObject(ms);
}
}
private async void OnClick(object sender, RoutedEventArgs e) {
var socket = new StreamSocket();
await socket.ConnectAsync(new HostName(txtHost.Text), txtPort.Text);
var reader = new DataReader(socket.InputStream) { ByteOrder = ByteOrder.LittleEndian };
await reader.LoadAsync(4); // 讀長度
int len = reader.ReadInt32();
await reader.LoadAsync((uint)len); // 讀資料
var buf = reader.ReadBuffer(len);
var prd = DeserializeObject(buf.ToArray());
tbContent.Text = $"產品編號:{prd.ProductNo}\n產品名稱:{prd.ProductName}\n產品單價:{prd.ProductPrice:C2}";
}
伺服器輸入產品資料 → 點擊「開始偵聽」 → 客戶端輸入 IP/Port → 點擊連線 → 即時顯示反序列化後的產品物件,格式完整、型別正確 (decimal 保留小數、日期格式等)。
| 傳統字串拼接 | 序列化方案 |
|---|---|
| 分隔符衝突風險 | 無分隔符,結構化資料 |
| 型別資訊流失 (全變字串) | 完整保留型別 (int/decimal/date/陣列/巢狀物件) |
| 手動解析易出錯 | 框架自動處理,程式碼極簡 |
| 擴充欄位需改協定 | 新增屬性即可,舊版相容 (DataMember 預設可選) |
| 跨語言困難 | JSON/Protobuf 跨平台通用 |
DataContract 而非 Serializable,支援選擇性成員、順序無關、版本演進。Protobuf-net、MessagePack、MemoryPack 等二進位序列化器。將序列化引入 Socket 傳輸,是從「手工拼裝」邁向「工業化標準化」的關鍵一步。