本文提供最精簡的 TCP Socket 通訊範例:主控台應用程式作為回聲伺服器 (收到訊息轉大寫並回傳),WinForms 應用程式作為用戶端,透過文字方塊輸入、列表方塊顯示收發紀錄。適合初學者理解 TcpListener/TcpClient、NetworkStream、StreamReader/Writer 基礎運作。
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
public class EchoServer
{
public static void Main()
{
const int Port = 1234;
IPAddress ip = IPAddress.Parse("127.0.0.1"); // 僅本機測試
// IPAddress ip = IPAddress.Any; // 監聽所有介面
TcpListener listener = new TcpListener(ip, Port);
listener.Start();
Console.WriteLine($"Server started on {ip}:{Port}. Waiting for client...");
// 同步阻塞等待單一用戶端 (示範用,實務應用循環 Accept 或非同步)
using (Socket clientSocket = listener.AcceptSocket())
{
Console.WriteLine($"Client connected: {clientSocket.RemoteEndPoint}");
using (NetworkStream ns = new NetworkStream(clientSocket))
using (StreamReader reader = new StreamReader(ns))
using (StreamWriter writer = new StreamWriter(ns) { AutoFlush = true })
{
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine($"Received: {line}");
string reply = line.ToUpper() + "!";
writer.WriteLine(reply);
Console.WriteLine($"Sent: {reply}");
}
}
}
Console.WriteLine("Client disconnected. Press any key to exit.");
Console.ReadKey();
}
}
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net.Sockets;
using System.Windows.Forms;
public class ClientForm : Form
{
private TextBox txtInput;
private Button btnSend;
private ListBox lstMessages;
private TcpClient _client;
private StreamReader _reader;
private StreamWriter _writer;
private readonly List _messageLog = new List();
public ClientForm()
{
InitializeComponent();
ConnectToServer();
}
private void InitializeComponent()
{
this.Text = "Socket Client Demo";
this.Size = new Size(450, 350);
this.StartPosition = FormStartPosition.CenterScreen;
txtInput = new TextBox { Left = 20, Top = 20, Width = 300, Height = 25 };
txtInput.TextChanged += (s, e) => btnSend.Enabled = txtInput.TextLength > 0;
btnSend = new Button { Text = "Send", Left = 330, Top = 20, Width = 80, Height = 25, Enabled = false };
btnSend.Click += BtnSend_Click;
lstMessages = new ListBox { Left = 20, Top = 60, Width = 390, Height = 230, Font = new Font("Consolas", 9) };
this.Controls.AddRange(new Control[] { txtInput, btnSend, lstMessages });
}
private void ConnectToServer()
{
try
{
_messageLog.Add("Connecting to 127.0.0.1:1234...");
_client = new TcpClient("127.0.0.1", 1234);
_messageLog.Add("Connected. Type message and click Send.");
NetworkStream ns = _client.GetStream();
_reader = new StreamReader(ns);
_writer = new StreamWriter(ns) { AutoFlush = true };
RefreshLog();
}
catch (Exception ex)
{
_messageLog.Add($"Connection failed: {ex.Message}");
RefreshLog();
}
}
private void BtnSend_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtInput.Text)) return;
try
{
string msg = txtInput.Text.Trim();
_writer.WriteLine(msg);
_messageLog.Add($">> {msg}");
string reply = _reader.ReadLine();
_messageLog.Add($"<< {reply}");
txtInput.Clear();
txtInput.Focus();
}
catch (Exception ex)
{
_messageLog.Add($"Error: {ex.Message}");
}
finally
{
RefreshLog();
}
}
private void RefreshLog()
{
lstMessages.Items.Clear();
lstMessages.Items.AddRange(_messageLog.ToArray());
lstMessages.TopIndex = lstMessages.Items.Count - 1; // 自動捲動到底
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
_writer?.WriteLine("BYE");
_writer?.Flush();
_reader?.Close();
_writer?.Close();
_client?.Close();
base.OnFormClosing(e);
}
[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ClientForm());
}
}
Program.cs (Server) 與 Form1.cs (Client,改類別名為 ClientForm 並更新 Main)。Server started...。Connected.。hello world,點擊 Send,列表顯示:
> hello world < HELLO WORLD!
TcpListener (Server)
└─ AcceptSocket() → Socket
TcpClient (Client)
└─ GetStream() → NetworkStream
NetworkStream (雙向串流)
├─ StreamReader → ReadLine() (文字行讀取)
└─ StreamWriter → WriteLine() (文字行寫入,AutoFlush=true 即時送出)
| 現象 | 原因 | 解決 |
|---|---|---|
| 用戶端連不上 | 防火牆阻擋、IP/Port 錯誤、伺服器未啟動 | 檢查防火牆規則,確認 IP (127.0.0.1 vs 實體 IP)、Port 一致 |
| ReadLine 卡住不回傳 | 對方未送出換行符 \r\n、串流未 Flush | 確雙方皆用 WriteLine/ReadLine,Writer 設 AutoFlush=true |
| 中文亂碼 | 編碼不一致 | 指定編碼:new StreamReader(ns, Encoding.UTF8), new StreamWriter(ns, Encoding.UTF8) |
| 只能單一用戶端 | Server 只 Accept 一次 | 放入 while 迴圈或改用非同步 AcceptAsync / ThreadPool |
while(true) { Socket s = listener.AcceptSocket(); ThreadPool.QueueUserWorkItem(HandleClient, s); }ReadLineAsync/WriteLineAsync 搭配 await,避免阻塞執行緒。SslStream 替代 NetworkStream。PING 偵測斷線。此範例展示 .NET 核心網路類別 TcpListener/TcpClient 搭配串流讀寫器的最基本用法。掌握同步阻塞模型後,建議直接進入 async/await 非同步模式與訊息協定設計,才是實務專案的標準開發路徑。