高併發網路應用中,頻繁建立/關閉 TCP 連線開銷極大。連線池透過預建立連線、重複使用、狀態管理,平衡長連線效能與短連線資源釋放優勢。本文完整解析設計原理、核心實作、併發控制與生產環境優化。
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
public class SocketPool : IDisposable
{
private readonly string _host;
private readonly int _port;
private readonly int _minSize;
private readonly int _maxSize;
private readonly TimeSpan _idleTimeout;
private readonly ConcurrentQueue _available = new();
private readonly HashSet _allSockets = new();
private readonly ReaderWriterLockSlim _lock = new();
private readonly Timer _maintenanceTimer;
private bool _disposed;
public SocketPool(string host, int port, int minSize = 10, int maxSize = 200, TimeSpan? idleTimeout = null)
{
_host = host;
_port = port;
_minSize = minSize;
_maxSize = maxSize;
_idleTimeout = idleTimeout ?? TimeSpan.FromMinutes(5);
// 預熱建立最小連線數
for (int i = 0; i < _minSize; i++)
CreateAndEnqueueSocket();
// 定期維護:健康檢查、閒置回收、補充最小數
_maintenanceTimer = new Timer(MaintenanceCallback, null,
TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
}
private PooledSocket CreateAndEnqueueSocket()
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
{
SendTimeout = 5000,
ReceiveTimeout = 5000,
NoDelay = true // 關閉 Nagle 演算法,降低延遲
};
socket.Connect(_host, _port);
var pooled = new PooledSocket(socket);
_lock.EnterWriteLock();
try
{
_allSockets.Add(pooled);
_available.Enqueue(pooled);
}
finally { _lock.ExitWriteLock(); }
return pooled;
}
public PooledSocket Acquire(TimeSpan? timeout = null)
{
var deadline = DateTime.UtcNow + (timeout ?? TimeSpan.FromSeconds(30));
while (DateTime.UtcNow < deadline)
{
// 嘗試取得可用連線
if (_available.TryDequeue(out var pooled) && pooled.IsHealthy)
{
pooled.MarkBusy();
return pooled;
}
else if (pooled != null && !pooled.IsHealthy)
{
RemoveSocket(pooled); // 移除損壞連線
}
// 池未滿,嘗試建立新連線
_lock.EnterReadLock();
int currentCount = _allSockets.Count;
_lock.ExitReadLock();
if (currentCount < _maxSize)
{
try
{
var newPooled = CreateAndEnqueueSocket();
newPooled.MarkBusy();
return newPooled;
}
catch { /* 建立失敗,繼續等待 */ }
}
Thread.Sleep(10); // 避免忙碌等待
}
throw new TimeoutException("無法在指定時間內取得可用連線");
}
public void Release(PooledSocket pooled)
{
if (pooled == null || _disposed) return;
if (pooled.IsHealthy)
{
pooled.MarkIdle();
_available.Enqueue(pooled);
}
else
{
RemoveSocket(pooled);
}
}
private void RemoveSocket(PooledSocket pooled)
{
_lock.EnterWriteLock();
try
{
if (_allSockets.Remove(pooled))
{
pooled.Dispose();
}
}
finally { _lock.ExitWriteLock(); }
}
private void MaintenanceCallback(object state)
{
if (_disposed) return;
_lock.EnterWriteLock();
try
{
var now = DateTime.UtcNow;
var toRemove = new List();
// 檢查所有連線健康狀態
foreach (var pooled in _allSockets)
{
if (!pooled.IsHealthy || (pooled.State == SocketState.Idle && now - pooled.LastUsed > _idleTimeout))
{
toRemove.Add(pooled);
}
}
foreach (var pooled in toRemove)
{
_allSockets.Remove(pooled);
pooled.Dispose();
}
// 補充至最小連線數
while (_allSockets.Count < _minSize)
{
try { CreateAndEnqueueSocket(); }
catch { break; } // 建立失敗停止嘗試
}
}
finally { _lock.ExitWriteLock(); }
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_maintenanceTimer?.Dispose();
_lock.EnterWriteLock();
try
{
foreach (var pooled in _allSockets)
pooled.Dispose();
_allSockets.Clear();
while (_available.TryDequeue(out var p)) p.Dispose();
}
finally { _lock.ExitWriteLock(); }
}
}
public class PooledSocket : IDisposable
{
public Socket Socket { get; }
public SocketState State { get; private set; }
public DateTime LastUsed { get; private set; }
public bool IsHealthy => State != SocketState.Broken && Socket?.Connected == true;
public PooledSocket(Socket socket)
{
Socket = socket;
State = SocketState.Idle;
LastUsed = DateTime.UtcNow;
}
public void MarkBusy()
{
State = SocketState.Busy;
LastUsed = DateTime.UtcNow;
}
public void MarkIdle()
{
State = SocketState.Idle;
LastUsed = DateTime.UtcNow;
}
public void MarkBroken() => State = SocketState.Broken;
public void Dispose()
{
try { Socket?.Shutdown(SocketShutdown.Both); } catch { }
try { Socket?.Close(); } catch { }
Socket?.Dispose();
State = SocketState.Broken;
}
}
public enum SocketState { Idle, Busy, Broken }
// 應用程式啟動時建立單例
var pool = new SocketPool("127.0.0.1", 8080, minSize: 20, maxSize: 200);
// 業務邏輯中使用
public async Task SendRequestAsync(string request)
{
var pooled = pool.Acquire(TimeSpan.FromSeconds(10));
try
{
var requestBytes = Encoding.UTF8.GetBytes(request);
await pooled.Socket.SendAsync(requestBytes, SocketFlags.None);
var buffer = new byte[4096];
var received = await pooled.Socket.ReceiveAsync(buffer, SocketFlags.None);
return Encoding.UTF8.GetString(buffer, 0, received);
}
catch (Exception ex)
{
pooled.MarkBroken(); // 標記損壞,歸還時會被移除
throw;
}
finally
{
pool.Release(pooled);
}
}
Socket 連線池是高效能網路服務的基石,正確實作能帶來數量級的吞吐量提升與延遲降低。現代開發優先考慮成熟框架內建機制,自行實作僅限於特殊協定或極致優化場景。