Socket 是網路程式設計的基石。.NET 提供 System.Net.Sockets.Socket 類別封裝 Berkeley Socket API,並提供 TcpClient/TcpListener/UdpClient 高階封裝。本文系統整理 Socket 核心概念、同步/非同步模式、TCP/UDP 差異、完整可執行範例,並探討實務選型建議。
| 概念 | 說明 |
|---|---|
| Socket | 網路通訊端點,IP + Port + 協定 的三元組 |
| TCP (SOCK_STREAM) | 面向連接、可靠、有序、雙向串流、三向交握建立連線 |
| UDP (SOCK_DGRAM) | 無連接、不可靠、無序、資料報文、低延遲、適合廣播/即時 |
| 阻塞模式 | 預設,Accept/Receive/Send 等待完成才返回 |
| 非阻塞/非同步 | Begin/End 模式、Task-based (SocketAsyncEventArgs)、IOCP 效能最佳 |
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace TcpSyncDemo
{
class TcpServer
{
static void Main()
{
const int PORT = 9050;
var localEP = new IPEndPoint(IPAddress.Any, PORT);
var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(localEP);
server.Listen(10);
Console.WriteLine($"TCP Server listening on {localEP}...");
while (true)
{
Socket client = server.Accept(); // 阻塞等待連線
var clientEP = (IPEndPoint)client.RemoteEndPoint;
Console.WriteLine($"Client connected: {clientEP}");
// 歡迎訊息
byte[] welcome = Encoding.ASCII.GetBytes("Welcome to TCP Server!\n");
client.Send(welcome);
// 回應迴圈
byte[] buffer = new byte[1024];
while (true)
{
int received = client.Receive(buffer);
if (received == 0) break; // 客戶端斷開
string msg = Encoding.ASCII.GetString(buffer, 0, received).Trim();
Console.WriteLine($"Received: {msg}");
if (msg.Equals("exit", StringComparison.OrdinalIgnoreCase)) break;
// 回送確認
client.Send(buffer, received, SocketFlags.None);
}
Console.WriteLine($"Client disconnected: {clientEP}");
client.Shutdown(SocketShutdown.Both);
client.Close();
}
}
}
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace TcpSyncDemo
{
class TcpClient
{
static void Main()
{
Console.Write("Server IP: ");
string ip = Console.ReadLine();
Console.Write("Server Port: ");
int port = int.Parse(Console.ReadLine());
var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var serverEP = new IPEndPoint(IPAddress.Parse(ip), port);
try
{
client.Connect(serverEP);
Console.WriteLine("Connected to server.");
byte[] buffer = new byte[1024];
int recv = client.Receive(buffer);
Console.WriteLine($"Server: {Encoding.ASCII.GetString(buffer, 0, recv)}");
while (true)
{
Console.Write("> ");
string input = Console.ReadLine();
if (input == "exit") break;
client.Send(Encoding.ASCII.GetBytes(input));
recv = client.Receive(buffer);
Console.WriteLine($"Echo: {Encoding.ASCII.GetString(buffer, 0, recv)}");
}
}
catch (SocketException ex)
{
Console.WriteLine($"Connection failed: {ex.Message}");
}
finally
{
client.Shutdown(SocketShutdown.Both);
client.Close();
}
}
}
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace UdpSyncDemo
{
class UdpServer
{
static void Main()
{
const int PORT = 9050;
var localEP = new IPEndPoint(IPAddress.Any, PORT);
var server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
server.Bind(localEP);
Console.WriteLine($"UDP Server listening on {localEP}...");
EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
byte[] buffer = new byte[1024];
while (true)
{
int recv = server.ReceiveFrom(buffer, ref remoteEP);
string msg = Encoding.ASCII.GetString(buffer, 0, recv);
Console.WriteLine($"From {remoteEP}: {msg}");
// 回送
server.SendTo(buffer, recv, SocketFlags.None, remoteEP);
}
}
}
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace UdpSyncDemo
{
class UdpClient
{
static void Main()
{
var serverEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);
var client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
byte[] buffer = new byte[1024];
// 首次發送
client.SendTo(Encoding.ASCII.GetBytes("Hello Server"), serverEP);
int recv = client.ReceiveFrom(buffer, ref remoteEP);
Console.WriteLine($"Server: {Encoding.ASCII.GetString(buffer, 0, recv)}");
while (true)
{
Console.Write("> ");
string input = Console.ReadLine();
if (input == "exit") break;
client.SendTo(Encoding.ASCII.GetBytes(input), serverEP);
recv = client.ReceiveFrom(buffer, ref remoteEP);
Console.WriteLine($"Echo: {Encoding.ASCII.GetString(buffer, 0, recv)}");
}
client.Close();
}
}
}
// 非同步 Accept
server.BeginAccept(AcceptCallback, server);
void AcceptCallback(IAsyncResult ar)
{
var listener = (Socket)ar.AsyncState;
Socket client = listener.EndAccept(ar);
// 立即投遞下一個 Accept
listener.BeginAccept(AcceptCallback, listener);
// 開始非同步接收
var state = new StateObject { Socket = client };
client.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReceiveCallback, state);
}
void ReceiveCallback(IAsyncResult ar)
{
var state = (StateObject)ar.AsyncState;
Socket client = state.Socket;
int read = client.EndReceive(ar);
if (read > 0)
{
// 處理資料...
client.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReceiveCallback, state);
}
else client.Close();
}
class StateObject { public Socket Socket; public byte[] Buffer = new byte[1024]; }
.NET Core / .NET 5+ 建議使用 SocketAsyncEventArgs 搭配 ValueTask 或 Memory<byte> 實現高效能 IOCP 模式,避免 APM 的物件分配開銷。範例略,請參考 Microsoft 官方文件 SocketAsyncEventArgs。
| 類別 | 協定 | 特點 | 適用場景 |
|---|---|---|---|
| TcpClient | TCP | 簡化 Connect/Send/Receive,內建 NetworkStream | 客戶端快速開發 |
| TcpListener | TCP | 簡化 Bind/Listen/Accept,回傳 TcpClient | 伺服器快速開發 |
| UdpClient | UDP | 簡化 SendTo/ReceiveFrom,支援 Multicast | UDP 快速開發 |
| Socket | TCP/UDP/Raw | 完整控制、非同步、IOCP、效能最佳 | 高併發、自定義協定、極致效能 |
using 或 try/finally 確保 Socket.Close() / Dispose()IPAddress.IPv6Any、Socket.OPTION_IPV6_V6ONLY = false掌握 Socket 同步/非同步模式、TCP/UDP 特性與適用場景,是 .NET 網路程式設計的核心竭力。從簡單的主控台聊天室到百萬併發的即時通訊伺服器,核心原理始終圍繞 端點建立、連線管理、資料收發、生命週期控制 四大面向。建議由高階封裝 (TcpClient) 入手,逐步深入 Socket 非同步與 IOCP,構建穩健高效的網路應用系統。