1
0
mirror of https://github.com/oliverbooth/TcpDotNet synced 2024-11-23 00:18:46 +00:00

Compare commits

...

6 Commits

6 changed files with 70 additions and 18 deletions

View File

@ -7,7 +7,11 @@ using TcpDotNet.Protocol.Packets.ClientBound;
using TcpDotNet.Protocol.Packets.ServerBound;
using var client = new ProtocolClient();
client.Disconnected += (_, e) => Console.WriteLine($"Disconnected: {e.DisconnectReason}");
client.Disconnected += (_, e) =>
{
Console.WriteLine($"Disconnected: {e.DisconnectReason}");
return Task.CompletedTask;
};
client.RegisterPacketHandler(PacketHandler<PongPacket>.Empty);
client.RegisterPacketHandler(new GoodbyePacketHandler());

View File

@ -5,8 +5,16 @@ using TcpDotNet.Protocol.Packets.ClientBound;
using TcpDotNet.Protocol.Packets.ServerBound;
var listener = new ProtocolListener();
listener.ClientConnected += (_, e) => Console.WriteLine($"Client connected from {e.Client.RemoteEndPoint} with session {e.Client.SessionId}");
listener.ClientDisconnected += (_, e) => Console.WriteLine($"Client {e.Client.SessionId} disconnected ({e.DisconnectReason})");
listener.ClientConnected += (_, e) =>
{
Console.WriteLine($"Client connected from {e.Client.RemoteEndPoint} with session {e.Client.SessionId}");
return Task.CompletedTask;
};
listener.ClientDisconnected += (_, e) =>
{
Console.WriteLine($"Client {e.Client.SessionId} disconnected ({e.DisconnectReason})");
return Task.CompletedTask;
};
listener.RegisterPacketHandler(new HelloPacketHandler());
listener.RegisterPacketHandler(new PingPacketHandler());

View File

@ -0,0 +1,16 @@
namespace TcpDotNet;
/// <summary>
/// Represents the method that will handle an asynchronous event that has no event data.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">An object that contains no event data.</param>
public delegate Task AsyncEventHandler(object? sender, EventArgs e);
/// <summary>
/// Represents the method that will handle an asynchronous event when the event provides data.
/// </summary>
/// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
/// <param name="sender">The source of the event.</param>
/// <param name="e">An object that contains event data.</param>
public delegate Task AsyncEventHandler<in TEventArgs>(object? sender, TEventArgs e);

View File

@ -16,6 +16,7 @@ public abstract class ClientNode : Node
{
private readonly ConcurrentDictionary<int, List<TaskCompletionSource<Packet>>> _packetCompletionSources = new();
private readonly ConcurrentDictionary<long, TaskCompletionSource<ResponsePacket>> _callbackCompletionSources = new();
private EndPoint? _remoteEP;
/// <summary>
@ -140,10 +141,7 @@ public abstract class ClientNode : Node
return null;
}
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
ConstructorInfo? constructor =
packetType.GetConstructors(bindingFlags).FirstOrDefault(c => c.GetParameters().Length == 0);
ConstructorInfo? constructor = GetConstructor(packetType);
if (constructor is null)
return null;
@ -163,6 +161,9 @@ public abstract class ClientNode : Node
}
}
if (packet is ResponsePacket response && _callbackCompletionSources.TryGetValue(response.CallbackId, out TaskCompletionSource<ResponsePacket>? callback))
callback.SetResult(response);
return packet;
}
@ -235,12 +236,16 @@ public abstract class ClientNode : Node
{
if (packet is null) throw new ArgumentNullException(nameof(packet));
long callbackId = packet.CallbackId;
var completionSource = new TaskCompletionSource<ResponsePacket>();
if (!_callbackCompletionSources.TryAdd(packet.CallbackId, completionSource))
if (!_callbackCompletionSources.TryAdd(callbackId, completionSource))
throw new InvalidOperationException("Duplicate packet sent");
await SendPacketAsync(packet, cancellationToken);
return (TReceive)await completionSource.Task;
var response = (TReceive)await completionSource.Task;
_callbackCompletionSources.TryRemove(callbackId, out _);
return response;
}
/// <summary>
@ -259,6 +264,24 @@ public abstract class ClientNode : Node
return WaitForPacketAsync<TPacket>(completionSource, cancellationToken);
}
private static ConstructorInfo? GetConstructor(Type packetType)
{
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
ConstructorInfo[] constructors = packetType.GetConstructors(bindingFlags);
ConstructorInfo? constructor = null;
for (var index = 0; index < constructors.Length; index++)
{
ConstructorInfo current = constructors[index];
if (current.GetParameters().Length == 0)
{
constructor = current;
break;
}
}
return constructor;
}
private async Task<TPacket> WaitForPacketAsync<TPacket>(TaskCompletionSource<Packet> completionSource,
CancellationToken cancellationToken = default)
where TPacket : Packet
@ -295,11 +318,11 @@ public abstract class ClientNode : Node
}
catch (TaskCanceledException)
{
completionSource.SetCanceled();
completionSource.SetCanceled(cancellationToken);
}
}
completionSource.SetCanceled();
completionSource.SetCanceled(cancellationToken);
}, cancellationToken);
var packet = (TPacket)await Task.Run(() => completionSource.Task, cancellationToken);

View File

@ -30,7 +30,7 @@ public sealed class ProtocolClient : ClientNode
/// <summary>
/// Occurs when the client has been disconnected.
/// </summary>
public event EventHandler<DisconnectedEventArgs>? Disconnected;
public event AsyncEventHandler<DisconnectedEventArgs>? Disconnected;
/// <summary>
/// Establishes a connection to a remote host.
@ -95,8 +95,9 @@ public sealed class ProtocolClient : ClientNode
State = ClientState.Handshaking;
var handshakeRequest = new HandshakeRequestPacket(ProtocolVersion);
var handshakeResponse = await SendAndReceiveAsync<HandshakeResponsePacket>(handshakeRequest, cancellationToken);
await SendPacketAsync(handshakeRequest, cancellationToken);
var handshakeResponse = await WaitForPacketAsync<HandshakeResponsePacket>(cancellationToken);
if (handshakeResponse.HandshakeResponse != HandshakeResponse.Success)
{
Close();

View File

@ -27,27 +27,27 @@ public sealed partial class ProtocolListener : Node
/// <summary>
/// Occurs when a client connects to the listener.
/// </summary>
public event EventHandler<ClientConnectedEventArgs>? ClientConnected;
public event AsyncEventHandler<ClientConnectedEventArgs>? ClientConnected;
/// <summary>
/// Occurs when a client disconnects from the listener.
/// </summary>
public event EventHandler<ClientDisconnectedEventArgs>? ClientDisconnected;
public event AsyncEventHandler<ClientDisconnectedEventArgs>? ClientDisconnected;
/// <summary>
/// Occurs when a client sends a packet to the listener.
/// </summary>
public event EventHandler<ClientPacketReceivedEventArgs>? ClientPacketReceived;
public event AsyncEventHandler<ClientPacketReceivedEventArgs>? ClientPacketReceived;
/// <summary>
/// Occurs when the server has started.
/// </summary>
public event EventHandler? Started;
public event AsyncEventHandler? Started;
/// <summary>
/// Occurs when the server has started.
/// </summary>
public event EventHandler? Stopped;
public event AsyncEventHandler? Stopped;
/// <summary>
/// Gets a read-only view of the clients connected to this listener.