254 lines
9.4 KiB
C#
254 lines
9.4 KiB
C#
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);
|
||
}
|
||
}
|
||
} |