2023-03-06 10:06:43 +04:00
|
|
|
/*
|
|
|
|
|
* Copyright 2023 Eduard Kargin <kargin.eduard@gmail.com>
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2022-12-20 12:20:52 -08:00
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.WebSockets;
|
|
|
|
|
using NLog;
|
2023-03-06 10:06:43 +04:00
|
|
|
using Ragon.Protocol;
|
2023-04-09 11:06:52 +04:00
|
|
|
using Ragon.Server.IO;
|
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
|
|
|
{
|
2023-03-06 10:06:43 +04:00
|
|
|
public Executor Executor => _executor;
|
2023-10-11 19:37:50 +03:00
|
|
|
|
2022-12-25 03:13:01 -08:00
|
|
|
private ILogger _logger = LogManager.GetCurrentClassLogger();
|
|
|
|
|
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()
|
2022-12-25 03:13:01 -08:00
|
|
|
{
|
|
|
|
|
_sequencer = new Stack<ushort>();
|
|
|
|
|
_connections = Array.Empty<WebSocketConnection>();
|
|
|
|
|
_activeConnections = new List<WebSocketConnection>();
|
2023-03-06 10:06:43 +04:00
|
|
|
_executor = new Executor();
|
2022-12-25 03:13:01 -08:00
|
|
|
}
|
2023-10-11 19:37:50 +03:00
|
|
|
|
2022-12-25 03:13:01 -08:00
|
|
|
public async void StartAccept(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
while (!cancellationToken.IsCancellationRequested)
|
2022-12-20 12:20:52 -08:00
|
|
|
{
|
2023-10-19 20:20:40 +03:00
|
|
|
WebSocketConnection connection = null!;
|
|
|
|
|
try
|
2023-03-06 10:06:43 +04:00
|
|
|
{
|
2023-10-19 20:20:40 +03:00
|
|
|
var context = await _httpListener.GetContextAsync();
|
|
|
|
|
if (!context.Request.IsWebSocketRequest)
|
|
|
|
|
{
|
|
|
|
|
context.Response.StatusCode = 200;
|
|
|
|
|
context.Response.ContentLength64 = 0;
|
|
|
|
|
context.Response.Close();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var webSocketContext = await context.AcceptWebSocketAsync(null);
|
|
|
|
|
var webSocket = webSocketContext.WebSocket;
|
|
|
|
|
var peerId = _sequencer.Pop();
|
|
|
|
|
|
|
|
|
|
connection = new WebSocketConnection(webSocket, peerId);
|
2023-03-06 10:06:43 +04:00
|
|
|
}
|
2023-10-19 20:20:40 +03:00
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.Warn(ex);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_connections[connection.Id] = connection;
|
|
|
|
|
|
2022-12-25 03:13:01 -08:00
|
|
|
StartListen(connection, cancellationToken);
|
2022-12-20 12:20:52 -08:00
|
|
|
}
|
2022-12-25 03:13:01 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async void StartListen(WebSocketConnection connection, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
_activeConnections.Add(connection);
|
|
|
|
|
_networkListener.OnConnected(connection);
|
|
|
|
|
|
|
|
|
|
var webSocket = connection.Socket;
|
|
|
|
|
var bytes = new byte[2048];
|
|
|
|
|
var buffer = new Memory<byte>(bytes);
|
2023-10-14 21:35:51 +03:00
|
|
|
|
2022-12-25 03:13:01 -08:00
|
|
|
while (
|
|
|
|
|
webSocket.State == WebSocketState.Open ||
|
|
|
|
|
!cancellationToken.IsCancellationRequested)
|
2022-12-20 12:20:52 -08:00
|
|
|
{
|
2022-12-25 03:13:01 -08:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var result = await webSocket.ReceiveAsync(buffer, cancellationToken);
|
2023-10-07 19:30:52 +03:00
|
|
|
if (result.Count > 0)
|
|
|
|
|
{
|
2023-10-14 21:35:51 +03:00
|
|
|
var payload = buffer.Slice(0, result.Count);
|
2023-10-11 19:37:50 +03:00
|
|
|
_networkListener.OnData(connection, NetworkChannel.RELIABLE, payload.ToArray());
|
2023-10-07 19:30:52 +03:00
|
|
|
}
|
2022-12-25 03:13:01 -08:00
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-12-20 12:20:52 -08:00
|
|
|
}
|
|
|
|
|
|
2022-12-25 03:13:01 -08:00
|
|
|
_sequencer.Push(connection.Id);
|
|
|
|
|
_activeConnections.Remove(connection);
|
2023-10-19 20:20:40 +03:00
|
|
|
|
2022-12-25 03:13:01 -08:00
|
|
|
_networkListener.OnDisconnected(connection);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 10:06:43 +04:00
|
|
|
public void Update()
|
2022-12-25 03:13:01 -08: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
|
|
|
}
|
|
|
|
|
|
2022-12-25 03:13:01 -08:00
|
|
|
public async void Flush()
|
|
|
|
|
{
|
|
|
|
|
foreach (var conn in _activeConnections)
|
|
|
|
|
await conn.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Start(
|
|
|
|
|
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();
|
2023-03-06 10:06:43 +04:00
|
|
|
_httpListener.Prefixes.Add($"http://+:{configuration.Port}/");
|
2022-12-25 03:13:01 -08:00
|
|
|
_httpListener.Start();
|
|
|
|
|
|
|
|
|
|
_executor.Run(() => StartAccept(_cancellationTokenSource.Token));
|
|
|
|
|
|
|
|
|
|
var protocolDecoded = RagonVersion.Parse(configuration.Protocol);
|
2023-03-06 10:06:43 +04:00
|
|
|
_logger.Info($"Listen at http://0.0.0.0:{configuration.Port}/");
|
2022-12-25 03:13:01 -08:00
|
|
|
_logger.Info($"Protocol: {protocolDecoded}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
|
|
|
|
_cancellationTokenSource.Cancel();
|
|
|
|
|
_httpListener.Stop();
|
|
|
|
|
}
|
2022-12-20 12:20:52 -08:00
|
|
|
}
|