傳統 Web Service 與 .NET Remoting 採用 RPC(遠端程序呼叫)模型,開發者彷彿呼叫本地函式,底層卻隱藏了序列化、網路傳輸、反序列化等複雜細節。而 Socket 程式設計則徹底改變了思維模式:一切皆訊息。資料的收發不再透過回傳值或 out 參數,而是透過明確的訊息收發,這賦予了伺服器主動推送資料給用戶端的能力(如股票即時行情、遊戲狀態同步)。
開放網路埠口即代表潛在攻擊面。惡意用戶可能發送異常封包導致服務當機或嘗試滲透系統。生產環境務必部署於防火牆後方,並實作輸入驗證、速率限制與認證授權機制。
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
public class SimpleTcpServer
{
public static void Main()
{
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
TcpListener listener = new TcpListener(localAddr, 9999);
Console.WriteLine("等待連線中...");
listener.Start();
using (Socket socket = listener.AcceptSocket())
using (NetworkStream stream = new NetworkStream(socket))
using (BinaryReader reader = new BinaryReader(stream))
using (BinaryWriter writer = new BinaryWriter(stream))
{
Console.WriteLine("用戶端已連線。");
int number = reader.ReadInt32();
number++;
writer.Write(number);
Console.WriteLine($"處理完成:{number}");
}
}
}
127.0.0.1 僅允許本機連線,跨機器需綁定 IPAddress.Any (0.0.0.0) 或具體區網 IP。為了傳遞結構化物件而非原始位元組,我們定義訊息類型列舉與序列化介面:
public enum MessageType : int
{
RequestEmployee = 1,
Employee = 2
}
public interface ISocketMessage
{
void Serialize(BinaryWriter writer);
}
public class RequestEmployee : ISocketMessage
{
public int EmployeeId { get; }
public RequestEmployee(int id) => EmployeeId = id;
// 反序列化建構子
public RequestEmployee(BinaryReader reader) => EmployeeId = reader.ReadInt32();
public void Serialize(BinaryWriter writer)
{
writer.Write((int)MessageType.RequestEmployee); // 先寫入訊息類型標識
writer.Write(EmployeeId);
}
}
public class Employee : ISocketMessage
{
public string Name { get; }
public string Address { get; }
public Employee(string name, string address)
{
Name = name;
Address = address;
}
public Employee(BinaryReader reader)
{
Name = reader.ReadString();
Address = reader.ReadString();
}
public void Serialize(BinaryWriter writer)
{
writer.Write((int)MessageType.Employee);
writer.Write(Name);
writer.Write(Address);
}
}
// 發送請求
RequestEmployee req = new RequestEmployee(15);
req.Serialize(writer);
// 接收回應
MessageType msgType = (MessageType)reader.ReadInt32();
switch (msgType)
{
case MessageType.Employee:
Employee emp = new Employee(reader);
Console.WriteLine($"員工資訊:{emp.Name} - {emp.Address}");
break;
default:
Console.WriteLine($"未知訊息類型:{msgType}");
break;
}
自訂二進位協議雖需手寫序列化碼,但掌控度最高、效能最優、跨平台相容性最佳。
連線建立後,雙方皆持有 NetworkStream,通訊程式碼幾乎完全對稱。差別僅在於連線發起方式(Connect vs Accept),這為後續實作雙向推播奠定基礎。
下篇將深入探討: