This commit is contained in:
wuyanchen 2025-12-30 11:22:46 +08:00
parent fd60b1cea5
commit 84d98d54d4
4 changed files with 40 additions and 16 deletions

View File

@ -0,0 +1,11 @@
using System.Collections.Concurrent;
using System.Net.WebSockets;
namespace XNet.Business.Entity
{
public class WebSocketInfo
{
public WebSocket WebSocket { get; set; } = null!;
public ConcurrentDictionary<string, bool> RoomIds { get; set; } = new();
}
}

View File

@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Net.Sockets; using System.Net.Sockets;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text.Json; using System.Text.Json;
using XNet.Business.Entity;
using XNet.Business.PathNavigation; using XNet.Business.PathNavigation;
namespace XNet.Business.Net namespace XNet.Business.Net
@ -13,7 +14,7 @@ namespace XNet.Business.Net
public class WsConnectionManager public class WsConnectionManager
{ {
// ========== 原有核心字段 ========== // ========== 原有核心字段 ==========
private readonly ConcurrentDictionary<string, WebSocket> _connections = new(); private readonly ConcurrentDictionary<string, WebSocketInfo> _connections = new();
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, bool>> _instanceSubscribers = new(); private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, bool>> _instanceSubscribers = new();
//private readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; //private readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
@ -23,7 +24,7 @@ namespace XNet.Business.Net
private readonly ObjectPool<List<string>> _deadConnListPool; private readonly ObjectPool<List<string>> _deadConnListPool;
private readonly ObjectPool<byte[]> _byteArrayPool; private readonly ObjectPool<byte[]> _byteArrayPool;
private readonly SceneAgent _sceneAgent = null; private readonly SceneAgent _sceneAgent = null!;
public WsConnectionManager(SceneAgent sceneAgent) public WsConnectionManager(SceneAgent sceneAgent)
{ {
@ -41,16 +42,19 @@ namespace XNet.Business.Net
public string AddConnection(WebSocket socket) public string AddConnection(WebSocket socket)
{ {
string connId = $"Conn_{Nanoid.Generate()}"; string connId = $"Conn_{Nanoid.Generate()}";
_connections.TryAdd(connId, socket); _connections.TryAdd(connId, new WebSocketInfo
{
WebSocket = socket
});
Console.WriteLine($"[WS .NET 10] 新连接:{connId},当前连接数:{_connections.Count}"); Console.WriteLine($"[WS .NET 10] 新连接:{connId},当前连接数:{_connections.Count}");
return connId; return connId;
} }
public void RemoveConnection(string connId) public void RemoveConnection(string connId)
{ {
if (_connections.TryRemove(connId, out _)) if (_connections.TryRemove(connId, out var socketInfo))
{ {
foreach (var instanceId in _instanceSubscribers.Keys) foreach (var instanceId in socketInfo.RoomIds.Keys)
{ {
_instanceSubscribers[instanceId].TryRemove(connId, out _); _instanceSubscribers[instanceId].TryRemove(connId, out _);
@ -66,7 +70,7 @@ namespace XNet.Business.Net
public bool SubscribeInstance(string connId, string mapKey, ref string roomId) public bool SubscribeInstance(string connId, string mapKey, ref string roomId)
{ {
if (!_connections.ContainsKey(connId)) if (!_connections.TryGetValue(connId,out WebSocketInfo? socketInfo))
{ {
Console.WriteLine($"[WS .NET 10] 订阅失败:连接 {connId} 不存在"); Console.WriteLine($"[WS .NET 10] 订阅失败:连接 {connId} 不存在");
return false; return false;
@ -88,6 +92,8 @@ namespace XNet.Business.Net
_instanceSubscribers[roomId].TryAdd(connId, true); _instanceSubscribers[roomId].TryAdd(connId, true);
socketInfo.RoomIds[roomId] = true;
if (isNewInstance) if (isNewInstance)
{ {
_sceneAgent.CreateInstance(roomId, mapKey); _sceneAgent.CreateInstance(roomId, mapKey);
@ -107,7 +113,7 @@ namespace XNet.Business.Net
public async Task SendSerializeMessageToPointWsSocket<T>(string connId, T syncMsgs) public async Task SendSerializeMessageToPointWsSocket<T>(string connId, T syncMsgs)
{ {
if (_connections.TryGetValue(connId, out var socket)) if (_connections.TryGetValue(connId, out var socketInfo))
{ {
// 3. 复用:从池获取失效连接列表 // 3. 复用:从池获取失效连接列表
var deadConnIds = _deadConnListPool.Get(); var deadConnIds = _deadConnListPool.Get();
@ -117,12 +123,12 @@ namespace XNet.Business.Net
if (msgBytesLength != 0) if (msgBytesLength != 0)
{ {
// 3. 发送时传递有效长度 // 3. 发送时传递有效长度
_ = SendToSingleConnAsync(socket, msgBytes, msgBytesLength, connId, deadConnIds); _ = SendToSingleConnAsync(socketInfo.WebSocket, msgBytes, msgBytesLength, connId, deadConnIds);
} }
else else
{ {
// 3. 发送整串字节数组 // 3. 发送整串字节数组
_ = SendToSingleConnAsync(socket, msgBytes, msgBytes.Length, connId, deadConnIds); _ = SendToSingleConnAsync(socketInfo.WebSocket, msgBytes, msgBytes.Length, connId, deadConnIds);
} }
// 清理失效连接 // 清理失效连接
foreach (var deadConnId in deadConnIds) foreach (var deadConnId in deadConnIds)
@ -169,17 +175,17 @@ namespace XNet.Business.Net
{ {
foreach (var connKv in subscriberConnIds) foreach (var connKv in subscriberConnIds)
{ {
if (_connections.TryGetValue(connKv.Key, out var socket)) if (_connections.TryGetValue(connKv.Key, out var socketInfo))
{ {
if (msgBytesLength != 0) if (msgBytesLength != 0)
{ {
// 3. 发送时传递有效长度 // 3. 发送时传递有效长度
_ = SendToSingleConnAsync(socket, msgBytes, msgBytesLength, connKv.Key, deadConnIds); _ = SendToSingleConnAsync(socketInfo.WebSocket, msgBytes, msgBytesLength, connKv.Key, deadConnIds);
} }
else else
{ {
// 3. 发送整串字节数组 // 3. 发送整串字节数组
_ = SendToSingleConnAsync(socket, msgBytes, msgBytes.Length, connKv.Key, deadConnIds); _ = SendToSingleConnAsync(socketInfo.WebSocket, msgBytes, msgBytes.Length, connKv.Key, deadConnIds);
} }
} }
} }
@ -284,13 +290,14 @@ namespace XNet.Business.Net
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()
{ {
foreach (var (_, socket) in _connections) foreach (var (key, socketInfo) in _connections)
{ {
if (socket.State == WebSocketState.Open) if (socketInfo.WebSocket.State == WebSocketState.Open)
{ {
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Server shutdown", CancellationToken.None); await socketInfo.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Server shutdown", CancellationToken.None);
} }
socket.Dispose(); socketInfo.WebSocket.Dispose();
RemoveConnection(key);
} }
_connections.Clear(); _connections.Clear();
_instanceSubscribers.Clear(); _instanceSubscribers.Clear();

View File

@ -39,6 +39,7 @@ namespace XNet.Business.Net
if (result.MessageType == WebSocketMessageType.Close) if (result.MessageType == WebSocketMessageType.Close)
{ {
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by client", CancellationToken.None); await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by client", CancellationToken.None);
wsManager.RemoveConnection(connId);
break; break;
} }

View File

@ -136,6 +136,11 @@ namespace XNet.Business.PathNavigation
_instanceMap.TryRemove(roomId, out _); _instanceMap.TryRemove(roomId, out _);
} }
public void ClearInstanceMap()
{
_instanceMap.Clear();
}
// ========================================== // ==========================================
// 3. 【新增】供 SceneAgent 使用的辅助方法 // 3. 【新增】供 SceneAgent 使用的辅助方法
// ========================================== // ==========================================