Files

175 lines
4.8 KiB
C#
Raw Permalink Normal View History

2023-03-06 10:06:43 +04:00
/*
2024-05-19 12:26:42 +03:00
* Copyright 2023-2024 Eduard Kargin <kargin.eduard@gmail.com>
2023-03-06 10:06:43 +04:00
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2024-08-17 10:29:27 +03:00
using System.Buffers;
2022-12-20 12:20:52 -08:00
using System.Net;
using System.Net.WebSockets;
2023-03-06 10:06:43 +04:00
using Ragon.Protocol;
2023-04-09 11:06:52 +04:00
using Ragon.Server.IO;
using Ragon.Server.Logging;
2022-12-20 12:20:52 -08:00
2023-07-01 07:47:57 +03:00
namespace Ragon.Server.WebSocketServer;
2022-12-20 12:20:52 -08:00
2023-07-01 07:47:57 +03:00
public class WebSocketServer : INetworkServer
2022-12-20 12:20:52 -08:00
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(WebSocketServer));
private INetworkListener _networkListener;
private Stack<ushort> _sequencer;
private Executor _executor;
private HttpListener _httpListener;
private WebSocketConnection[] _connections;
private List<WebSocketConnection> _activeConnections;
private CancellationTokenSource _cancellationTokenSource;
2023-10-11 19:37:50 +03:00
2023-07-01 07:47:57 +03:00
public WebSocketServer()
{
_sequencer = new Stack<ushort>();
_connections = Array.Empty<WebSocketConnection>();
_activeConnections = new List<WebSocketConnection>();
2023-03-06 10:06:43 +04:00
_executor = new Executor();
}
2024-05-19 11:28:36 +03:00
2025-10-04 15:08:53 +03:00
public async ValueTask StartAccept(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
2022-12-20 12:20:52 -08:00
{
WebSocketConnection connection = null!;
try
2023-03-06 10:06:43 +04:00
{
var context = await _httpListener.GetContextAsync();
if (!context.Request.IsWebSocketRequest)
{
context.Response.StatusCode = 200;
context.Response.ContentLength64 = 0;
context.Response.Close();
continue;
}
2025-10-04 15:08:53 +03:00
var webSocketContext = await context.AcceptWebSocketAsync(null);
var webSocket = webSocketContext.WebSocket;
var peerId = _sequencer.Pop();
2025-10-04 15:08:53 +03:00
connection = new WebSocketConnection(webSocket, peerId);
2023-03-06 10:06:43 +04:00
}
catch (Exception ex)
{
_logger.Error(ex);
continue;
}
2025-10-04 15:08:53 +03:00
_connections[connection.Id] = connection;
2025-10-04 15:08:53 +03:00
_ = StartListen(connection, cancellationToken);
2022-12-20 12:20:52 -08:00
}
}
2025-10-04 15:08:53 +03:00
async ValueTask StartListen(WebSocketConnection connection, CancellationToken cancellationToken)
{
_activeConnections.Add(connection);
_networkListener.OnConnected(connection);
var webSocket = connection.Socket;
2024-08-17 10:29:27 +03:00
var rawData = new byte[2048];
var rawDataBuffer = new Memory<byte>(rawData);
var data = new ArrayBufferWriter<byte>();
2025-10-04 15:08:53 +03:00
2024-08-17 10:29:27 +03:00
while (webSocket.State == WebSocketState.Open || !cancellationToken.IsCancellationRequested)
2022-12-20 12:20:52 -08:00
{
try
{
2024-08-17 10:29:27 +03:00
while (true)
2023-10-07 19:30:52 +03:00
{
2024-08-17 10:29:27 +03:00
var result = await webSocket.ReceiveAsync(rawDataBuffer, cancellationToken);
var payload = rawDataBuffer.Slice(0, result.Count);
2025-10-04 15:08:53 +03:00
2024-08-17 10:29:27 +03:00
data.Write(payload.Span);
2025-10-04 15:08:53 +03:00
2024-08-17 10:29:27 +03:00
if (result.EndOfMessage)
break;
}
2025-10-04 15:08:53 +03:00
2024-08-17 10:29:27 +03:00
if (data.WrittenCount > 0)
{
_networkListener.OnData(connection, NetworkChannel.RELIABLE, data.WrittenMemory.ToArray());
2025-10-04 15:08:53 +03:00
2024-08-17 10:29:27 +03:00
data.Clear();
2023-10-07 19:30:52 +03:00
}
}
catch (Exception ex)
{
break;
}
2022-12-20 12:20:52 -08:00
}
_sequencer.Push(connection.Id);
_activeConnections.Remove(connection);
2025-10-04 15:08:53 +03:00
_networkListener.OnDisconnected(connection);
}
2023-03-06 10:06:43 +04:00
public void Update()
{
_executor.Update();
2025-10-04 15:08:53 +03:00
_ = Flush();
}
2023-10-11 19:37:50 +03:00
public void Broadcast(byte[] data, NetworkChannel channel)
2023-10-04 14:42:59 +03:00
{
foreach (var activeConnection in _activeConnections)
2023-10-11 19:37:50 +03:00
activeConnection.Reliable.Send(data);
2023-10-04 14:42:59 +03:00
}
2025-10-04 15:08:53 +03:00
public async ValueTask Flush()
{
foreach (var conn in _activeConnections)
await conn.Flush();
}
public void Listen(
INetworkListener listener,
NetworkConfiguration configuration
)
{
_networkListener = listener;
_cancellationTokenSource = new CancellationTokenSource();
var limit = (ushort)configuration.LimitConnections;
for (ushort i = limit; i != 0; i--)
_sequencer.Push(i);
_sequencer.Push(0);
_connections = new WebSocketConnection[configuration.LimitConnections];
_httpListener = new HttpListener();
_httpListener.Prefixes.Add($"http://{configuration.Address}:{configuration.Port}/");
_httpListener.Start();
_executor.Run(() => StartAccept(_cancellationTokenSource.Token));
var protocolDecoded = RagonVersion.Parse(configuration.Protocol);
_logger.Info($"Listen at http://{configuration.Address}:{configuration.Port}/");
_logger.Info($"Protocol: {protocolDecoded}");
}
public void Stop()
{
_cancellationTokenSource.Cancel();
_httpListener.Stop();
}
2022-12-20 12:20:52 -08:00
}