.NET TCP 连接池






3.56/5 (8投票s)
通过连接池提高 TCP 连接性能。
引言
对于任何处理客户端请求的软件,在某个时刻,都必须存在一个持久点来存储所有与客户端相关的数据。这些存储可以是任何东西(数据库、大型机、文件等)。如果此软件需要使用并发请求访问这些存储,并且知道打开连接会消耗内存和 CPU,该怎么办?连接池是打开的和可重用的连接的容器。它将帮助您节省内存和 CPU 时间。它还将帮助您管理连接。
背景
由于我们在本例中使用 TCP 协议,因此您应该了解 C# 中的基本 TCP 实现。您还应该熟悉 System.Collection.Generic
命名空间和 .NET 框架。
使用代码
本文描述了连接池机制的自定义实现;我们有两个主要类:ConnectionPool
和 CustomSocket
。
CustomSocket
是一个简单的 TCPClient
的表示。
public class CustomSocket:TcpClient
{
private DateTime _TimeCreated;
public DateTime TimeCreated
{
get { return _TimeCreated; }
set { _TimeCreated = value; }
}
public CustomSocket(string host,int port)
: base(host,port)
{
_TimeCreated = DateTime.Now;
}
}
ConnectionPool
是连接池机制的操作管理器,它包含以下内容
InitializeConnectionPool
函数初始化连接池,其中 hostIpAddress
是目标 TCP 服务器,hostPortNumber
是目标 TCP 服务器访问端口,而 minConnection
表示要创建的最小连接数。由于我们可以创建默认数量的连接,因此控制新连接的创建非常重要,否则我们会得到溢出。maxConnections
表示要创建的最大数量;如果超过该值,将抛出异常。
public static void InitializeConnectionPool(string hostIPAddress,
int hostPortNumber, int minConnections, int maxConnections)
{
POOL_MAX_SIZE = maxConnections;
POOL_MIN_SIZE = minConnections;
hostIP = hostIPAddress;
hostPort = hostPortNumber;
availableSockets = new Queue<CustomSocket>();
for(int i=0 ; i < minConnections ; i++)
{
CustomSocket cachedSocket = OpenSocket();
PutSocket(cachedSocket);
}
Initialized = true;
System.Diagnostics.Trace.WriteLine("Connection Pool is initialized" +
" with Max Number of " +
POOL_MAX_SIZE.ToString() + " And Min number of " +
availableSockets.Count.ToString());
}
GetSocket
函数返回一个连接对象,具有两种流程。如果池中有可用的连接,它将弹出一个连接;如果没有,它将创建一个新连接。
public static CustomSocket GetSocket()
{
if (ConnectionPool.availableSockets.Count > 0)
{
lock (availableSockets)
{
CustomSocket socket = null;
while (ConnectionPool.availableSockets.Count > 0)
{
socket = ConnectionPool.availableSockets.Dequeue();
if (socket.Connected)
{
System.Diagnostics.Trace.WriteLine("Socket Dequeued -> Pool size: " +
ConnectionPool.availableSockets.Count.ToString());
return socket;
}
else
{
socket.Close();
System.Threading.Interlocked.Decrement(ref SocketCounter);
System.Diagnostics.Trace.WriteLine("GetSocket -- Close -- Count: " +
SocketCounter.ToString());
}
}
}
return ConnectionPool.OpenSocket();
}
PutSocket
函数负责处理连接的释放;如果达到最大限制,则将释放连接;否则,它将被排队。
public static void PutSocket(CustomSocket socket)
{
lock (availableSockets)
{
TimeSpan socketLifeTime = DateTime.Now.Subtract(socket.TimeCreated);
if (ConnectionPool.availableSockets.Count <
ConnectionPool.POOL_MAX_SIZE && socketLifeTime.Minutes < 2)
// Configuration Value
{
if (socket != null)
{
if (socket.Connected)
{
ConnectionPool.availableSockets.Enqueue(socket);
System.Diagnostics.Trace.WriteLine(
"Socket Queued -> Pool size: " +
ConnectionPool.availableSockets.Count.ToString());
}
else
{
socket.Close();
}
}
}
else
{
socket.Close();
System.Diagnostics.Trace.WriteLine("PutSocket - Socket is forced " +
"to closed -> Pool size: " +
ConnectionPool.availableSockets.Count.ToString());
}
}
}
当抛出异常时,将调用此方法以通知连接池已释放套接字
public static void PopulateSocketError()
{
System.Threading.Interlocked.Decrement(ref SocketCounter);
System.Diagnostics.Trace.WriteLine("Populate Socket Error host " +
"Connections count: " + SocketCounter.ToString());
}
要使用此组件,请执行以下操作
ConnectionPool.InitializeConnectionPool(hostIPAddress, hostPortNumber,
minConnections, maxConnections);
try
{
// Get Socket
CustomSocket = ConnectionPool.GetSocket();
// Do Something
// Return socket
ConnectionPool.PutSocket();
}
catch(Exception)
{
ConnectionPool.PopulateSocketError();
}
注释
每个套接字都有一个过期时间;如果过期,则将释放该套接字。
历史
- 版本 1.0。