在网络编程中,TCP服务器是一个常见的组件,用于处理客户端的连接和数据传输。然而,在某些情况下,需要能够随时停止服务器的运行,这就需要引入取消机制。本文将介绍如何使用C#创建一个支持取消的TCP服务器,包括监听连接、读取数据和响应客户端请求的实现方式。
TCP服务器的核心功能之一是监听来自客户端的连接请求。在C#中,可以使用TcpListener
类来实现这一功能。服务器需要在一个指定的端口上监听,当有客户端尝试连接时,服务器可以接受这个连接并创建一个TcpClient
对象来与客户端进行通信。
一旦服务器与客户端建立了连接,下一步就是读取客户端发送的数据。这通常通过读取TcpClient
的网络流来实现。服务器需要不断地检查是否有可用的数据可以读取,如果有,就将这些数据读取到缓冲区中,并将其转换为字符串或其他适当的格式。
在接收到客户端的数据后,服务器可以根据需要对这些数据进行处理,并将处理结果发送回客户端。这通常涉及到将数据写入到与客户端连接的网络流中。在处理完所有请求后,服务器应该关闭与客户端的连接。
在某些情况下,可能需要在服务器运行过程中随时停止它。为了实现这一点,可以引入一个取消机制。在C#中,可以使用CancellationToken
类来实现这一功能。服务器可以在运行过程中定期检查这个取消令牌的状态,如果它被设置为取消状态,服务器就应该停止当前的操作并关闭所有连接。
public static class TcpServer
{
private const int SleepTimeout = 50; // milliseconds
public static Task Run(int port, CancellationToken cancellation,
Action, TcpClient, CancellationToken> onLineReceived)
{
return Accept(port, cancellation, (client, token) =>
{
var receivedLines = new List();
while (!cancellation.IsCancellationRequested && client.Connected)
{
var available = client.Available;
if (available == 0)
{
Thread.Sleep(SleepTimeout);
continue;
}
var buffer = new byte[available];
client.GetStream().Read(buffer, 0, available);
var newData = Encoding.UTF8.GetString(buffer);
bool newLine;
AppendLines(receivedLines, newData, out newLine);
if (newLine) onLineReceived(receivedLines, client, cancellation);
}
client.Close();
});
}
private static Task Accept(int port, CancellationToken cancellation,
Action onClientAccepted)
{
var childTasks = new List();
var listener = new TcpListener(IPAddress.Any, port);
listener.Server.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReuseAddress, 1);
listener.Start();
return Task.Factory.StartNew(() =>
{
while (!cancellation.IsCancellationRequested)
{
if (!listener.Pending())
{
Thread.Sleep(SleepTimeout);
continue;
}
var client = listener.AcceptTcpClient();
var childTask = new Task(() => onClientAccepted(client, cancellation));
childTasks.Add(childTask);
childTask.ContinueWith(t => childTasks.Remove(t));
childTask.Start();
}
}, cancellation).ContinueWith(t =>
{
Task.WaitAll(childTasks.ToArray());
listener.Stop();
});
}
private static void AppendLines(List lines, string newData, out bool newLine)
{
int i;
int pos = 0;
string line;
newLine = false;
for (i = pos; i < newData.Length; i++)
{
if (newData[i] == '\n')
{
line = (i > 0 && newData[i - 1] == '\r') ?
newData.Substring(pos, i - pos - 1) :
newData.Substring(pos, i - pos);
if (lines.Count == 0) lines.Add(line);
else
{
if (newLine) lines.Add(line);
else lines[lines.Count - 1] = lines[lines.Count - 1] + line;
}
newLine = true;
pos = i + 1;
}
}
line = newData.Substring(pos);
if (!string.IsNullOrEmpty(line)) lines[lines.Count - 1] = lines[lines.Count - 1] + line;
}
}