upper-computer-program/DoAn/Core/通讯/SockerTcpServer.cs

254 lines
9.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DoAn.Core.
{
public class SockerTcpServer : IDisposable
{
private readonly string _ip;
private readonly int _port;
private Socket _serverSocket;
private volatile bool _isRunning;
private readonly ConcurrentDictionary<Socket, ClientState> _clients = new ConcurrentDictionary<Socket, ClientState>();
private readonly ConcurrentDictionary<string, Socket> _clientIdMap = new ConcurrentDictionary<string, Socket>();
private readonly ConcurrentDictionary<Socket, StringBuilder> _clientBuffers = new ConcurrentDictionary<Socket, StringBuilder>(); // 客户端缓冲区
private Thread _listenThread;
public delegate void MessageReceivedHandler(string message);
public event MessageReceivedHandler MessageReceived;
public SockerTcpServer(string ip, int port)
{
_ip = ip;
_port = port;
}
// 启动服务器
public bool Start()
{
try
{
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var endPoint = new IPEndPoint(IPAddress.Parse(_ip), _port);
_serverSocket.Bind(endPoint);
_serverSocket.Listen(20);
_isRunning = true;
_listenThread = new Thread(ListenForClients);
_listenThread.IsBackground = true;
_listenThread.Start();
Console.WriteLine($"服务器已启动,监听 {_ip}:{_port}");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"启动服务器失败: {ex.Message}");
return false;
}
}
// 停止服务器
public void Stop()
{
_isRunning = false;
_serverSocket?.Close();
_listenThread?.Join(1000);
foreach (var client in _clients.Keys)
{
try { client.Shutdown(SocketShutdown.Both); client.Close(); } catch { }
}
_clients.Clear();
_clientIdMap.Clear();
_clientBuffers.Clear();
Console.WriteLine("服务器已停止");
}
// 监听客户端连接
private void ListenForClients()
{
while (_isRunning)
{
try
{
var clientSocket = _serverSocket.Accept();
var clientId = Guid.NewGuid().ToString();
_clients.TryAdd(clientSocket, new ClientState { ClientId = clientId });
_clientIdMap.TryAdd(clientId, clientSocket);
_clientBuffers.TryAdd(clientSocket, new StringBuilder()); // 初始化缓冲区
Console.WriteLine($"客户端 {clientId} 已连接,当前连接数:{_clients.Count}");
_ = HandleClientAsync(clientSocket, clientId); // 异步处理客户端
}
catch (ObjectDisposedException) { break; }
catch (Exception ex) { Console.WriteLine($"接受连接失败:{ex.Message}"); }
}
}
// 异步处理客户端消息(改进版本)
// 异步处理客户端消息(适用于旧版 .NET
private async Task HandleClientAsync(Socket clientSocket, string clientId)
{
try
{
var buffer = new byte[1024];
while (_isRunning && clientSocket.Connected)
{
// 使用 SocketAsyncEventArgs 替代两参数的 ReceiveAsync
var args = new SocketAsyncEventArgs();
args.SetBuffer(buffer, 0, buffer.Length);
// 使用 TaskCompletionSource 转换为异步操作
var tcs = new TaskCompletionSource<int>();
args.Completed += (sender, e) => tcs.TrySetResult(e.BytesTransferred);
bool willRaiseEvent = clientSocket.ReceiveAsync(args);
if (!willRaiseEvent)
{
tcs.TrySetResult(args.BytesTransferred);
}
int bytesRead = await tcs.Task;
if (bytesRead == 0)
{
Console.WriteLine($"客户端 {clientId} 已断开连接");
RemoveClient(clientSocket, clientId);
return;
}
string chunk = Encoding.UTF8.GetString(buffer, 0, bytesRead);
_clientBuffers[clientSocket].Append(chunk);
Console.WriteLine($"[{DateTime.Now}] 从客户端 {clientId} 接收数据: {chunk}");
string fullData = _clientBuffers[clientSocket].ToString();
int startIndex = 0;
int nextDelimiterIndex;
// 循环处理所有完整消息
while ((nextDelimiterIndex = fullData.IndexOf("\\n", startIndex)) != -1)
{
int messageLength = nextDelimiterIndex - startIndex;
if (messageLength > 0)
{
string message = fullData.Substring(startIndex, messageLength);
ProcessMessage(clientId, message);
}
startIndex = nextDelimiterIndex + 2; // 跳过 `\n`2个字符
}
// 清除已处理的数据
if (startIndex > 0)
{
// 如果有剩余数据,保留到缓冲区
if (startIndex < fullData.Length)
{
string remainingData = fullData.Substring(startIndex);
_clientBuffers[clientSocket].Clear();
_clientBuffers[clientSocket].Append(remainingData);
}
else
{
_clientBuffers[clientSocket].Clear();
}
}
}
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset)
{
Console.WriteLine($"客户端 {clientId} 连接重置");
}
catch (Exception ex)
{
Console.WriteLine($"客户端 {clientId} 处理失败:{ex.Message}");
}
finally
{
RemoveClient(clientSocket, clientId);
}
}
// 处理单条完整消息(过滤逻辑)
private void ProcessMessage(string clientId, string message)
{
string trimmedMessage = message.Trim(); // 去除首尾空白字符
Console.WriteLine($"[{DateTime.Now}] 收到消息:{trimmedMessage}"); // 调试输出
if (trimmedMessage != "NoRead")
{
Console.WriteLine($"[{DateTime.Now}] 有效消息:{trimmedMessage}");
MessageReceived?.Invoke(trimmedMessage);
}
}
// 移除客户端
private void RemoveClient(Socket clientSocket, string clientId)
{
if (_clients.TryRemove(clientSocket, out _))
{
_clientIdMap.TryRemove(clientId, out _);
_clientBuffers.TryRemove(clientSocket, out _);
try { clientSocket.Close(); } catch { }
Console.WriteLine($"客户端 {clientId} 已断开,剩余连接数:{_clients.Count}");
}
}
// 发送消息到特定客户端
public void SendMessage(string clientId, string message)
{
if (_clientIdMap.TryGetValue(clientId, out var socket))
{
try
{
byte[] buffer = Encoding.UTF8.GetBytes(message + "\n"); // 添加消息结束符
socket.Send(buffer);
Console.WriteLine($"[{DateTime.Now}] 发送到客户端 {clientId}: {message}");
}
catch (Exception ex)
{
RemoveClient(socket, clientId);
Console.WriteLine($"发送失败:{ex.Message}");
}
}
}
// 广播消息
public void BroadcastMessage(string message)
{
var buffer = Encoding.UTF8.GetBytes(message + "\n"); // 添加结束符
foreach (var client in _clients.Keys)
{
try
{
client.Send(buffer);
Console.WriteLine($"[{DateTime.Now}] 广播消息:{message}");
}
catch
{
RemoveClient(client, _clients[client].ClientId);
}
}
}
// 客户端状态类
private class ClientState
{
public string ClientId { get; set; }
}
// 资源释放
public void Dispose()
{
Stop();
_serverSocket?.Dispose();
GC.SuppressFinalize(this);
}
}
}