Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ChannelsNS = System.Threading.Channels;
|
||||
using XWorld.Framework.Protocol;
|
||||
|
||||
namespace XWorld.Server.Gateway
|
||||
{
|
||||
// 一个 WebSocket 连接:收循环解码帧→Submit;发循环排空出站队列→SendAsync(每 socket 仅一个发循环,发送串行)
|
||||
public sealed class Connection : IClientConnection
|
||||
{
|
||||
public int PlayerId { get; }
|
||||
|
||||
private readonly WebSocket _socket;
|
||||
private readonly ServerLoop _loop;
|
||||
private readonly ChannelsNS.Channel<byte[]> _outbound = ChannelsNS.Channel.CreateUnbounded<byte[]>();
|
||||
|
||||
public Connection(int playerId, WebSocket socket, ServerLoop loop)
|
||||
{
|
||||
PlayerId = playerId; _socket = socket; _loop = loop;
|
||||
}
|
||||
|
||||
public void Send(byte[] frame) => _outbound.Writer.TryWrite(frame);
|
||||
|
||||
public async Task RunAsync(CancellationToken ct)
|
||||
{
|
||||
var sendTask = SendLoopAsync(ct);
|
||||
try { await ReceiveLoopAsync(ct).ConfigureAwait(false); }
|
||||
finally
|
||||
{
|
||||
_outbound.Writer.TryComplete();
|
||||
await sendTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReceiveLoopAsync(CancellationToken ct)
|
||||
{
|
||||
var buf = new byte[8192];
|
||||
using var ms = new MemoryStream();
|
||||
try
|
||||
{
|
||||
while (_socket.State == WebSocketState.Open && !ct.IsCancellationRequested)
|
||||
{
|
||||
ms.SetLength(0);
|
||||
WebSocketReceiveResult res;
|
||||
do
|
||||
{
|
||||
res = await _socket.ReceiveAsync(new ArraySegment<byte>(buf), ct).ConfigureAwait(false);
|
||||
if (res.MessageType == WebSocketMessageType.Close) return;
|
||||
ms.Write(buf, 0, res.Count);
|
||||
} while (!res.EndOfMessage);
|
||||
|
||||
Frame frame;
|
||||
try { frame = FrameCodec.Decode(ms.ToArray()); }
|
||||
catch (FormatException) { continue; } // 畸形帧:丢弃,不拖垮连接
|
||||
|
||||
_loop.Submit(new FrameCommand { PlayerId = PlayerId, Frame = frame });
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { } // 主机关停/请求中止
|
||||
catch (WebSocketException) { } // 对端突然断开(TCP 掉线)
|
||||
}
|
||||
|
||||
private async Task SendLoopAsync(CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
await foreach (var frame in _outbound.Reader.ReadAllAsync(ct).ConfigureAwait(false))
|
||||
{
|
||||
if (_socket.State != WebSocketState.Open) break;
|
||||
await _socket.SendAsync(new ArraySegment<byte>(frame),
|
||||
WebSocketMessageType.Binary, true, ct).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch (WebSocketException) { } // 对端已断,停止发送
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user