Skip to content

Commit

Permalink
add a socket testing tool (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
yoli799480165 authored Jul 3, 2024
1 parent 4d92633 commit 1fafe1b
Show file tree
Hide file tree
Showing 8 changed files with 498 additions and 0 deletions.
33 changes: 33 additions & 0 deletions tools/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Tools

## SocketTester

A socket testing tool.

### Usage

```bash
cd SocketTester

# send tcp packages
dotnet run -- -t tcp -sp 9999 -cp 9999 -tp 10 -c

# send udp packages
dotnet run -- -t udp -sp 9999 -cp 9999 -tp 10 -c
```

Startup parameters

```bash
dotnet run -- -h
```

| Name | Default Value | Desc |
| ----------------- | ------------- | ---------------------------------------------- |
| --type,-t | tcp | Socket type, value is tcp,udp etc. |
| --server-port,-sp | 9999 | Server listen port |
| --client-port,-cp | 8888 | Client connection port |
| --compressed,-c | false | Enable stream compression |
| --frequency,-f | 1000 | Frequency of sending(ms) |
| --total-packs,-tp | 0 | Maximum number of packets sent. 0 is unlimited |
| --pack-size,-ps | 1024 | Package size |
35 changes: 35 additions & 0 deletions tools/SocketTester/ISocketTester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.CommandLine;
using System.CommandLine.Binding;

namespace SocketTester;

internal interface ISocketTester
{
bool Compressed { get; set; }
int Frequency { get; set; }
int TotalPacks { get; set; }
int PackSize { get; set; }
Task RunServerAsync(int port = 9999);
Task RunClientAsync(int port = 8888, string ip = "127.0.0.1");
}

internal class SocketTesterBinder : BinderBase<ISocketTester>
{
private readonly Option<string> _typeOption;

public SocketTesterBinder(Option<string> typeOption)
{
_typeOption = typeOption;
}

protected override ISocketTester GetBoundValue(BindingContext bindingContext)
{
var value = bindingContext.ParseResult.GetValueForOption(_typeOption);
return value switch
{
"tcp" => new TcpTester(),
"udp" => new UdpTester(),
_ => new TcpTester()
};
}
}
81 changes: 81 additions & 0 deletions tools/SocketTester/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// See https://aka.ms/new-console-template for more information

using SocketTester;
using System.CommandLine;

var returnCode = 0;
var rootCommand = new RootCommand("RhoAias Socket Tester.");

var typeOption = new Option<string>(
name: "--type",
description: "Socket type, value is tcp,udp etc.",
getDefaultValue: () => "tcp");
typeOption.AddAlias("-t");

var serverPortOption = new Option<int>(
name:"--server-port",
description: "Server listen port.",
getDefaultValue: () => 9999);
serverPortOption.AddAlias("-sp");

var clientPortOption = new Option<int>(
name: "--client-port",
description: "Client connection port.",
getDefaultValue: () => 8888);
clientPortOption.AddAlias("-cp");

var compressedOption = new Option<bool>(
name: "--compressed",
description: "Enable stream compression.",
getDefaultValue: () => false);
compressedOption.AddAlias("-c");

var frequencyOption = new Option<int>(
name: "--frequency",
description: "Frequency of sending(ms).",
getDefaultValue: () => 1000);
frequencyOption.AddAlias("-f");

var totalPacksOption = new Option<int>(
name: "--total-packs",
description: "Maximum number of packets sent. 0 is unlimited.",
getDefaultValue: () => 0);
totalPacksOption.AddAlias("-tp");

var packSizeOption = new Option<int>(
name: "--pack-size",
description: "Package size.",
getDefaultValue: () => 1024);
packSizeOption.AddAlias("-ps");

rootCommand.Add(typeOption);
rootCommand.Add(serverPortOption);
rootCommand.Add(clientPortOption);
rootCommand.Add(compressedOption);
rootCommand.Add(frequencyOption);
rootCommand.Add(totalPacksOption);
rootCommand.Add(packSizeOption);

rootCommand.SetHandler(async (tester, serverPort, clientPort, compressed, frequency, totalPacks, packSize) =>
{
tester.Compressed = compressed;
tester.Frequency = frequency;
tester.TotalPacks = totalPacks;
tester.PackSize = packSize;

var task1 = tester.RunServerAsync(serverPort);
await Task.Delay(2000);
var task2 = tester.RunClientAsync(clientPort);
await Task.WhenAll(task1, task2);
},
new SocketTesterBinder(typeOption),
serverPortOption,
clientPortOption,
compressedOption,
frequencyOption,
totalPacksOption,
packSizeOption);

await rootCommand.InvokeAsync(args);

return returnCode;
16 changes: 16 additions & 0 deletions tools/SocketTester/SocketTester.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>1.0.0</Version>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Snappier" Version="1.1.6" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
</ItemGroup>
</Project>
93 changes: 93 additions & 0 deletions tools/SocketTester/TcpTester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
using Snappier;

namespace SocketTester;

internal class TcpTester : ISocketTester
{
private long _packIndex = 1;
public bool Compressed { get; set; } = false;
public int Frequency { get; set; } = 2000;
public int TotalPacks { get; set; } = 0;
public int PackSize { get; set; } = 1024;

public Task RunServerAsync(int port = 9999)
{
return Task.Run(async () =>
{
var listener = new TcpListener(new IPEndPoint(IPAddress.Any, port));
listener.Start();
var client = await listener.AcceptTcpClientAsync();
await using var sendStream = GetStream(client, Compressed, CompressionMode.Compress);
await using var recvStream = GetStream(client, Compressed, CompressionMode.Decompress);
while (TotalPacks == 0 || _packIndex < TotalPacks)
{
var readBytes = await RecvAsync(recvStream);
if (readBytes > 0)
{
await Task.Delay(Frequency);
await SendAsync(sendStream);
}
}
});
}

public Task RunClientAsync(int port = 8888, string ip = "127.0.0.1")
{
return Task.Run(async () =>
{
var client = new TcpClient();
await client.ConnectAsync(IPAddress.Parse(ip), port);
await using var sendStream = GetStream(client, Compressed, CompressionMode.Compress);
await using var recvStream = GetStream(client, Compressed, CompressionMode.Decompress);
await SendAsync(sendStream);
while (TotalPacks == 0 || _packIndex < TotalPacks)
{
var readBytes = await RecvAsync(recvStream);
if (readBytes > 0)
{
await Task.Delay(Frequency);
await SendAsync(sendStream);
}
}
});
}

private Stream GetStream(TcpClient client, bool compressed, CompressionMode mode)
{
var stream = client.GetStream();
if (compressed)
{
return new SnappyStream(stream, mode);
}

return stream;
}

private async Task SendAsync(Stream stream)
{
if (TotalPacks != 0 && _packIndex > TotalPacks)
{
return;
}
var data = Util.GenerateRandomBytes(PackSize);
Console.WriteLine($"Send -> PackIndex: {_packIndex} PackSize: {PackSize} Length:{data.Length} CheckSum: {Util.CheckSum(data)}");
await stream.WriteAsync(data);
await stream.FlushAsync();
}

private async Task<int> RecvAsync(Stream stream)
{
var buffer = new byte[2048];
var readBytes = await stream.ReadAsync(buffer, 0, buffer.Length);

if (readBytes > 0)
{
Console.WriteLine($"Recv -> PackIndex: {_packIndex} PackSize: {PackSize} Length:{readBytes} CheckSum: {Util.CheckSum(buffer[..readBytes])}");
_packIndex++;
}
return readBytes;
}
}
111 changes: 111 additions & 0 deletions tools/SocketTester/UdpStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using System.Net;
using System.Net.Sockets;

namespace SocketTester
{
internal class UdpStream : Stream
{
private readonly UdpClient _client;
private readonly bool _remoteWrite;
private IPEndPoint? _remoteEndPoint;

public IPEndPoint RemoteEndPoint => _remoteEndPoint;

public UdpStream(UdpClient client, bool remoteWrite)
{
_client = client;
_remoteWrite = remoteWrite;
}

public override void Flush()
{
}

public override int Read(byte[] buffer, int offset, int count)
{
var recv = _client.Receive(ref _remoteEndPoint);
recv.CopyTo(buffer, 0);
return recv.Length;
}

public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
var recv = await _client.ReceiveAsync(cancellationToken);
_remoteEndPoint = recv.RemoteEndPoint;
recv.Buffer.CopyTo(buffer, 0);
return recv.Buffer.Length;
}

public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = new CancellationToken())
{
var recv = await _client.ReceiveAsync(cancellationToken);
_remoteEndPoint = recv.RemoteEndPoint;
recv.Buffer.CopyTo(buffer);
return recv.Buffer.Length;
}

public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}

public override void SetLength(long value)
{
throw new NotSupportedException();
}

public override void Write(byte[] buffer, int offset, int count)
{
if (_remoteWrite && _remoteEndPoint != null)
{
_client.Send(buffer, count, _remoteEndPoint);
}
else
{
_client.Send(buffer, count);
}
}

public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_remoteWrite && _remoteEndPoint != null)
{
await _client.SendAsync(buffer, count, _remoteEndPoint);
}
else
{
await _client.SendAsync(buffer, count);
}
}

public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = new CancellationToken())
{
if (_remoteWrite && _remoteEndPoint != null)
{
await _client.SendAsync(buffer, _remoteEndPoint, cancellationToken);
}
else
{
await _client.SendAsync(buffer, cancellationToken);
}
}

public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
}

internal static class Ext
{
public static UdpStream GetStream(this UdpClient client, bool remoteWrite = false)
{
return new UdpStream(client, remoteWrite);
}
}
}
Loading

0 comments on commit 1fafe1b

Please sign in to comment.