namespace Tcplib.Server
{
using System;
using System.Net.Sockets;
using System.Threading.Tasks;
/// <summary>
/// Вспомогательный класс содержащий в себе информацию о текущей сессии
/// </summary>
public class Session
{
private const int defaultBufferSize = 1024;
private byte[] _readBuffer;
private byte[] _writeBuffer;
private readonly TcpServer _owner;
private TcpClient _client;
public Guid Guid { get; }
public Session(TcpServer owner, TcpClient client, Guid guid, int readBudderSize = 1024, int writeBufferSize = 1024)
{
_owner = owner;
_client = client;
Guid = guid;
if (readBudderSize <= 0)
readBudderSize = defaultBufferSize;
if (writeBufferSize <= 0)
writeBufferSize = defaultBufferSize;
_readBuffer = new byte[readBudderSize];
_writeBuffer = new byte[writeBufferSize];
}
/// <summary>
/// Метод получения датаграммы от клиента
/// </summary>
/// <param name="retryCount"></param>
/// <param name="retryDelayMS"></param>
/// <returns></returns>
public async Task<ReadOnlyMemory<byte>?> TryReceiveData(int retryCount = 5, int retryDelayMS = 1000)
{
int readed = 0;
try
{
var ns = _client.GetStream();
do
{
readed += await ns.ReadAsync(_readBuffer.AsMemory().Slice(readed));
if (_client.Client.Available == 0 || readed >= _readBuffer.Length)//мы прочитали все данные или в буффере уже нет места
break;
} while (ns.DataAvailable || retryCount > 0);
}
catch (SocketException ex)
{
Console.WriteLine("[WARN] SocketException in TryReceiveData");
Console.WriteLine(ex.ToString());
}
catch (ObjectDisposedException ex)
{
Console.WriteLine("[WARN] ObjectDisposedException in TryReceiveData");
Console.WriteLine(ex.ToString());
}
catch (Exception ex)
{
Console.WriteLine("[WARN] UndefinedException in TryReceiveData");
Console.WriteLine(ex.ToString());
}
return _readBuffer[0..readed];
}
/// <summary>
/// ИИетод отправки датаграммы клиенту
/// </summary>
/// <param name="dataToSend"></param>
/// <param name="retryCount"></param>
/// <param name="retryDelayMS"></param>
/// <returns></returns>
public async Task TrySendData(ReadOnlyMemory<byte> datagramm, int retryCount = 5, int retryDelayMS = 1000)
{
try
{
var ns = _client.GetStream();
do
{
try
{
await ns.WriteAsync(datagramm);
retryCount = 0;
}
catch (SocketException ex)
{
Console.WriteLine("[WARN] SocketException in TrySendData");
Console.WriteLine(ex.ToString());
}
catch (ObjectDisposedException ex)
{
Console.WriteLine("[WARN] ObjectDisposedException in TrySendData");
Console.WriteLine(ex.ToString());
}
catch (Exception ex)
{
Console.WriteLine("[WARN] UndefinedException in TrySendData");
Console.WriteLine(ex.ToString());
}
retryCount--;
await Task.Delay(retryCount);
} while (retryCount > 0);
}
catch (SocketException ex)
{
Console.WriteLine("[WARN] SocketException in TrySendData");
Console.WriteLine(ex.ToString());
}
catch (ObjectDisposedException ex)
{
Console.WriteLine("[WARN] ObjectDisposedException in TrySendData");
Console.WriteLine(ex.ToString());
}
catch (Exception ex)
{
Console.WriteLine("[WARN] UndefinedException in TrySendData");
Console.WriteLine(ex.ToString());
}
}
public void StopSession()
{
try
{
_client.Close();
_client.Dispose();
}
catch (SocketException ex)
{
Console.WriteLine("[WARN] SocketException in StopSession");
Console.WriteLine(ex.ToString());
}
catch (ObjectDisposedException ex)
{
Console.WriteLine("[WARN] ObjectDisposedException in StopSession");
Console.WriteLine(ex.ToString());
}
catch (Exception ex)
{
Console.WriteLine("[WARN] UndefinedException in StopSession");
Console.WriteLine(ex.ToString());
}
}
}
}
namespace Tcplib.Server
{
using System.Net.Sockets;
using System.Threading;
using System.Collections.Concurrent;
using System;
using System.Threading.Tasks;
public class TcpServer
{
#region Events
public event ServerStartedHandler ServerStarted;
public event ServerStopedHandler ServerStoped;
public event ClientConnectedHandler ClientAccepted;
public event ClientDisconnectedHandler ClientDisconnected;
#endregion Events
#region ServerSettings
private readonly ServerConfig _config;
#endregion ServerSettings
#region ServerFields
private readonly TcpListener _listener;
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly int _maxConnections;
private readonly ConcurrentDictionary<Guid, Session> _sessions = new ConcurrentDictionary<Guid, Session>();
#endregion
#region ServerProperties
public bool IsStarted { get; private set; }
#endregion ServerProperties
public TcpServer(ServerConfig config)
{
_config = config;
_listener = new TcpListener(_config.ListeningEndPoint);
_maxConnections = _config.MaxConnections;
_cancellationTokenSource = new CancellationTokenSource();
}
#region Server public methods
public async void Start()
{
if(IsStarted)
return;
try{
var token = _cancellationTokenSource.Token;
_listener.Start();
IsStarted = true;
await Task.Run(() => AcceptCients(token));
}
catch(Exception ex)
{
Console.WriteLine("[WARN] UndefinedException in Start");
Console.WriteLine(ex.ToString());
}
}
public void Stop()
{
if(!IsStarted)
return;
try
{
_listener.Stop();
foreach(var c in _sessions.Values)
{
try{
c.StopSession();
}
catch(Exception ex)
{
Console.WriteLine("[WARN] UndefinedException in Start");
Console.WriteLine(ex.ToString());
}
}
_sessions.Clear();
}
catch (SocketException ex)
{
Console.WriteLine("[WARN] SocketException in Stop");
Console.WriteLine(ex.ToString());
}
catch (ObjectDisposedException ex)
{
Console.WriteLine("[WARN] ObjectDisposedException in Stop");
Console.WriteLine(ex.ToString());
}
catch (Exception ex)
{
Console.WriteLine("[WARN] UndefinedException in Stop");
Console.WriteLine(ex.ToString());
}
}
#endregion
#region Server private methods
private async Task AcceptCients(CancellationToken cancellationToken){
try{
while (IsStarted && !cancellationToken.IsCancellationRequested)
{
var acceptedClient = await _listener.AcceptTcpClientAsync();
Guid clientGuid = Guid.NewGuid();
var session = new Session(this, acceptedClient,clientGuid);
if(!_sessions.TryAdd(clientGuid, session))
{
session.StopSession();
continue;
}
ProcessSession(session);
}
}
catch (SocketException ex)
{
Console.WriteLine("[WARN] SocketException in Stop");
Console.WriteLine(ex.ToString());
}
catch (ObjectDisposedException ex)
{
Console.WriteLine("[WARN] ObjectDisposedException in Stop");
Console.WriteLine(ex.ToString());
}
catch (Exception ex)
{
Console.WriteLine("[WARN] UndefinedException in Stop");
Console.WriteLine(ex.ToString());
}
}
public Action<Session> ProcessSessionHandler;
private async void ProcessSession(Session session)
{
if(ProcessSessionHandler==null)
return;
await Task.Run(()=> ProcessSessionHandler(session));
try
{
session.StopSession();
}
catch(Exception ex)
{
Console.WriteLine("[WARN] UndefinedException in ProcessSession");
}
if(!_sessions.TryRemove(session.Guid, out var s))
{
Console.WriteLine($"Cant remove session {session.Guid}");
}
}
#endregion Server private methods
}
}