Init
This commit is contained in:
71
.vscode/launch.json
vendored
Normal file
71
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "HTTP/JSON Server",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/Server/bin/Debug/net8.0/Server.dll",
|
||||
"args": [ "http", "json" ],
|
||||
"cwd": "${workspaceFolder}/Server",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "HTTP/JSON Client",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/Client/bin/Debug/net8.0/Client.dll",
|
||||
"args": [ "http", "json" ],
|
||||
"cwd": "${workspaceFolder}/Client",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "HTTP/BIN Server",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/Server/bin/Debug/net8.0/Server.dll",
|
||||
"args": [ "http", "bin" ],
|
||||
"cwd": "${workspaceFolder}/Server",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "HTTP/BIN Client",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/Client/bin/Debug/net8.0/Client.dll",
|
||||
"args": [ "http", "bin" ],
|
||||
"cwd": "${workspaceFolder}/Client",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "HTTP/JSON: Server and Client",
|
||||
"configurations": ["HTTP/JSON Server", "HTTP/JSON Client"],
|
||||
"preLaunchTask": "dotnet: build",
|
||||
"stopAll": true
|
||||
},
|
||||
{
|
||||
"name": "HTTP/BIN: Server and Client",
|
||||
"configurations": ["HTTP/BIN Server", "HTTP/BIN Client"],
|
||||
"preLaunchTask": "dotnet: build",
|
||||
"stopAll": true
|
||||
}
|
||||
]
|
||||
}
|
||||
18
Client/Client.csproj
Normal file
18
Client/Client.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Domain/Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
96
Client/HttpClient.cs
Normal file
96
Client/HttpClient.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Domain;
|
||||
using Domain.Dto;
|
||||
using MessagePack;
|
||||
|
||||
namespace Client;
|
||||
|
||||
public class HttpClientWrapper : IClient
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly string _baseUrl;
|
||||
private CancellationTokenSource _cts = new CancellationTokenSource();
|
||||
private Task? _runningTask;
|
||||
private Func<HttpResponseMessage, Task<Data?>>? _responseConverter;
|
||||
|
||||
public HttpClientWrapper(Func<HttpResponseMessage, Task<Data?>> responseConverter, string baseUrl = "http://localhost:5555/")
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
_baseUrl = baseUrl;
|
||||
_responseConverter = responseConverter;
|
||||
}
|
||||
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_cts = new CancellationTokenSource();
|
||||
_runningTask = Task.Run(() => RunAsync(_cts.Token));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_cts.Cancel();
|
||||
_runningTask?.Wait();
|
||||
_httpClient.Dispose();
|
||||
}
|
||||
|
||||
private async Task RunAsync(CancellationToken token)
|
||||
{
|
||||
long index = 0;
|
||||
var sw = Stopwatch.StartNew();
|
||||
var lastMs = 0L;
|
||||
var lastIndex = 0L;
|
||||
var ms = 1000;
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{_baseUrl}fetchpackage?index={index}";
|
||||
var response = await _httpClient.GetAsync(url, token);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
if (_responseConverter != null)
|
||||
{
|
||||
var data = await _responseConverter(response);
|
||||
if (data != null)
|
||||
{
|
||||
var diff = sw.ElapsedMilliseconds - lastMs;
|
||||
if (diff >= ms)
|
||||
{
|
||||
var fetched = index - lastIndex;
|
||||
System.Console.WriteLine($"Fetched {fetched} data packages in {diff} ms.");
|
||||
lastIndex = index;
|
||||
lastMs = sw.ElapsedMilliseconds;
|
||||
}
|
||||
//System.Console.WriteLine(data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Response converter not set.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Failed to fetch data for index {index}: {response.StatusCode}");
|
||||
}
|
||||
index++;
|
||||
//await Task.Delay(100, token); // Wait 1 second between requests
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
await Task.Delay(1000, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Client/IClient.cs
Normal file
7
Client/IClient.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Client;
|
||||
|
||||
interface IClient
|
||||
{
|
||||
public void Start();
|
||||
public void Stop();
|
||||
}
|
||||
53
Client/Program.cs
Normal file
53
Client/Program.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Client;
|
||||
using Domain;
|
||||
using Domain.Dto;
|
||||
using MessagePack;
|
||||
|
||||
if (args.Length < 2)
|
||||
{
|
||||
System.Console.WriteLine("Pass two args: http/tcp and json/bin");
|
||||
return -1;
|
||||
}
|
||||
|
||||
var protocol = args[0];
|
||||
var serialization = args[1];
|
||||
IClient? client = protocol == "http" ? new HttpClientWrapper(serialization == "json" ? ConvertJsonResponse : ConvertMessagePackResponse) : null;
|
||||
|
||||
client?.Start();
|
||||
System.Console.WriteLine("Client started:");
|
||||
System.Console.WriteLine(client);
|
||||
|
||||
// Обработка выхода по Ctrl+C
|
||||
Console.CancelKeyPress += (sender, e) =>
|
||||
{
|
||||
e.Cancel = true; // Prevent immediate termination
|
||||
Console.WriteLine("Shutdown signal received. Stopping client...");
|
||||
client?.Stop();
|
||||
Console.WriteLine("Goodbye!");
|
||||
Environment.Exit(0);
|
||||
};
|
||||
|
||||
// Бесконечный цикл ожидания
|
||||
while (true)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
static async Task<Data?> ConvertJsonResponse(HttpResponseMessage response)
|
||||
{
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
var jsonData = JsonSerializer.Deserialize<JsonData>(json);
|
||||
return jsonData?.ToData();
|
||||
}
|
||||
|
||||
static async Task<Data?> ConvertMessagePackResponse(HttpResponseMessage response)
|
||||
{
|
||||
var bytes = await response.Content.ReadAsByteArrayAsync();
|
||||
var msgPackData = MessagePackSerializer.Deserialize<MessagePackData>(bytes);
|
||||
return msgPackData?.ToData();
|
||||
}
|
||||
17
Domain/Data.cs
Normal file
17
Domain/Data.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Domain;
|
||||
|
||||
public record class Data(
|
||||
double ConcentrationIndex,
|
||||
double RelaxationIndex,
|
||||
double CognitiveControl,
|
||||
double CognitiveLoad,
|
||||
double Alpha,
|
||||
double Beta,
|
||||
double Theta,
|
||||
double Smr,
|
||||
double MuWave,
|
||||
bool Artifact,
|
||||
double SignalQuality,
|
||||
long PackageIndex,
|
||||
DateTime TimeOfDataGenerate
|
||||
);
|
||||
14
Domain/Domain.csproj
Normal file
14
Domain/Domain.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
71
Domain/Dto/JsonData.cs
Normal file
71
Domain/Dto/JsonData.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
namespace Domain.Dto;
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
public class JsonData
|
||||
{
|
||||
[JsonPropertyName("concentrationIndex")]
|
||||
public double ConcentrationIndex { get; set; }
|
||||
[JsonPropertyName("relaxationIndex")]
|
||||
public double RelaxationIndex { get; set; }
|
||||
[JsonPropertyName("cognitiveControl")]
|
||||
public double CognitiveControl { get; set; }
|
||||
[JsonPropertyName("cognitiveLoad")]
|
||||
public double CognitiveLoad { get; set; }
|
||||
[JsonPropertyName("alpha")]
|
||||
public double Alpha { get; set; }
|
||||
[JsonPropertyName("beta")]
|
||||
public double Beta { get; set; }
|
||||
[JsonPropertyName("theta")]
|
||||
public double Theta { get; set; }
|
||||
[JsonPropertyName("smr")]
|
||||
public double Smr { get; set; }
|
||||
[JsonPropertyName("muWave")]
|
||||
public double MuWave { get; set; }
|
||||
[JsonPropertyName("artifact")]
|
||||
public bool Artifact { get; set; }
|
||||
[JsonPropertyName("signalQuality")]
|
||||
public double SignalQuality { get; set; }
|
||||
[JsonPropertyName("packageIndex")]
|
||||
public long PackageIndex { get; set; }
|
||||
[JsonPropertyName("timeOfDataGenerate")]
|
||||
public DateTime TimeOfDataGenerate { get; set; }
|
||||
|
||||
public JsonData() { }
|
||||
|
||||
public JsonData(Data data)
|
||||
{
|
||||
ConcentrationIndex = data.ConcentrationIndex;
|
||||
RelaxationIndex = data.RelaxationIndex;
|
||||
CognitiveControl = data.CognitiveControl;
|
||||
CognitiveLoad = data.CognitiveLoad;
|
||||
Alpha = data.Alpha;
|
||||
Beta = data.Beta;
|
||||
Theta = data.Theta;
|
||||
Smr = data.Smr;
|
||||
MuWave = data.MuWave;
|
||||
Artifact = data.Artifact;
|
||||
SignalQuality = data.SignalQuality;
|
||||
PackageIndex = data.PackageIndex;
|
||||
TimeOfDataGenerate = data.TimeOfDataGenerate;
|
||||
}
|
||||
|
||||
public Data ToData()
|
||||
{
|
||||
return new Data(
|
||||
ConcentrationIndex,
|
||||
RelaxationIndex,
|
||||
CognitiveControl,
|
||||
CognitiveLoad,
|
||||
Alpha,
|
||||
Beta,
|
||||
Theta,
|
||||
Smr,
|
||||
MuWave,
|
||||
Artifact,
|
||||
SignalQuality,
|
||||
PackageIndex,
|
||||
TimeOfDataGenerate
|
||||
);
|
||||
}
|
||||
}
|
||||
72
Domain/Dto/MessagePackData.cs
Normal file
72
Domain/Dto/MessagePackData.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
namespace Domain.Dto;
|
||||
|
||||
using MessagePack;
|
||||
|
||||
[MessagePackObject]
|
||||
public class MessagePackData
|
||||
{
|
||||
[Key("concentrationIndex")]
|
||||
public double ConcentrationIndex { get; set; }
|
||||
[Key("relaxationIndex")]
|
||||
public double RelaxationIndex { get; set; }
|
||||
[Key("cognitiveControl")]
|
||||
public double CognitiveControl { get; set; }
|
||||
[Key("cognitiveLoad")]
|
||||
public double CognitiveLoad { get; set; }
|
||||
[Key("alpha")]
|
||||
public double Alpha { get; set; }
|
||||
[Key("beta")]
|
||||
public double Beta { get; set; }
|
||||
[Key("theta")]
|
||||
public double Theta { get; set; }
|
||||
[Key("smr")]
|
||||
public double Smr { get; set; }
|
||||
[Key("muWave")]
|
||||
public double MuWave { get; set; }
|
||||
[Key("artifact")]
|
||||
public bool Artifact { get; set; }
|
||||
[Key("signalQuality")]
|
||||
public double SignalQuality { get; set; }
|
||||
[Key("packageIndex")]
|
||||
public long PackageIndex { get; set; }
|
||||
[Key("timeOfDataGenerate")]
|
||||
public DateTime TimeOfDataGenerate { get; set; }
|
||||
|
||||
public MessagePackData() { }
|
||||
|
||||
public MessagePackData(Data data)
|
||||
{
|
||||
ConcentrationIndex = data.ConcentrationIndex;
|
||||
RelaxationIndex = data.RelaxationIndex;
|
||||
CognitiveControl = data.CognitiveControl;
|
||||
CognitiveLoad = data.CognitiveLoad;
|
||||
Alpha = data.Alpha;
|
||||
Beta = data.Beta;
|
||||
Theta = data.Theta;
|
||||
Smr = data.Smr;
|
||||
MuWave = data.MuWave;
|
||||
Artifact = data.Artifact;
|
||||
SignalQuality = data.SignalQuality;
|
||||
PackageIndex = data.PackageIndex;
|
||||
TimeOfDataGenerate = data.TimeOfDataGenerate;
|
||||
}
|
||||
|
||||
public Data ToData()
|
||||
{
|
||||
return new Data(
|
||||
ConcentrationIndex,
|
||||
RelaxationIndex,
|
||||
CognitiveControl,
|
||||
CognitiveLoad,
|
||||
Alpha,
|
||||
Beta,
|
||||
Theta,
|
||||
Smr,
|
||||
MuWave,
|
||||
Artifact,
|
||||
SignalQuality,
|
||||
PackageIndex,
|
||||
TimeOfDataGenerate
|
||||
);
|
||||
}
|
||||
}
|
||||
34
NetworkTest.sln
Normal file
34
NetworkTest.sln
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{A33376EF-FB88-4A2F-A1FD-0F9B0F89B976}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{B4ABD6BA-1C0A-49A4-8580-E5A5B9A4DD4D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "Domain\Domain.csproj", "{7EFE01A5-B489-4460-988D-E34D6C67711D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A33376EF-FB88-4A2F-A1FD-0F9B0F89B976}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A33376EF-FB88-4A2F-A1FD-0F9B0F89B976}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A33376EF-FB88-4A2F-A1FD-0F9B0F89B976}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A33376EF-FB88-4A2F-A1FD-0F9B0F89B976}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B4ABD6BA-1C0A-49A4-8580-E5A5B9A4DD4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B4ABD6BA-1C0A-49A4-8580-E5A5B9A4DD4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B4ABD6BA-1C0A-49A4-8580-E5A5B9A4DD4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B4ABD6BA-1C0A-49A4-8580-E5A5B9A4DD4D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7EFE01A5-B489-4460-988D-E34D6C67711D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7EFE01A5-B489-4460-988D-E34D6C67711D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7EFE01A5-B489-4460-988D-E34D6C67711D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7EFE01A5-B489-4460-988D-E34D6C67711D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
88
Server/DataGenerator.cs
Normal file
88
Server/DataGenerator.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Domain;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class DataGenerator
|
||||
{
|
||||
private static readonly Random _random = new Random();
|
||||
|
||||
private readonly int _minNewPackages;
|
||||
private readonly int _maxPreviousPackages;
|
||||
private readonly TimeSpan _generationInterval;
|
||||
private readonly LinkedList<Data> _cache = new LinkedList<Data>();
|
||||
private readonly ConcurrentDictionary<long, Data> _dict = [];
|
||||
private readonly object _lock = new object();
|
||||
private long _maxRequestedIndex = 0;
|
||||
private Task _generationTask;
|
||||
private CancellationTokenSource _cts = new();
|
||||
|
||||
public DataGenerator(int minNewPackages = 50, int maxPreviousPackages = 50, TimeSpan? generationInterval = null)
|
||||
{
|
||||
_minNewPackages = Math.Max(minNewPackages, 5);
|
||||
_maxPreviousPackages = Math.Max(maxPreviousPackages, 5);
|
||||
_generationInterval = generationInterval ?? TimeSpan.FromSeconds(1);
|
||||
_generationTask = Task.Run(() => GenerateInBackground(_cts.Token));
|
||||
}
|
||||
|
||||
private void GenerateInBackground(CancellationToken token)
|
||||
{
|
||||
var firstData = GenerateRandomData(0);
|
||||
_cache.AddLast(firstData);
|
||||
_dict[firstData.PackageIndex] = firstData;
|
||||
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
//await Task.Delay(_generationInterval, token);
|
||||
var first = _cache.First!.Value;
|
||||
var last = _cache.Last!.Value;
|
||||
if (last.PackageIndex - _maxRequestedIndex < _minNewPackages)
|
||||
{
|
||||
var data = GenerateRandomData(last.PackageIndex + 1);
|
||||
_cache.AddLast(data);
|
||||
_dict[data.PackageIndex] = data;
|
||||
}
|
||||
if (_maxRequestedIndex - first.PackageIndex > _maxPreviousPackages)
|
||||
{
|
||||
_cache.RemoveFirst();
|
||||
_dict.TryRemove(first.PackageIndex, out _);
|
||||
}
|
||||
//System.Console.WriteLine($"[{first.PackageIndex}; {last.PackageIndex}]");
|
||||
}
|
||||
}
|
||||
|
||||
public Data? GetPackage(long packageIndex)
|
||||
{
|
||||
var res = _dict.TryGetValue(packageIndex, out var value);
|
||||
_maxRequestedIndex = Math.Max(_maxRequestedIndex, packageIndex);
|
||||
return res ? value : null;
|
||||
}
|
||||
|
||||
private Data GenerateRandomData(long packageNumber)
|
||||
{
|
||||
var alpha = _random.NextDouble();
|
||||
var beta = _random.NextDouble() * (1 - alpha);
|
||||
var theta = 1 - alpha - beta;
|
||||
var signalQuality = _random.NextDouble();
|
||||
|
||||
return new Data(
|
||||
ConcentrationIndex: _random.NextDouble(),
|
||||
RelaxationIndex: _random.NextDouble(),
|
||||
CognitiveControl: _random.NextDouble(),
|
||||
CognitiveLoad: _random.NextDouble(),
|
||||
Alpha: alpha,
|
||||
Beta: beta,
|
||||
Theta: theta,
|
||||
Smr: _random.NextDouble(),
|
||||
MuWave: _random.NextDouble(),
|
||||
Artifact: signalQuality < 0.5,
|
||||
SignalQuality: signalQuality,
|
||||
PackageIndex: packageNumber,
|
||||
TimeOfDataGenerate: DateTime.Now
|
||||
);
|
||||
}
|
||||
}
|
||||
105
Server/HttpServer.cs
Normal file
105
Server/HttpServer.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Domain;
|
||||
using Domain.Dto;
|
||||
|
||||
namespace NetworkTest;
|
||||
|
||||
public class HttpServer : IServer
|
||||
{
|
||||
private readonly HttpListener _listener;
|
||||
private readonly string _url;
|
||||
private CancellationTokenSource _cts = new CancellationTokenSource();
|
||||
private Func<long, Data?> _getData;
|
||||
private Action<Data, HttpListenerResponse> _writeResponse;
|
||||
|
||||
public HttpServer(Func<long, Data?> getData, Action<Data, HttpListenerResponse> writeResponse, string url = "http://*:5555/")
|
||||
{
|
||||
_getData = getData;
|
||||
_writeResponse = writeResponse;
|
||||
_url = url;
|
||||
_listener = new HttpListener();
|
||||
_listener.Prefixes.Add(_url);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_cts = new CancellationTokenSource();
|
||||
_listener.Start();
|
||||
Task.Run(() => ListenAsync(_cts.Token));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_cts.Cancel();
|
||||
_listener.Stop();
|
||||
}
|
||||
|
||||
private async Task ListenAsync(CancellationToken token)
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var context = await _listener.GetContextAsync();
|
||||
_ = Task.Run(() => HandleRequest(context));
|
||||
}
|
||||
catch (HttpListenerException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleRequest(HttpListenerContext context)
|
||||
{
|
||||
if (context.Request.Url?.AbsolutePath.Equals("/fetchpackage", StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
string? indexStr = context.Request.QueryString["index"];
|
||||
if (indexStr != null && long.TryParse(indexStr, out long index))
|
||||
{
|
||||
var data = _getData(index);
|
||||
if (data != null)
|
||||
{
|
||||
_writeResponse(data, context.Response);
|
||||
}
|
||||
else
|
||||
{
|
||||
var responseText = JsonSerializer.Serialize(new { error = "Data not found" });
|
||||
context.Response.StatusCode = 404;
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(responseText);
|
||||
context.Response.ContentType = "application/json";
|
||||
context.Response.ContentLength64 = buffer.Length;
|
||||
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
byte[] buffer = Encoding.UTF8.GetBytes("Invalid or missing 'index' parameter.");
|
||||
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = 404;
|
||||
byte[] buffer = Encoding.UTF8.GetBytes("Not Found");
|
||||
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
context.Response.OutputStream.Close();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"HTTP: [{string.Join(", ", _listener.Prefixes)}]";
|
||||
}
|
||||
}
|
||||
|
||||
8
Server/IServer.cs
Normal file
8
Server/IServer.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace NetworkTest;
|
||||
|
||||
interface IServer
|
||||
{
|
||||
public void Start();
|
||||
public void Stop();
|
||||
|
||||
}
|
||||
111
Server/Program.cs
Normal file
111
Server/Program.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Domain;
|
||||
using Domain.Dto;
|
||||
using MessagePack;
|
||||
using NetworkTest;
|
||||
using Server;
|
||||
|
||||
if (args.Length < 2)
|
||||
{
|
||||
System.Console.WriteLine("Pass twp arg: test/http/tcp and json/bin");
|
||||
return -1;
|
||||
}
|
||||
|
||||
var dataGenerator = new DataGenerator(generationInterval: TimeSpan.FromMilliseconds(1));
|
||||
|
||||
var protocol = args[0];
|
||||
var serialization = args[1];
|
||||
|
||||
if (protocol == "test")
|
||||
{
|
||||
int nullCount = 0;
|
||||
long index = 0;
|
||||
var sw = Stopwatch.StartNew();
|
||||
var lastMs = 0L;
|
||||
var lastIndex = 0L;
|
||||
var ms = 1000;
|
||||
|
||||
var json = serialization == "json";
|
||||
while (true)
|
||||
{
|
||||
var data = dataGenerator.GetPackage(index);
|
||||
if (data == null)
|
||||
{
|
||||
nullCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (json)
|
||||
{
|
||||
JsonData jsonData = new JsonData(data);
|
||||
var responseText = JsonSerializer.Serialize(jsonData);
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(responseText);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessagePackData msgPackData = new MessagePackData(data);
|
||||
byte[] buffer = MessagePackSerializer.Serialize(msgPackData);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
var diff = sw.ElapsedMilliseconds - lastMs;
|
||||
if (diff >= ms)
|
||||
{
|
||||
var serializrd = index - lastIndex;
|
||||
System.Console.WriteLine($"Serialized {serializrd} data packages in {diff} ms.");
|
||||
lastIndex = index;
|
||||
lastMs = sw.ElapsedMilliseconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IServer? server = protocol == "http" ?
|
||||
new HttpServer(index => dataGenerator.GetPackage(index),
|
||||
serialization == "json" ? PrepareResponseJson : PrepareResponseMessagePack) :
|
||||
null;
|
||||
|
||||
server?.Start();
|
||||
System.Console.WriteLine("Server started:");
|
||||
System.Console.WriteLine(server);
|
||||
|
||||
// Обработка выхода по Ctrl+C
|
||||
Console.CancelKeyPress += (sender, e) =>
|
||||
{
|
||||
e.Cancel = true; // Prevent immediate termination
|
||||
Console.WriteLine("Shutdown signal received. Stopping server...");
|
||||
server?.Stop();
|
||||
Console.WriteLine("Goodbye!");
|
||||
Environment.Exit(0);
|
||||
};
|
||||
|
||||
|
||||
// Бесконечный цикл ожидания
|
||||
while (true)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
|
||||
void PrepareResponseJson(Data data, HttpListenerResponse response)
|
||||
{
|
||||
JsonData jsonData = new JsonData(data);
|
||||
var responseText = JsonSerializer.Serialize(jsonData);
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(responseText);
|
||||
response.ContentType = "application/json";
|
||||
response.ContentLength64 = buffer.Length;
|
||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
void PrepareResponseMessagePack(Data data, HttpListenerResponse response)
|
||||
{
|
||||
MessagePackData msgPackData = new MessagePackData(data);
|
||||
byte[] buffer = MessagePackSerializer.Serialize(msgPackData);
|
||||
response.ContentType = "application/x-msgpack";
|
||||
response.ContentLength64 = buffer.Length;
|
||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
18
Server/Server.csproj
Normal file
18
Server/Server.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Domain/Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user