162 lines
5.7 KiB
C#
162 lines
5.7 KiB
C#
using System;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Lobbies
|
|
{
|
|
internal class TcpLobbbyClient : IDisposable
|
|
{
|
|
internal delegate void DataReceivedEventArgs(int bytes, Memory<byte> data);
|
|
internal event DataReceivedEventArgs? DataReceived;
|
|
|
|
internal delegate void DisconnectedEventArgs(bool clean, string error);
|
|
internal event DisconnectedEventArgs? Disconnected;
|
|
|
|
|
|
internal delegate void ConnectedEventArgs();
|
|
internal event ConnectedEventArgs? Connected;
|
|
|
|
TcpClient? tcpClient;
|
|
NetworkStream? networkStream;
|
|
CancellationTokenSource? cancellationTokenSource = new CancellationTokenSource();
|
|
bool running = false;
|
|
|
|
internal async Task Connect(string host, int port)
|
|
{
|
|
bool wasError = false;
|
|
string error = string.Empty;
|
|
|
|
try
|
|
{
|
|
running = true;
|
|
tcpClient = new TcpClient();
|
|
await tcpClient.ConnectAsync(host, port, cancellationTokenSource!.Token);
|
|
networkStream = tcpClient.GetStream();
|
|
|
|
Memory<byte> buffer = new byte[4096];
|
|
Memory<byte> target = new byte[4096];
|
|
|
|
int currentOffset = 0;
|
|
int currentMessageRemainingLength = 0;
|
|
int currentMessageLength = 0;
|
|
bool validMessage = true;
|
|
int currentReadOffset = 0;
|
|
bool offsetSizeInt = false;
|
|
Connected?.Invoke();
|
|
|
|
while (running)
|
|
{
|
|
int copyOffset = 0;
|
|
int receivedBytes = currentReadOffset;
|
|
|
|
if (currentReadOffset < 4)
|
|
{
|
|
receivedBytes += await networkStream.ReadAsync(buffer.Slice(currentReadOffset), cancellationTokenSource.Token) + currentReadOffset;
|
|
}
|
|
|
|
if (receivedBytes == 0 && running && !cancellationTokenSource.Token.IsCancellationRequested)
|
|
{
|
|
throw new Exception("Connection lost!");
|
|
}
|
|
|
|
if (receivedBytes > 3 || (currentMessageRemainingLength > 0 && receivedBytes > currentMessageRemainingLength))
|
|
{
|
|
currentReadOffset = 0;
|
|
if (currentMessageLength == 0)
|
|
{
|
|
currentMessageRemainingLength = BitConverter.ToInt32(buffer.Span);
|
|
currentMessageLength = currentMessageRemainingLength;
|
|
receivedBytes -= 4;
|
|
copyOffset += 4;
|
|
offsetSizeInt = true;
|
|
}
|
|
else
|
|
offsetSizeInt = false;
|
|
|
|
var receivedCount = Math.Min(receivedBytes, currentMessageRemainingLength);
|
|
receivedBytes -= receivedCount;
|
|
copyOffset += receivedCount;
|
|
|
|
if (validMessage && currentOffset + receivedCount > 0)
|
|
{
|
|
if (currentOffset + receivedCount < target.Length)
|
|
buffer.Slice(offsetSizeInt ? 4 : 0, receivedCount).CopyTo(target.Slice(currentOffset));
|
|
else
|
|
validMessage = false;
|
|
}
|
|
|
|
currentOffset += receivedCount;
|
|
currentMessageRemainingLength -= receivedCount;
|
|
|
|
if (currentMessageRemainingLength <= 0)
|
|
{
|
|
if (validMessage)
|
|
DataReceived?.Invoke(currentMessageLength, target);
|
|
|
|
if (receivedBytes > 0)
|
|
{
|
|
buffer.Slice(copyOffset, receivedBytes).CopyTo(buffer);
|
|
currentReadOffset += receivedBytes;
|
|
}
|
|
|
|
currentOffset = 0;
|
|
currentMessageLength = 0;
|
|
currentMessageRemainingLength = 0;
|
|
validMessage = true;
|
|
}
|
|
}
|
|
else if (receivedBytes > 0)
|
|
{
|
|
currentReadOffset += receivedBytes;
|
|
}
|
|
}
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
error = e.Message;
|
|
}
|
|
finally
|
|
{
|
|
wasError = running;
|
|
|
|
running = false;
|
|
|
|
networkStream?.Dispose();
|
|
tcpClient?.Dispose();
|
|
|
|
tcpClient = null;
|
|
networkStream = null;
|
|
|
|
Disconnected?.Invoke(!wasError, error);
|
|
}
|
|
}
|
|
|
|
internal async Task Send(byte[] buffer, int offset, int count)
|
|
{
|
|
try
|
|
{
|
|
if (running && networkStream != null)
|
|
{
|
|
await networkStream.WriteAsync(BitConverter.GetBytes(count - offset), 0, 4, cancellationTokenSource!.Token);
|
|
await networkStream.WriteAsync(buffer, offset, count, cancellationTokenSource!.Token);
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
internal void Stop()
|
|
{
|
|
running = false;
|
|
cancellationTokenSource?.Cancel();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
cancellationTokenSource?.Cancel();
|
|
tcpClient?.Dispose();
|
|
cancellationTokenSource?.Dispose();
|
|
}
|
|
}
|
|
}
|