.NET Framework 提供 System.Net 與 System.Net.Sockets 兩大命名空間封裝網路通訊細節。本文以最基礎的 TCP 同步阻塞模式 為例,逐步建構一對簡易的伺服器/用戶端主控台/WinForms 程式,釐清 TcpListener/TcpClient、NetworkStream、StreamReader/Writer 核心類別運作機制。
| 術語 | 說明 |
|---|---|
| 同步 | 發送方發送後等待接收方回應才繼續 (本文採用) |
| 非同步 | 發送後立即繼續,透過 Callback/await 處理回應 |
| 阻塞 | 呼叫 ReadLine() 等方法時,執行緒暫停直到有資料或逾時 |
| 非阻塞 | 立即回傳,需輪詢或事件通知 |
生產環境高併發建議使用 非同步非阻塞 (BeginAccept/EndAccept、async/await、SocketAsyncEventArgs),本文以同步阻塞為教學基礎。
TcpListener listener = new TcpListener(IPAddress.Any, 1234); // 監聽所有介面 1234 埠
listener.Start();
Socket clientSocket = listener.AcceptSocket(); // 阻塞直到客戶端連入
// 或使用更高層級:
TcpClient client = listener.AcceptTcpClient();
NetworkStream ns = client.GetStream(); // 或 new NetworkStream(clientSocket)
StreamReader reader = new StreamReader(ns, Encoding.UTF8);
StreamWriter writer = new StreamWriter(ns, Encoding.UTF8) { AutoFlush = true };
string msg = reader.ReadLine(); // 阻塞讀取一行 (以 \n 結尾)
writer.WriteLine("Server Echo: " + msg); // 寫入並自動 Flush
Thread serverThread = new Thread(() =>
{
while (true)
{
TcpClient client = listener.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(HandleClient, client); // 或 new Thread
}
});
serverThread.IsBackground = true;
serverThread.Start();
void HandleClient(object state)
{
TcpClient client = (TcpClient)state;
// 處理單一客戶端收發迴圈
client.Close();
}
TcpClient client = new TcpClient("127.0.0.1", 1234); // 連線伺服器
NetworkStream ns = client.GetStream();
StreamReader reader = new StreamReader(ns, Encoding.UTF8);
StreamWriter writer = new StreamWriter(ns, Encoding.UTF8) { AutoFlush = true };
writer.WriteLine("Hello Server");
string reply = reader.ReadLine();
Console.WriteLine("Server: " + reply);
client.Close();
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Windows.Forms;
public class ServerForm : Form
{
private ListBox lstLog;
private TextBox txtSend;
private Button btnStart, btnStop;
private TcpListener listener;
private Thread listenThread;
public ServerForm() { InitializeComponent(); }
private void btnStart_Click(object sender, EventArgs e)
{
lstLog.Items.Add("伺服器啟動中...");
listener = new TcpListener(IPAddress.Any, 1234);
listener.Start();
listenThread = new Thread(ListenLoop) { IsBackground = true };
listenThread.Start();
btnStart.Enabled = false; btnStop.Enabled = true;
}
private void ListenLoop()
{
try
{
while (true)
{
TcpClient client = listener.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(ClientHandler, client);
}
}
catch (ThreadAbortException) { /* 正常停止 */ }
catch (Exception ex) { SafeLog("監聽錯誤: " + ex.Message); }
}
private void ClientHandler(object state)
{
TcpClient client = (TcpClient)state;
SafeLog($"客戶端連入: {client.Client.RemoteEndPoint}");
using (NetworkStream ns = client.GetStream())
using (StreamReader r = new StreamReader(ns, Encoding.UTF8))
using (StreamWriter w = new StreamWriter(ns, Encoding.UTF8) { AutoFlush = true })
{
try
{
while (true)
{
string msg = r.ReadLine();
if (msg == null) break; // 客戶端斷線
SafeLog($"收到: {msg}");
// 這裡示範回覆固定訊息,實務可從 UI 取得
string reply = "Server Received: " + msg;
w.WriteLine(reply);
SafeLog($"回覆: {reply}");
}
}
catch (IOException) { /* 連線中斷 */ }
finally { SafeLog("客戶端斷線"); }
}
client.Close();
}
private void SafeLog(string msg)
{
if (lstLog.InvokeRequired)
lstLog.Invoke(new Action(SafeLog), msg);
else
lstLog.Items.Add($"[{DateTime.Now:HH:mm:ss}] {msg}");
}
private void btnStop_Click(object sender, EventArgs e)
{
listener?.Stop();
listenThread?.Abort(); // 簡易示範,生產環境建議用 CancellationToken
btnStart.Enabled = true; btnStop.Enabled = false;
SafeLog("伺服器已停止");
}
// InitializeComponent 略
}
public class ClientForm : Form
{
private ListBox lstLog;
private TextBox txtInput;
private Button btnSend;
private TcpClient client;
private StreamReader reader;
private StreamWriter writer;
public ClientForm()
{
InitializeComponent();
Connect();
}
private void Connect()
{
try
{
client = new TcpClient("127.0.0.1", 1234);
NetworkStream ns = client.GetStream();
reader = new StreamReader(ns, Encoding.UTF8);
writer = new StreamWriter(ns, Encoding.UTF8) { AutoFlush = true };
Log("已連線至伺服器");
}
catch (Exception ex) { Log("連線失敗: " + ex.Message); }
}
private void btnSend_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtInput.Text)) return;
try
{
writer.WriteLine(txtInput.Text);
Log($"已送出: {txtInput.Text}");
string reply = reader.ReadLine();
Log($"伺服器回覆: {reply}");
}
catch (Exception ex) { Log("通訊錯誤: " + ex.Message); }
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
reader?.Close(); writer?.Close(); client?.Close();
base.OnFormClosed(e);
}
// InitializeComponent 略
}
| 主題 | 關鍵技術 | 適用場景 |
|---|---|---|
| 非同步 Socket | SocketAsyncEventArgs, await AcceptAsync() | 高併發 (萬級連線) 伺服器 |
| 訊息框架 | Length-Prefix, Protobuf, MessagePack | 二進位高效序列化、黏包/拆包處理 |
| SSL/TLS 加密 | SslStream, 憑證驗證 | 機密資料傳輸 |
| WebSocket | System.Net.WebSockets, SignalR | 雙向即時通訊、瀏覽器相容 |
| gRPC | HTTP/2, Protobuf, Grpc.AspNetCore | 微服務 RPC、強型別契約 |