mirror of
https://github.com/oliverbooth/TcpDotNet
synced 2024-11-10 07:55:42 +00:00
Merge 45111aae72
into 87cb461872
This commit is contained in:
commit
0db6fd2976
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1 +1,2 @@
|
|||||||
|
ko_fi: oliverbooth
|
||||||
custom: ['https://buymeacoffee.com/oliverbooth']
|
custom: ['https://buymeacoffee.com/oliverbooth']
|
||||||
|
12
.github/workflows/dotnet.yml
vendored
12
.github/workflows/dotnet.yml
vendored
@ -2,9 +2,13 @@ name: .NET
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main, develop ]
|
branches:
|
||||||
|
- '*'
|
||||||
|
- '*/*'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main, develop ]
|
branches:
|
||||||
|
- '*'
|
||||||
|
- '*/*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -16,9 +20,9 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v2
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Add NuGet source
|
- name: Add NuGet source
|
||||||
run: dotnet nuget add source --username oliverbooth --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/oliverbooth/index.json"
|
run: dotnet nuget add source --username oliverbooth --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/oliverbooth/index.json"
|
||||||
|
7
.github/workflows/nightly.yml
vendored
7
.github/workflows/nightly.yml
vendored
@ -3,7 +3,8 @@ name: Publish Nightly
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- develop
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
nightly:
|
nightly:
|
||||||
@ -15,9 +16,9 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v2
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Add GitHub NuGet source
|
- name: Add GitHub NuGet source
|
||||||
run: dotnet nuget add source --username oliverbooth --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/oliverbooth/index.json"
|
run: dotnet nuget add source --username oliverbooth --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/oliverbooth/index.json"
|
||||||
|
4
.github/workflows/prerelease.yml
vendored
4
.github/workflows/prerelease.yml
vendored
@ -15,9 +15,9 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v2
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Add GitHub NuGet source
|
- name: Add GitHub NuGet source
|
||||||
run: dotnet nuget add source --username oliverbooth --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/oliverbooth/index.json"
|
run: dotnet nuget add source --username oliverbooth --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/oliverbooth/index.json"
|
||||||
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -15,9 +15,9 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v2
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Add GitHub NuGet source
|
- name: Add GitHub NuGet source
|
||||||
run: dotnet nuget add source --username oliverbooth --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/oliverbooth/index.json"
|
run: dotnet nuget add source --username oliverbooth --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/oliverbooth/index.json"
|
||||||
|
18
README.md
18
README.md
@ -1,15 +1,15 @@
|
|||||||
<h1 align="center">TcpDotNet</h1>
|
<h1 align="center"><img src="branding_Banner.png" alt="TCP.NET"></h1>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/oliverbooth/TcpDotNet/actions/workflows/dotnet.yml"><img src="https://img.shields.io/github/actions/workflow/status/oliverbooth/TcpDotNet/dotnet.yml?branch=main" alt="GitHub Workflow Status" title="GitHub Workflow Status"></a>
|
<a href="https://github.com/oliverbooth/TcpDotNet/actions/workflows/dotnet.yml"><img src="https://img.shields.io/github/actions/workflow/status/oliverbooth/TcpDotNet/dotnet.yml?branch=main&style=flat-square" alt="GitHub Workflow Status" title="GitHub Workflow Status"></a>
|
||||||
<a href="https://github.com/oliverbooth/TcpDotNet/issues"><img src="https://img.shields.io/github/issues/oliverbooth/TcpDotNet" alt="GitHub Issues" title="GitHub Issues"></a>
|
<a href="https://github.com/oliverbooth/TcpDotNet/issues"><img src="https://img.shields.io/github/issues/oliverbooth/TcpDotNet?style=flat-square" alt="GitHub Issues" title="GitHub Issues"></a>
|
||||||
<a href="https://www.nuget.org/packages/TcpDotNet/"><img src="https://img.shields.io/nuget/dt/TcpDotNet" alt="NuGet Downloads" title="NuGet Downloads"></a>
|
<a href="https://www.nuget.org/packages/TcpDotNet/"><img src="https://img.shields.io/nuget/dt/TcpDotNet?style=flat-square" alt="NuGet Downloads" title="NuGet Downloads"></a>
|
||||||
<a href="https://www.nuget.org/packages/TcpDotNet/"><img src="https://img.shields.io/nuget/v/TcpDotNet?label=stable" alt="Stable Version" title="Stable Version"></a>
|
<a href="https://www.nuget.org/packages/TcpDotNet/"><img src="https://img.shields.io/nuget/v/TcpDotNet?label=stable&style=flat-square" alt="Stable Version" title="Stable Version"></a>
|
||||||
<a href="https://www.nuget.org/packages/TcpDotNet/"><img src="https://img.shields.io/nuget/vpre/TcpDotNet?label=nightly" alt="Nightly Version" title="Nightly Version"></a>
|
<a href="https://www.nuget.org/packages/TcpDotNet/"><img src="https://img.shields.io/nuget/vpre/TcpDotNet?label=nightly&style=flat-square" alt="Nightly Version" title="Nightly Version"></a>
|
||||||
<a href="https://github.com/oliverbooth/TcpDotNet/blob/master/LICENSE.md"><img src="https://img.shields.io/github/license/oliverbooth/TcpDotNet" alt="MIT License" title="MIT License"></a>
|
<a href="https://github.com/oliverbooth/TcpDotNet/blob/master/LICENSE.md"><img src="https://img.shields.io/github/license/oliverbooth/TcpDotNet?style=flat-square" alt="MIT License" title="MIT License"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### About
|
### About
|
||||||
TcpDotNet is a .NET Standard 2.1 package that enables you to perform server/client communications over the TCP protocol.
|
TcpDotNet is a .NET package that enables you to perform server/client communications over the TCP protocol.
|
||||||
This package is a work-in-progress and not deemed ready for production use. However, it is available on NuGet as a very crude and early development build. This package will be maintained as I see fit.
|
This package is a work-in-progress and not deemed ready for production use. However, it is available on NuGet as a very crude and early development build. This package will be maintained as I see fit.
|
||||||
|
|
||||||
*(I'm also [dogfooding](https://www.pcmag.com/encyclopedia/term/dogfooding) this library, so there's that.)*
|
*(I'm also [dogfooding](https://www.pcmag.com/encyclopedia/term/dogfooding) this library, so there's that.)*
|
||||||
@ -17,7 +17,7 @@ This package is a work-in-progress and not deemed ready for production use. Howe
|
|||||||
## Installation
|
## Installation
|
||||||
### NuGet installation
|
### NuGet installation
|
||||||
```ps
|
```ps
|
||||||
Install-Package TcpDotNet -Version 0.1.0-nightly.1
|
Install-Package TcpDotNet -Version 1.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
### Manual installation
|
### Manual installation
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using TcpDotNet.Protocol;
|
using TcpDotNet.Protocol;
|
||||||
|
|
||||||
namespace TcpDotNet.ClientIntegrationTest;
|
namespace TcpDotNet.ClientIntegrationTest;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using TcpDotNet.Protocol;
|
using TcpDotNet.Protocol;
|
||||||
|
|
||||||
namespace TcpDotNet.ClientIntegrationTest;
|
namespace TcpDotNet.ClientIntegrationTest;
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
using TcpDotNet.Protocol;
|
using TcpDotNet.Protocol;
|
||||||
|
|
||||||
namespace TcpDotNet.ClientIntegrationTest.PacketHandlers;
|
namespace TcpDotNet.ClientIntegrationTest.PacketHandlers;
|
||||||
|
|
||||||
internal sealed class GoodbyePacketHandler : PacketHandler<GoodbyePacket>
|
internal sealed class GoodbyePacketHandler : PacketHandler<GoodbyePacket>
|
||||||
{
|
{
|
||||||
public override Task HandleAsync(BaseClientNode recipient, GoodbyePacket packet, CancellationToken cancellationToken = default)
|
public override Task HandleAsync(ClientNode recipient, GoodbyePacket packet, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Server sent {packet.Message}");
|
Console.WriteLine($"Server sent {packet.Message}");
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using TcpDotNet;
|
using TcpDotNet;
|
||||||
using TcpDotNet.ClientIntegrationTest;
|
using TcpDotNet.ClientIntegrationTest;
|
||||||
using TcpDotNet.ClientIntegrationTest.PacketHandlers;
|
using TcpDotNet.ClientIntegrationTest.PacketHandlers;
|
||||||
@ -17,7 +17,7 @@ Console.WriteLine($"Connected to {client.RemoteEndPoint}. My session is {client.
|
|||||||
|
|
||||||
var ping = new PingPacket();
|
var ping = new PingPacket();
|
||||||
Console.WriteLine($"Sending ping packet with payload: {BitConverter.ToString(ping.Payload)}");
|
Console.WriteLine($"Sending ping packet with payload: {BitConverter.ToString(ping.Payload)}");
|
||||||
var pong = await client.SendAndReceiveAsync<PingPacket, PongPacket>(ping);
|
var pong = await client.SendAndReceiveAsync<PongPacket>(ping);
|
||||||
|
|
||||||
Console.WriteLine($"Received pong packet with payload: {BitConverter.ToString(pong.Payload)}");
|
Console.WriteLine($"Received pong packet with payload: {BitConverter.ToString(pong.Payload)}");
|
||||||
Console.WriteLine(pong.Payload.SequenceEqual(ping.Payload) ? "Payload matches!" : "Payload does not match!");
|
Console.WriteLine(pong.Payload.SequenceEqual(ping.Payload) ? "Payload matches!" : "Payload does not match!");
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using TcpDotNet.Protocol;
|
using TcpDotNet.Protocol;
|
||||||
|
|
||||||
namespace TcpDotNet.ListenerIntegrationTest;
|
namespace TcpDotNet.ListenerIntegrationTest;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using TcpDotNet.Protocol;
|
using TcpDotNet.Protocol;
|
||||||
|
|
||||||
namespace TcpDotNet.ListenerIntegrationTest;
|
namespace TcpDotNet.ListenerIntegrationTest;
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
using TcpDotNet.Protocol;
|
using TcpDotNet.Protocol;
|
||||||
|
|
||||||
namespace TcpDotNet.ListenerIntegrationTest.PacketHandlers;
|
namespace TcpDotNet.ListenerIntegrationTest.PacketHandlers;
|
||||||
|
|
||||||
internal sealed class HelloPacketHandler : PacketHandler<HelloPacket>
|
internal sealed class HelloPacketHandler : PacketHandler<HelloPacket>
|
||||||
{
|
{
|
||||||
public override Task HandleAsync(BaseClientNode recipient, HelloPacket packet, CancellationToken cancellationToken = default)
|
public override Task HandleAsync(ClientNode recipient, HelloPacket packet, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Client sent {packet.Message}");
|
Console.WriteLine($"Client sent {packet.Message}");
|
||||||
return recipient.SendPacketAsync(new GoodbyePacket {Message = "Goodbye!"}, cancellationToken);
|
return recipient.SendPacketAsync(new GoodbyePacket {Message = "Goodbye!"}, cancellationToken);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using TcpDotNet;
|
using TcpDotNet;
|
||||||
using TcpDotNet.ListenerIntegrationTest.PacketHandlers;
|
using TcpDotNet.ListenerIntegrationTest.PacketHandlers;
|
||||||
using TcpDotNet.Protocol;
|
using TcpDotNet.Protocol;
|
||||||
using TcpDotNet.Protocol.Packets.ClientBound;
|
using TcpDotNet.Protocol.Packets.ClientBound;
|
||||||
@ -16,7 +16,7 @@ await Task.Delay(-1);
|
|||||||
|
|
||||||
internal sealed class PingPacketHandler : PacketHandler<PingPacket>
|
internal sealed class PingPacketHandler : PacketHandler<PingPacket>
|
||||||
{
|
{
|
||||||
public override async Task HandleAsync(BaseClientNode recipient, PingPacket packet, CancellationToken cancellationToken = default)
|
public override async Task HandleAsync(ClientNode recipient, PingPacket packet, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Client {recipient.SessionId} sent ping with payload {BitConverter.ToString(packet.Payload)}");
|
Console.WriteLine($"Client {recipient.SessionId} sent ping with payload {BitConverter.ToString(packet.Payload)}");
|
||||||
var pong = new PongPacket(packet.CallbackId, packet.Payload);
|
var pong = new PongPacket(packet.CallbackId, packet.Payload);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -6,6 +6,23 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TcpDotNet.ListenerIntegrati
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TcpDotNet.ClientIntegrationTest", "TcpDotNet.ClientIntegrationTest\TcpDotNet.ClientIntegrationTest.csproj", "{ED9C812F-9835-4268-9AFC-57CFAAC16162}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TcpDotNet.ClientIntegrationTest", "TcpDotNet.ClientIntegrationTest\TcpDotNet.ClientIntegrationTest.csproj", "{ED9C812F-9835-4268-9AFC-57CFAAC16162}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D55FB87A-1066-489D-B0CC-F2C09B611751}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.gitignore = .gitignore
|
||||||
|
LICENSE.md = LICENSE.md
|
||||||
|
README.md = README.md
|
||||||
|
branding_Banner.png = branding_Banner.png
|
||||||
|
branding_Icon.png = branding_Icon.png
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{0FAB77D5-AAE4-4722-A5F4-88A239858D65}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
|
||||||
|
.github\workflows\nightly.yml = .github\workflows\nightly.yml
|
||||||
|
.github\workflows\prerelease.yml = .github\workflows\prerelease.yml
|
||||||
|
.github\workflows\release.yml = .github\workflows\release.yml
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -1 +1 @@
|
|||||||
[assembly: CLSCompliant(true)]
|
[assembly: CLSCompliant(true)]
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using Chilkat;
|
using Chilkat;
|
||||||
using TcpDotNet.Protocol;
|
using TcpDotNet.Protocol;
|
||||||
using Stream = System.IO.Stream;
|
using Stream = System.IO.Stream;
|
||||||
@ -13,16 +12,16 @@ namespace TcpDotNet;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a client node.
|
/// Represents a client node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseClientNode : Node
|
public abstract class ClientNode : Node
|
||||||
{
|
{
|
||||||
private readonly ObjectIDGenerator _callbackIdGenerator = new();
|
|
||||||
private readonly ConcurrentDictionary<int, List<TaskCompletionSource<Packet>>> _packetCompletionSources = new();
|
private readonly ConcurrentDictionary<int, List<TaskCompletionSource<Packet>>> _packetCompletionSources = new();
|
||||||
|
private readonly ConcurrentDictionary<long, TaskCompletionSource<ResponsePacket>> _callbackCompletionSources = new();
|
||||||
private EndPoint? _remoteEP;
|
private EndPoint? _remoteEP;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BaseClientNode" /> class.
|
/// Initializes a new instance of the <see cref="ClientNode" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected BaseClientNode()
|
protected ClientNode()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +147,7 @@ public abstract class BaseClientNode : Node
|
|||||||
if (constructor is null)
|
if (constructor is null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var packet = (Packet) constructor.Invoke(null);
|
var packet = (Packet)constructor.Invoke(null);
|
||||||
packet.Deserialize(bufferReader);
|
packet.Deserialize(bufferReader);
|
||||||
await targetStream.DisposeAsync();
|
await targetStream.DisposeAsync();
|
||||||
|
|
||||||
@ -198,7 +197,7 @@ public abstract class BaseClientNode : Node
|
|||||||
buffer.Position = 0;
|
buffer.Position = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var length = (int) buffer.Length;
|
var length = (int)buffer.Length;
|
||||||
networkWriter.Write(length);
|
networkWriter.Write(length);
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -219,53 +218,29 @@ public abstract class BaseClientNode : Node
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sends a packet, and waits for a specific packet to be received.
|
/// Sends a packet, and waits for a specific packet to be received.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="packetToSend">The packet to send.</param>
|
/// <param name="packet">The packet to send.</param>
|
||||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
/// <param name="cancellationToken">
|
||||||
/// <typeparam name="TSend">The type of the packet to send.</typeparam>
|
/// A cancellation token that can be used to cancel the asynchronous operation.
|
||||||
|
/// </param>
|
||||||
/// <typeparam name="TReceive">The type of the packet to return.</typeparam>
|
/// <typeparam name="TReceive">The type of the packet to return.</typeparam>
|
||||||
/// <returns>The received packet.</returns>
|
/// <returns>The received packet.</returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This method will consume all incoming packets, raising their associated handlers if such packets are recognised.
|
/// This method will consume all incoming packets, raising their associated handlers if such packets are
|
||||||
|
/// recognised.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public async Task<TReceive> SendAndReceiveAsync<TSend, TReceive>(TSend packetToSend,
|
/// <exception cref="ArgumentNullException"><paramref name="packet" /> is <see langword="null" />.</exception>
|
||||||
|
public async Task<TReceive> SendAndReceiveAsync<TReceive>(RequestPacket packet,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
where TSend : Packet
|
where TReceive : ResponsePacket
|
||||||
where TReceive : Packet
|
|
||||||
{
|
{
|
||||||
var attribute = typeof(TReceive).GetCustomAttribute<PacketAttribute>();
|
if (packet is null) throw new ArgumentNullException(nameof(packet));
|
||||||
if (attribute is null)
|
|
||||||
throw new ArgumentException($"The packet type {typeof(TReceive).Name} is not a valid packet.");
|
|
||||||
|
|
||||||
var requestPacket = packetToSend as RequestPacket;
|
var completionSource = new TaskCompletionSource<ResponsePacket>();
|
||||||
if (requestPacket is not null)
|
if (!_callbackCompletionSources.TryAdd(packet.CallbackId, completionSource))
|
||||||
requestPacket.CallbackId = _callbackIdGenerator.GetId(packetToSend, out _);
|
throw new InvalidOperationException("Duplicate packet sent");
|
||||||
|
|
||||||
var completionSource = new TaskCompletionSource<Packet>();
|
await SendPacketAsync(packet, cancellationToken);
|
||||||
if (!_packetCompletionSources.TryGetValue(attribute.Id, out List<TaskCompletionSource<Packet>>? completionSources))
|
return (TReceive)await completionSource.Task;
|
||||||
{
|
|
||||||
completionSources = new List<TaskCompletionSource<Packet>>();
|
|
||||||
_packetCompletionSources.TryAdd(attribute.Id, completionSources);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (completionSources)
|
|
||||||
{
|
|
||||||
if (!completionSources.Contains(completionSource))
|
|
||||||
completionSources.Add(completionSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
await SendPacketAsync(packetToSend, cancellationToken);
|
|
||||||
TReceive response;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
response = await WaitForPacketAsync<TReceive>(completionSource, cancellationToken);
|
|
||||||
if (requestPacket is null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (response is ResponsePacket responsePacket && responsePacket.CallbackId == requestPacket.CallbackId)
|
|
||||||
break;
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -284,17 +259,16 @@ public abstract class BaseClientNode : Node
|
|||||||
return WaitForPacketAsync<TPacket>(completionSource, cancellationToken);
|
return WaitForPacketAsync<TPacket>(completionSource, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<TPacket> WaitForPacketAsync<TPacket>(
|
private async Task<TPacket> WaitForPacketAsync<TPacket>(TaskCompletionSource<Packet> completionSource,
|
||||||
TaskCompletionSource<Packet> completionSource,
|
CancellationToken cancellationToken = default)
|
||||||
CancellationToken cancellationToken = default
|
|
||||||
)
|
|
||||||
where TPacket : Packet
|
where TPacket : Packet
|
||||||
{
|
{
|
||||||
var attribute = typeof(TPacket).GetCustomAttribute<PacketAttribute>();
|
var attribute = typeof(TPacket).GetCustomAttribute<PacketAttribute>();
|
||||||
if (attribute is null)
|
if (attribute is null)
|
||||||
throw new ArgumentException($"The packet type {typeof(TPacket).Name} is not a valid packet.");
|
throw new ArgumentException($"The packet type {typeof(TPacket).Name} is not a valid packet.");
|
||||||
|
|
||||||
if (!_packetCompletionSources.TryGetValue(attribute.Id, out List<TaskCompletionSource<Packet>>? completionSources))
|
if (!_packetCompletionSources.TryGetValue(attribute.Id,
|
||||||
|
out List<TaskCompletionSource<Packet>>? completionSources))
|
||||||
{
|
{
|
||||||
completionSources = new List<TaskCompletionSource<Packet>>();
|
completionSources = new List<TaskCompletionSource<Packet>>();
|
||||||
_packetCompletionSources.TryAdd(attribute.Id, completionSources);
|
_packetCompletionSources.TryAdd(attribute.Id, completionSources);
|
||||||
@ -328,7 +302,7 @@ public abstract class BaseClientNode : Node
|
|||||||
completionSource.SetCanceled();
|
completionSource.SetCanceled();
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
|
|
||||||
var packet = (TPacket) await Task.Run(() => completionSource.Task, cancellationToken);
|
var packet = (TPacket)await Task.Run(() => completionSource.Task, cancellationToken);
|
||||||
if (_packetCompletionSources.TryGetValue(attribute.Id, out completionSources))
|
if (_packetCompletionSources.TryGetValue(attribute.Id, out completionSources))
|
||||||
{
|
{
|
||||||
lock (completionSources)
|
lock (completionSources)
|
@ -1,7 +1,7 @@
|
|||||||
namespace TcpDotNet;
|
namespace TcpDotNet;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An enumeration of states for a <see cref="BaseClientNode" /> to be in.
|
/// An enumeration of states for a <see cref="ClientNode" /> to be in.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ClientState
|
public enum ClientState
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Chilkat;
|
using Chilkat;
|
||||||
|
|
||||||
namespace TcpDotNet;
|
namespace TcpDotNet;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@ -38,7 +38,8 @@ public abstract class Node : IDisposable
|
|||||||
/// <value>The registered packets.</value>
|
/// <value>The registered packets.</value>
|
||||||
public IReadOnlyDictionary<Type, IReadOnlyCollection<PacketHandler>> RegisteredPacketHandlers =>
|
public IReadOnlyDictionary<Type, IReadOnlyCollection<PacketHandler>> RegisteredPacketHandlers =>
|
||||||
new ReadOnlyDictionary<Type, IReadOnlyCollection<PacketHandler>>(
|
new ReadOnlyDictionary<Type, IReadOnlyCollection<PacketHandler>>(
|
||||||
_registeredPacketHandlers.ToDictionary(p => p.Key, p => (IReadOnlyCollection<PacketHandler>) p.Value.AsReadOnly()));
|
_registeredPacketHandlers.ToDictionary(p => p.Key,
|
||||||
|
p => (IReadOnlyCollection<PacketHandler>)p.Value.AsReadOnly()));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Closes the base socket connection and releases all associated resources.
|
/// Closes the base socket connection and releases all associated resources.
|
||||||
@ -124,7 +125,8 @@ public abstract class Node : IDisposable
|
|||||||
var attribute = packetType.GetCustomAttribute<PacketAttribute>();
|
var attribute = packetType.GetCustomAttribute<PacketAttribute>();
|
||||||
if (attribute is null) throw new ArgumentException($"{packetType.Name} is not a valid packet.");
|
if (attribute is null) throw new ArgumentException($"{packetType.Name} is not a valid packet.");
|
||||||
if (_registeredPackets.TryGetValue(attribute.Id, out Type? registeredPacket))
|
if (_registeredPackets.TryGetValue(attribute.Id, out Type? registeredPacket))
|
||||||
throw new ArgumentException($"The packet type {attribute.Id:X8} is already registered to {registeredPacket.Name}.");
|
throw new ArgumentException(
|
||||||
|
$"The packet type {attribute.Id:X8} is already registered to {registeredPacket.Name}.");
|
||||||
|
|
||||||
_registeredPackets.TryAdd(attribute.Id, packetType);
|
_registeredPackets.TryAdd(attribute.Id, packetType);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace TcpDotNet.Protocol;
|
namespace TcpDotNet.Protocol;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An enumeration of handshake responses.
|
/// An enumeration of handshake responses.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace TcpDotNet.Protocol;
|
namespace TcpDotNet.Protocol;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace TcpDotNet.Protocol;
|
namespace TcpDotNet.Protocol;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies metadata for a <see cref="Packet" />.
|
/// Specifies metadata for a <see cref="Packet" />.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace TcpDotNet.Protocol;
|
namespace TcpDotNet.Protocol;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the base class for a packet handler.
|
/// Represents the base class for a packet handler.
|
||||||
@ -12,7 +12,7 @@ public abstract class PacketHandler
|
|||||||
/// <param name="recipient">The recipient of the packet.</param>
|
/// <param name="recipient">The recipient of the packet.</param>
|
||||||
/// <param name="packet">The packet to handle.</param>
|
/// <param name="packet">The packet to handle.</param>
|
||||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
||||||
public abstract Task HandleAsync(BaseClientNode recipient, Packet packet, CancellationToken cancellationToken = default);
|
public abstract Task HandleAsync(ClientNode recipient, Packet packet, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -27,7 +27,7 @@ public abstract class PacketHandler<T> : PacketHandler
|
|||||||
public static readonly PacketHandler<T> Empty = new NullPacketHandler<T>();
|
public static readonly PacketHandler<T> Empty = new NullPacketHandler<T>();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task HandleAsync(BaseClientNode recipient, Packet packet, CancellationToken cancellationToken = default)
|
public override Task HandleAsync(ClientNode recipient, Packet packet, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (packet is T actual) return HandleAsync(recipient, actual, cancellationToken);
|
if (packet is T actual) return HandleAsync(recipient, actual, cancellationToken);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@ -39,7 +39,7 @@ public abstract class PacketHandler<T> : PacketHandler
|
|||||||
/// <param name="recipient">The recipient of the packet.</param>
|
/// <param name="recipient">The recipient of the packet.</param>
|
||||||
/// <param name="packet">The packet to handle.</param>
|
/// <param name="packet">The packet to handle.</param>
|
||||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
||||||
public abstract Task HandleAsync(BaseClientNode recipient, T packet, CancellationToken cancellationToken = default);
|
public abstract Task HandleAsync(ClientNode recipient, T packet, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -50,7 +50,7 @@ internal sealed class NullPacketHandler<T> : PacketHandler<T>
|
|||||||
where T : Packet
|
where T : Packet
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task HandleAsync(BaseClientNode recipient, T packet, CancellationToken cancellationToken = default)
|
public override Task HandleAsync(ClientNode recipient, T packet, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using TcpDotNet.Protocol.Packets.ClientBound;
|
using TcpDotNet.Protocol.Packets.ClientBound;
|
||||||
|
|
||||||
namespace TcpDotNet.Protocol.PacketHandlers;
|
namespace TcpDotNet.Protocol.PacketHandlers;
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ internal sealed class DisconnectPacketHandler : PacketHandler<DisconnectPacket>
|
|||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task HandleAsync(
|
public override Task HandleAsync(
|
||||||
BaseClientNode recipient,
|
ClientNode recipient,
|
||||||
DisconnectPacket packet,
|
DisconnectPacket packet,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using TcpDotNet.Protocol.Packets.ClientBound;
|
using TcpDotNet.Protocol.Packets.ClientBound;
|
||||||
using TcpDotNet.Protocol.Packets.ServerBound;
|
using TcpDotNet.Protocol.Packets.ServerBound;
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ internal sealed class EncryptionResponsePacketHandler : PacketHandler<Encryption
|
|||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task HandleAsync(
|
public override async Task HandleAsync(
|
||||||
BaseClientNode recipient,
|
ClientNode recipient,
|
||||||
EncryptionResponsePacket packet,
|
EncryptionResponsePacket packet,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using TcpDotNet.Protocol.Packets.ClientBound;
|
using TcpDotNet.Protocol.Packets.ClientBound;
|
||||||
using TcpDotNet.Protocol.Packets.ServerBound;
|
using TcpDotNet.Protocol.Packets.ServerBound;
|
||||||
|
|
||||||
namespace TcpDotNet.Protocol.PacketHandlers;
|
namespace TcpDotNet.Protocol.PacketHandlers;
|
||||||
@ -10,7 +10,7 @@ internal sealed class HandshakeRequestPacketHandler : PacketHandler<HandshakeReq
|
|||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task HandleAsync(
|
public override async Task HandleAsync(
|
||||||
BaseClientNode recipient,
|
ClientNode recipient,
|
||||||
HandshakeRequestPacket packet,
|
HandshakeRequestPacket packet,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
@ -22,13 +22,14 @@ internal sealed class HandshakeRequestPacketHandler : PacketHandler<HandshakeReq
|
|||||||
|
|
||||||
if (packet.ProtocolVersion != Node.ProtocolVersion)
|
if (packet.ProtocolVersion != Node.ProtocolVersion)
|
||||||
{
|
{
|
||||||
response = new HandshakeResponsePacket(packet.ProtocolVersion, HandshakeResponse.UnsupportedProtocolVersion);
|
const HandshakeResponse responseCode = HandshakeResponse.UnsupportedProtocolVersion;
|
||||||
|
response = new HandshakeResponsePacket(packet.CallbackId, packet.ProtocolVersion, responseCode);
|
||||||
await client.SendPacketAsync(response, cancellationToken);
|
await client.SendPacketAsync(response, cancellationToken);
|
||||||
client.Close();
|
client.Close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
response = new HandshakeResponsePacket(packet.ProtocolVersion, HandshakeResponse.Success);
|
response = new HandshakeResponsePacket(packet.CallbackId, packet.ProtocolVersion, HandshakeResponse.Success);
|
||||||
await client.SendPacketAsync(response, cancellationToken);
|
await client.SendPacketAsync(response, cancellationToken);
|
||||||
|
|
||||||
client.State = ClientState.Encrypting;
|
client.State = ClientState.Encrypting;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace TcpDotNet.Protocol.Packets.ClientBound;
|
namespace TcpDotNet.Protocol.Packets.ClientBound;
|
||||||
|
|
||||||
[Packet(0x7FFFFFFF)]
|
[Packet(0x7FFFFFFF)]
|
||||||
internal sealed class DisconnectPacket : Packet
|
internal sealed class DisconnectPacket : Packet
|
||||||
@ -24,12 +24,12 @@ internal sealed class DisconnectPacket : Packet
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected internal override void Deserialize(ProtocolReader reader)
|
protected internal override void Deserialize(ProtocolReader reader)
|
||||||
{
|
{
|
||||||
Reason = (DisconnectReason) reader.ReadByte();
|
Reason = (DisconnectReason)reader.ReadByte();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected internal override void Serialize(ProtocolWriter writer)
|
protected internal override void Serialize(ProtocolWriter writer)
|
||||||
{
|
{
|
||||||
writer.Write((byte) Reason);
|
writer.Write((byte)Reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace TcpDotNet.Protocol.Packets.ClientBound;
|
namespace TcpDotNet.Protocol.Packets.ClientBound;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using TcpDotNet.Protocol.Packets.ServerBound;
|
using TcpDotNet.Protocol.Packets.ServerBound;
|
||||||
|
|
||||||
namespace TcpDotNet.Protocol.Packets.ClientBound;
|
namespace TcpDotNet.Protocol.Packets.ClientBound;
|
||||||
|
|
||||||
@ -6,14 +6,16 @@ namespace TcpDotNet.Protocol.Packets.ClientBound;
|
|||||||
/// Represents a packet which responds to a <see cref="HandshakeRequestPacket" />.
|
/// Represents a packet which responds to a <see cref="HandshakeRequestPacket" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(0x7FFFFFE1)]
|
[Packet(0x7FFFFFE1)]
|
||||||
internal sealed class HandshakeResponsePacket : Packet
|
internal sealed class HandshakeResponsePacket : ResponsePacket
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="HandshakeResponsePacket" /> class.
|
/// Initializes a new instance of the <see cref="HandshakeResponsePacket" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="callbackId">The callback ID.</param>
|
||||||
/// <param name="protocolVersion">The requested protocol version.</param>
|
/// <param name="protocolVersion">The requested protocol version.</param>
|
||||||
/// <param name="handshakeResponse">The handshake response.</param>
|
/// <param name="handshakeResponse">The handshake response.</param>
|
||||||
public HandshakeResponsePacket(int protocolVersion, HandshakeResponse handshakeResponse)
|
public HandshakeResponsePacket(long callbackId, int protocolVersion, HandshakeResponse handshakeResponse)
|
||||||
|
: base(callbackId)
|
||||||
{
|
{
|
||||||
ProtocolVersion = protocolVersion;
|
ProtocolVersion = protocolVersion;
|
||||||
HandshakeResponse = handshakeResponse;
|
HandshakeResponse = handshakeResponse;
|
||||||
@ -38,14 +40,14 @@ internal sealed class HandshakeResponsePacket : Packet
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected internal override void Deserialize(ProtocolReader reader)
|
protected internal override void Deserialize(ProtocolReader reader)
|
||||||
{
|
{
|
||||||
HandshakeResponse = (HandshakeResponse) reader.ReadByte();
|
HandshakeResponse = (HandshakeResponse)reader.ReadByte();
|
||||||
ProtocolVersion = reader.ReadInt32();
|
ProtocolVersion = reader.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected internal override void Serialize(ProtocolWriter writer)
|
protected internal override void Serialize(ProtocolWriter writer)
|
||||||
{
|
{
|
||||||
writer.Write((byte) HandshakeResponse);
|
writer.Write((byte)HandshakeResponse);
|
||||||
writer.Write(ProtocolVersion);
|
writer.Write(ProtocolVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace TcpDotNet.Protocol.Packets.ClientBound;
|
namespace TcpDotNet.Protocol.Packets.ClientBound;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a packet which performs a heartbeat response.
|
/// Represents a packet which performs a heartbeat response.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace TcpDotNet.Protocol.Packets.ClientBound;
|
namespace TcpDotNet.Protocol.Packets.ClientBound;
|
||||||
|
|
||||||
[Packet(0x7FFFFFE4)]
|
[Packet(0x7FFFFFE4)]
|
||||||
internal sealed class SessionExchangePacket : Packet
|
internal sealed class SessionExchangePacket : Packet
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using TcpDotNet.Protocol.Packets.ClientBound;
|
using TcpDotNet.Protocol.Packets.ClientBound;
|
||||||
|
|
||||||
namespace TcpDotNet.Protocol.Packets.ServerBound;
|
namespace TcpDotNet.Protocol.Packets.ServerBound;
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
namespace TcpDotNet.Protocol.Packets.ServerBound;
|
namespace TcpDotNet.Protocol.Packets.ServerBound;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a packet which requests a handshake with a <see cref="ProtocolListener" />.
|
/// Represents a packet which requests a handshake with a <see cref="ProtocolListener" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(0x7FFFFFE0)]
|
[Packet(0x7FFFFFE0)]
|
||||||
internal sealed class HandshakeRequestPacket : Packet
|
internal sealed class HandshakeRequestPacket : RequestPacket
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="HandshakeRequestPacket" /> class.
|
/// Initializes a new instance of the <see cref="HandshakeRequestPacket" /> class.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace TcpDotNet.Protocol.Packets.ServerBound;
|
namespace TcpDotNet.Protocol.Packets.ServerBound;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -66,7 +66,7 @@ public sealed class ProtocolReader : BinaryReader
|
|||||||
result |= (byteReadJustNow & 0x7Fu) << shift;
|
result |= (byteReadJustNow & 0x7Fu) << shift;
|
||||||
|
|
||||||
if (byteReadJustNow <= 0x7Fu)
|
if (byteReadJustNow <= 0x7Fu)
|
||||||
return (int) result; // early exit
|
return (int)result; // early exit
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the 5th byte. Since we already read 28 bits,
|
// Read the 5th byte. Since we already read 28 bits,
|
||||||
@ -77,8 +77,8 @@ public sealed class ProtocolReader : BinaryReader
|
|||||||
if (byteReadJustNow > 0b_1111u)
|
if (byteReadJustNow > 0b_1111u)
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
|
|
||||||
result |= (uint) byteReadJustNow << (maxBytesWithoutOverflow * 7);
|
result |= (uint)byteReadJustNow << (maxBytesWithoutOverflow * 7);
|
||||||
return (int) result;
|
return (int)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -107,7 +107,7 @@ public sealed class ProtocolReader : BinaryReader
|
|||||||
result |= (byteReadJustNow & 0x7Ful) << shift;
|
result |= (byteReadJustNow & 0x7Ful) << shift;
|
||||||
|
|
||||||
if (byteReadJustNow <= 0x7Fu)
|
if (byteReadJustNow <= 0x7Fu)
|
||||||
return (long) result; // early exit
|
return (long)result; // early exit
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the 10th byte. Since we already read 63 bits,
|
// Read the 10th byte. Since we already read 63 bits,
|
||||||
@ -118,8 +118,8 @@ public sealed class ProtocolReader : BinaryReader
|
|||||||
if (byteReadJustNow > 0b_1u)
|
if (byteReadJustNow > 0b_1u)
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
|
|
||||||
result |= (ulong) byteReadJustNow << (maxBytesWithoutOverflow * 7);
|
result |= (ulong)byteReadJustNow << (maxBytesWithoutOverflow * 7);
|
||||||
return (long) result;
|
return (long)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -196,21 +196,21 @@ public sealed class ProtocolReader : BinaryReader
|
|||||||
[CLSCompliant(false)]
|
[CLSCompliant(false)]
|
||||||
public override ushort ReadUInt16()
|
public override ushort ReadUInt16()
|
||||||
{
|
{
|
||||||
return (ushort) IPAddress.NetworkToHostOrder((short) base.ReadUInt16());
|
return (ushort)IPAddress.NetworkToHostOrder((short)base.ReadUInt16());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[CLSCompliant(false)]
|
[CLSCompliant(false)]
|
||||||
public override uint ReadUInt32()
|
public override uint ReadUInt32()
|
||||||
{
|
{
|
||||||
return (uint) IPAddress.NetworkToHostOrder((int) base.ReadUInt32());
|
return (uint)IPAddress.NetworkToHostOrder((int)base.ReadUInt32());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[CLSCompliant(false)]
|
[CLSCompliant(false)]
|
||||||
public override ulong ReadUInt64()
|
public override ulong ReadUInt64()
|
||||||
{
|
{
|
||||||
return (ulong) IPAddress.NetworkToHostOrder((long) base.ReadUInt64());
|
return (ulong)IPAddress.NetworkToHostOrder((long)base.ReadUInt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -38,7 +38,11 @@ public sealed class ProtocolWriter : BinaryWriter
|
|||||||
public override void Write(double value)
|
public override void Write(double value)
|
||||||
{
|
{
|
||||||
Span<byte> buffer = stackalloc byte[8];
|
Span<byte> buffer = stackalloc byte[8];
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
MemoryMarshal.TryWrite(buffer, in value);
|
||||||
|
#else
|
||||||
MemoryMarshal.TryWrite(buffer, ref value);
|
MemoryMarshal.TryWrite(buffer, ref value);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (BitConverter.IsLittleEndian) buffer.Reverse();
|
if (BitConverter.IsLittleEndian) buffer.Reverse();
|
||||||
Write(buffer);
|
Write(buffer);
|
||||||
@ -89,7 +93,11 @@ public sealed class ProtocolWriter : BinaryWriter
|
|||||||
public override void Write(float value)
|
public override void Write(float value)
|
||||||
{
|
{
|
||||||
Span<byte> buffer = stackalloc byte[4];
|
Span<byte> buffer = stackalloc byte[4];
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
MemoryMarshal.TryWrite(buffer, in value);
|
||||||
|
#else
|
||||||
MemoryMarshal.TryWrite(buffer, ref value);
|
MemoryMarshal.TryWrite(buffer, ref value);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (BitConverter.IsLittleEndian) buffer.Reverse();
|
if (BitConverter.IsLittleEndian) buffer.Reverse();
|
||||||
Write(buffer);
|
Write(buffer);
|
||||||
@ -99,21 +107,21 @@ public sealed class ProtocolWriter : BinaryWriter
|
|||||||
[CLSCompliant(false)]
|
[CLSCompliant(false)]
|
||||||
public override void Write(ushort value)
|
public override void Write(ushort value)
|
||||||
{
|
{
|
||||||
base.Write((ushort) IPAddress.HostToNetworkOrder((short) value));
|
base.Write((ushort)IPAddress.HostToNetworkOrder((short)value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[CLSCompliant(false)]
|
[CLSCompliant(false)]
|
||||||
public override void Write(uint value)
|
public override void Write(uint value)
|
||||||
{
|
{
|
||||||
base.Write((uint) IPAddress.HostToNetworkOrder((int) value));
|
base.Write((uint)IPAddress.HostToNetworkOrder((int)value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[CLSCompliant(false)]
|
[CLSCompliant(false)]
|
||||||
public override void Write(ulong value)
|
public override void Write(ulong value)
|
||||||
{
|
{
|
||||||
base.Write((ulong) IPAddress.HostToNetworkOrder((long) value));
|
base.Write((ulong)IPAddress.HostToNetworkOrder((long)value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -155,7 +163,7 @@ public sealed class ProtocolWriter : BinaryWriter
|
|||||||
/// <param name="value">The 32-bit integer to be written.</param>
|
/// <param name="value">The 32-bit integer to be written.</param>
|
||||||
public void Write7BitEncodedInt32(int value)
|
public void Write7BitEncodedInt32(int value)
|
||||||
{
|
{
|
||||||
var uValue = (uint) value;
|
var uValue = (uint)value;
|
||||||
|
|
||||||
// Write out an int 7 bits at a time. The high bit of the byte,
|
// Write out an int 7 bits at a time. The high bit of the byte,
|
||||||
// when on, tells reader to continue reading more bytes.
|
// when on, tells reader to continue reading more bytes.
|
||||||
@ -165,11 +173,11 @@ public sealed class ProtocolWriter : BinaryWriter
|
|||||||
|
|
||||||
while (uValue > 0x7Fu)
|
while (uValue > 0x7Fu)
|
||||||
{
|
{
|
||||||
Write((byte) (uValue | ~0x7Fu));
|
Write((byte)(uValue | ~0x7Fu));
|
||||||
uValue >>= 7;
|
uValue >>= 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
Write((byte) uValue);
|
Write((byte)uValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -178,7 +186,7 @@ public sealed class ProtocolWriter : BinaryWriter
|
|||||||
/// <param name="value">The 64-bit integer to be written.</param>
|
/// <param name="value">The 64-bit integer to be written.</param>
|
||||||
public void Write7BitEncodedInt64(long value)
|
public void Write7BitEncodedInt64(long value)
|
||||||
{
|
{
|
||||||
var uValue = (ulong) value;
|
var uValue = (ulong)value;
|
||||||
|
|
||||||
// Write out an int 7 bits at a time. The high bit of the byte,
|
// Write out an int 7 bits at a time. The high bit of the byte,
|
||||||
// when on, tells reader to continue reading more bytes.
|
// when on, tells reader to continue reading more bytes.
|
||||||
@ -188,10 +196,10 @@ public sealed class ProtocolWriter : BinaryWriter
|
|||||||
|
|
||||||
while (uValue > 0x7Fu)
|
while (uValue > 0x7Fu)
|
||||||
{
|
{
|
||||||
Write((byte) ((uint) uValue | ~0x7Fu));
|
Write((byte)((uint)uValue | ~0x7Fu));
|
||||||
uValue >>= 7;
|
uValue >>= 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
Write((byte) uValue);
|
Write((byte)uValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,28 @@
|
|||||||
namespace TcpDotNet.Protocol;
|
using System.Buffers.Binary;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
namespace TcpDotNet.Protocol;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a request packet, which forms a request/response packet pair.
|
/// Represents a request packet, which forms a request/response packet pair.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class RequestPacket : Packet
|
public abstract class RequestPacket : Packet
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RequestPacket" /> class.
|
||||||
|
/// </summary>
|
||||||
|
protected RequestPacket()
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[8];
|
||||||
|
RandomNumberGenerator.Fill(buffer);
|
||||||
|
CallbackId = BinaryPrimitives.ReadInt64BigEndian(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the request identifier.
|
/// Gets the request identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The request identifier.</value>
|
/// <value>The request identifier.</value>
|
||||||
public long CallbackId { get; internal set; }
|
public long CallbackId { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected internal override void Deserialize(ProtocolReader reader)
|
protected internal override void Deserialize(ProtocolReader reader)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace TcpDotNet.Protocol;
|
namespace TcpDotNet.Protocol;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a response packet, which forms a request/response packet pair.
|
/// Represents a response packet, which forms a request/response packet pair.
|
||||||
@ -14,6 +14,10 @@ public abstract class ResponsePacket : Packet
|
|||||||
CallbackId = callbackId;
|
CallbackId = callbackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal ResponsePacket()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the response identifier.
|
/// Gets the response identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using TcpDotNet.EventData;
|
using TcpDotNet.EventData;
|
||||||
@ -14,7 +14,7 @@ namespace TcpDotNet;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a client on the TcpDotNet protocol.
|
/// Represents a client on the TcpDotNet protocol.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ProtocolClient : BaseClientNode
|
public sealed class ProtocolClient : ClientNode
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ProtocolClient" /> class.
|
/// Initializes a new instance of the <see cref="ProtocolClient" /> class.
|
||||||
@ -82,7 +82,7 @@ public sealed class ProtocolClient : BaseClientNode
|
|||||||
BaseSocket = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
BaseSocket = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Run(() => BaseSocket.ConnectAsync(remoteEP), cancellationToken);
|
await BaseSocket.ConnectAsync(remoteEP, cancellationToken);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -95,15 +95,15 @@ public sealed class ProtocolClient : BaseClientNode
|
|||||||
|
|
||||||
State = ClientState.Handshaking;
|
State = ClientState.Handshaking;
|
||||||
var handshakeRequest = new HandshakeRequestPacket(ProtocolVersion);
|
var handshakeRequest = new HandshakeRequestPacket(ProtocolVersion);
|
||||||
var handshakeResponse =
|
var handshakeResponse = await SendAndReceiveAsync<HandshakeResponsePacket>(handshakeRequest, cancellationToken);
|
||||||
await SendAndReceiveAsync<HandshakeRequestPacket, HandshakeResponsePacket>(handshakeRequest, cancellationToken);
|
|
||||||
|
|
||||||
if (handshakeResponse.HandshakeResponse != HandshakeResponse.Success)
|
if (handshakeResponse.HandshakeResponse != HandshakeResponse.Success)
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
IsConnected = false;
|
IsConnected = false;
|
||||||
throw new InvalidOperationException("Handshake failed. " +
|
|
||||||
$"Server responded with {handshakeResponse.HandshakeResponse:D}");
|
var message = $"Handshake failed. Server responded with {handshakeResponse.HandshakeResponse:D}";
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
State = ClientState.Encrypting;
|
State = ClientState.Encrypting;
|
||||||
@ -113,8 +113,7 @@ public sealed class ProtocolClient : BaseClientNode
|
|||||||
byte[] encryptedPayload = rsa.Encrypt(encryptionRequest.Payload, true);
|
byte[] encryptedPayload = rsa.Encrypt(encryptionRequest.Payload, true);
|
||||||
|
|
||||||
var key = new byte[128];
|
var key = new byte[128];
|
||||||
using var rng = new RNGCryptoServiceProvider();
|
RandomNumberGenerator.Fill(key);
|
||||||
rng.GetBytes(key);
|
|
||||||
|
|
||||||
Aes = CryptographyUtils.GenerateAes(key);
|
Aes = CryptographyUtils.GenerateAes(key);
|
||||||
byte[] aesKey = rsa.Encrypt(key, true);
|
byte[] aesKey = rsa.Encrypt(key, true);
|
||||||
|
@ -8,7 +8,7 @@ public sealed partial class ProtocolListener
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a client that is connected to a <see cref="ProtocolListener" />.
|
/// Represents a client that is connected to a <see cref="ProtocolListener" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class Client : BaseClientNode
|
public sealed class Client : ClientNode
|
||||||
{
|
{
|
||||||
internal Client(ProtocolListener listener, Socket socket)
|
internal Client(ProtocolListener listener, Socket socket)
|
||||||
{
|
{
|
||||||
@ -35,7 +35,8 @@ public sealed partial class ProtocolListener
|
|||||||
foreach (Type packetType in ParentListener.RegisteredPackets.Values)
|
foreach (Type packetType in ParentListener.RegisteredPackets.Values)
|
||||||
RegisterPacket(packetType);
|
RegisterPacket(packetType);
|
||||||
|
|
||||||
foreach ((Type packetType, IReadOnlyCollection<PacketHandler>? handlers) in ParentListener.RegisteredPacketHandlers)
|
foreach ((Type packetType, IReadOnlyCollection<PacketHandler>? handlers) in ParentListener
|
||||||
|
.RegisteredPacketHandlers)
|
||||||
foreach (PacketHandler handler in handlers)
|
foreach (PacketHandler handler in handlers)
|
||||||
RegisterPacketHandler(packetType, handler);
|
RegisterPacketHandler(packetType, handler);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using TcpDotNet.EventData;
|
using TcpDotNet.EventData;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.1;net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>10</LangVersion>
|
<LangVersion>10</LangVersion>
|
||||||
@ -12,6 +12,7 @@
|
|||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<Description>A TCP library for .NET with support for AES encryption.</Description>
|
<Description>A TCP library for .NET with support for AES encryption.</Description>
|
||||||
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
||||||
|
<PackageIcon>branding_Icon.png</PackageIcon>
|
||||||
<PackageTags>dotnet networking encryption tcp</PackageTags>
|
<PackageTags>dotnet networking encryption tcp</PackageTags>
|
||||||
<VersionPrefix>0.1.0</VersionPrefix>
|
<VersionPrefix>0.1.0</VersionPrefix>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
@ -40,10 +41,18 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Include="..\branding_Icon.png">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath/>
|
||||||
|
</None>
|
||||||
<None Include="..\LICENSE.md">
|
<None Include="..\LICENSE.md">
|
||||||
<Pack>True</Pack>
|
<Pack>True</Pack>
|
||||||
<PackagePath/>
|
<PackagePath/>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="..\README.md">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath/>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
BIN
branding_Banner.png
Normal file
BIN
branding_Banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
BIN
branding_Icon.png
Normal file
BIN
branding_Icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
7
global.json
Normal file
7
global.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"sdk": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"rollForward": "latestMajor",
|
||||||
|
"allowPrerelease": false
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user