mirror of
https://github.com/oliverbooth/TcpDotNet
synced 2024-11-23 00:18:46 +00:00
Compare commits
6 Commits
bf7f14acac
...
d8d4510d0f
Author | SHA1 | Date | |
---|---|---|---|
d8d4510d0f | |||
de672241f7 | |||
50a73d21eb | |||
45a0b3898a | |||
3ac93daf4f | |||
19c83b14a1 |
@ -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());
|
||||
|
@ -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());
|
||||
|
16
TcpDotNet/AsyncEventHandler.cs
Normal file
16
TcpDotNet/AsyncEventHandler.cs
Normal 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);
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user