首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 網路程式設計入門:TCP 同步阻塞式 Client/Server 完整實作

     編輯:關於C#

.NET Framework 提供 System.Net 與 System.Net.Sockets 兩大命名空間封裝網路通訊細節。本文以最基礎的 TCP 同步阻塞模式 為例,逐步建構一對簡易的伺服器/用戶端主控台/WinForms 程式,釐清 TcpListener/TcpClient、NetworkStream、StreamReader/Writer 核心類別運作機制。

核心概念釐清

術語說明
同步發送方發送後等待接收方回應才繼續 (本文採用)
非同步發送後立即繼續,透過 Callback/await 處理回應
阻塞呼叫 ReadLine() 等方法時,執行緒暫停直到有資料或逾時
非阻塞立即回傳,需輪詢或事件通知

生產環境高併發建議使用 非同步非阻塞 (BeginAccept/EndAccept、async/await、SocketAsyncEventArgs),本文以同步阻塞為教學基礎。

伺服器端關鍵步驟

1. 建立監聽器並接受連線


TcpListener listener = new TcpListener(IPAddress.Any, 1234); // 監聽所有介面 1234 埠
listener.Start();
Socket clientSocket = listener.AcceptSocket(); // 阻塞直到客戶端連入
// 或使用更高層級:
TcpClient client = listener.AcceptTcpClient();

2. 透過 NetworkStream 收發資料


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

3. 多執行緒處理多客戶端


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();

完整 WinForms 伺服器端程式碼 (ServerForm.cs)


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 略
}

完整 WinForms 用戶端程式碼 (ClientForm.cs)


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 略
}

執行測試流程

  1. 先啟動 ServerForm,點擊「啟動伺服器」。
  2. 啟動一或多個 ClientForm,自動連線。
  3. 在 Client 輸入文字點擊送出,觀察雙方 ListBox 訊息收發。

進階主題與現代化演進

常見坑點提醒

  • 編碼一致:Client/Server 必須使用相同 Encoding (建議 UTF-8),否則中文亂碼。
  • ReadLine 需換行:傳送端必須 WriteLine 或手動加 \n,否則接收端永久阻塞。
  • AutoFlush:StreamWriter 預設緩衝,需設 AutoFlush=true 或手動 Flush()。
  • 執行緒安全:UI 控制項存取必須 Invoke/BeginInvoke。
  • 資源釋放:using 區塊確保 Stream/Client 正確關閉,避免連線洩漏。

掌握 TcpListener/TcpClient + NetworkStream + StreamReader/Writer 這組經典組合,即可快速建構 TCP 應用原型。邁向生產級系統時,請務必引入非同步模式、訊息協定框架、錯誤重試與監控機制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
主題關鍵技術適用場景
非同步 SocketSocketAsyncEventArgs, await AcceptAsync()高併發 (萬級連線) 伺服器
訊息框架Length-Prefix, Protobuf, MessagePack二進位高效序列化、黏包/拆包處理
SSL/TLS 加密SslStream, 憑證驗證機密資料傳輸
WebSocketSystem.Net.WebSockets, SignalR雙向即時通訊、瀏覽器相容
gRPCHTTP/2, Protobuf, Grpc.AspNetCore微服務 RPC、強型別契約