在 C# 中進行網路程式設計時,System.Net.Sockets.Socket 是最底層的操作類別。雖然封裝良好,但要建立高效、穩定的 TCP 伺服器,仍需妥善處理執行緒、非同步 I/O、連線管理等議題。本文提出一個經典的「主執行緒 + 監聽執行緒 + 工作執行緒」三層架構,並附上關鍵程式碼片段。
Accept() 封包,接收新連線並建立 Connection 物件。private void btn_startServer_Click(object sender, EventArgs e)
{
Thread listenerThread = new Thread(new ThreadStart(WaitForConnect));
listenerThread.Start();
}
private void WaitForConnect()
{
SocketListener listener = new SocketListener(Convert.ToInt32(txt_port.Text));
listener.StartListening();
}
若直接在 UI 執行緒呼叫 Accept() 會導致介面凍結,因此必須另闢執行緒。
IPEndPoint localEP = new IPEndPoint(_ipAddress, _port);
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(localEP);
listener.Listen(20); // backlog = 20
while (true)
{
Socket connection = listener.Accept(); // 同步阻塞,直到有新連線
Logger.Log("Connect", connection.RemoteEndPoint.ToString());
// 為每個連線建立獨立執行緒
Connection conn = new Connection(connection);
Thread t = new Thread(new ThreadStart(conn.WaitForSendData));
t.Name = connection.RemoteEndPoint.ToString();
t.Start();
// 存入管理集合
Connections.Add(conn, t);
}
public void WaitForSendData()
{
while (true)
{
byte[] buffer = new byte[1024];
int bytesRec = _connection.Receive(buffer); // 同步阻塞等待資料
_lastConnectTime = DateTime.Now;
if (bytesRec == 0) // 對方關閉連線
{
Logger.Log("Close Connection", _connection.RemoteEndPoint.ToString());
break;
}
string data = Encoding.ASCII.GetString(buffer, 0, bytesRec);
// TODO: 處理業務邏輯
}
}
注意事項:
Receive 同樣為阻塞呼叫,需在獨立執行緒執行。Receive 直到讀完。// Listener 中的靜態集合
static public Hashtable Connections = new Hashtable(); // Connection -> Thread
// Connection 中記錄最後活動時間
private DateTime _lastConnectTime;
// WinForm 定時器定期掃描
foreach (DictionaryEntry entry in Connections)
{
Connection c = (Connection)entry.Key;
Thread t = (Thread)entry.Value;
if ((DateTime.Now - c.LastConnectTime).TotalMinutes > TIMEOUT)
{
c.Close();
t.Abort(); // 或優雅關閉
Connections.Remove(c);
}
}
SocketAsyncEventArgs (SAEA) 實現高效能 IOCP 模式。本文示範的同步阻塞 + 每連線一執行緒模型,邏輯清晰、易於除錯,適合中小型專案或快速原型。面對高併發場景,建議遷移至非同步/IOCP 架構。