TCP client and server

This commit is contained in:
Timerix22 2023-10-12 13:31:10 +06:00
parent aed71aefe0
commit 86161b5c95
9 changed files with 236 additions and 42 deletions

View File

@ -15,7 +15,7 @@
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<!--language features--> <!--language features-->
<LangVersion>10</LangVersion> <LangVersion>10</LangVersion>
<Nullable>disable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>disable</ImplicitUsings> <ImplicitUsings>disable</ImplicitUsings>
</PropertyGroup> </PropertyGroup>

View File

@ -0,0 +1,18 @@
using System.Net;
namespace DTLib.Network.FSP2;
public class Client : IDisposable
{
private TCPSocketClient _tcp;
public Client(IPEndPoint serverEndpoint)
{
_tcp = new TCPSocketClient(serverEndpoint);
}
public void Dispose()
{
_tcp.Dispose();
}
}

View File

@ -0,0 +1,13 @@
namespace DTLib.Network.FSP2;
public enum RequestType
{
Message,
DownloadFile,
UploadFile
}
public record Request(RequestType Type)
{
}

View File

@ -0,0 +1,13 @@
namespace DTLib.Network.FSP2;
public enum ResponseStatus
{
OK,
InvalidRequest,
AccessDenied
}
public record Response(ResponseStatus Status)
{
}

View File

@ -0,0 +1,26 @@
using System.Net;
namespace DTLib.Network.FSP2;
public class Server : IDisposable
{
private TCPSocketServer _tcp;
private readonly Func<Request, Response> _requestHandler;
public Server(IPEndPoint localEndpoint, Func<Request, Response> requestHandler, Action<Exception> exceptionHandler)
{
_requestHandler = requestHandler;
_tcp = new TCPSocketServer(localEndpoint, HandleFSP2Connection, exceptionHandler);
}
void HandleFSP2Connection(TCPSocketServer.ConnectionParams p)
{
}
public void Dispose()
{
_tcp.Dispose();
}
}

View File

@ -0,0 +1,19 @@
global using System.Net.Sockets;
global using System;
global using System.Threading;
global using System.Collections.Generic;
global using System.Linq;
global using System.Text;
global using DTLib.Extensions;
global using DTLib.Filesystem;
global using static DTLib.Logging.InternalLog;
using System.Diagnostics;
using System.Net.Http;
namespace DTLib.Network;
public static class Functions
{
/// gets public ip of this machine from ifconfig.me
public static string GetPublicIP() => new HttpClient().GetStringAsync("https://ifconfig.me/ip").GetAwaiter().GetResult();
}

View File

@ -1,41 +0,0 @@
global using System.Net.Sockets;
global using System;
global using System.Threading;
global using System.Collections.Generic;
global using System.Linq;
global using System.Text;
global using DTLib.Extensions;
global using DTLib.Filesystem;
global using static DTLib.Logging.InternalLog;
using System.Diagnostics;
using System.Net.Http;
namespace DTLib.Network;
//
// пара почти никогда не используемых методов
//
public static class OldNetwork
{
// получает с сайта публичный ip
public static string GetPublicIP() => new HttpClient().GetStringAsync("https://ifconfig.me/ip").GetAwaiter().GetResult();
// пингует айпи с помощью встроенной в винду проги, возвращает задержку
public static string PingIP(string address)
{
var proc = new Process();
proc.StartInfo.FileName = "cmd.exe";
proc.StartInfo.Arguments = "/c @echo off & chcp 65001 >nul & ping -n 5 " + address;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.Start();
System.IO.StreamReader outStream = proc.StandardOutput;
string rezult = outStream.ReadToEnd();
rezult = rezult.Remove(0, rezult.LastIndexOf('=') + 2);
return rezult.Remove(rezult.Length - 4);
}
}

View File

@ -0,0 +1,45 @@
using System.Net;
namespace DTLib.Network;
public class TCPSocketClient : IDisposable
{
protected Socket? _mainSocket;
public readonly IPEndPoint ServerEndpoint;
public TCPSocketClient(IPEndPoint serverEndpoint)
{
ServerEndpoint = serverEndpoint;
}
public virtual void Connect()
{
_mainSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_mainSocket.Connect(ServerEndpoint);
}
public virtual void Stop()
{
if(_mainSocket is null)
return;
_mainSocket.Shutdown(SocketShutdown.Both);
_mainSocket.Close();
_mainSocket = null;
}
public void Dispose() => Stop();
public void Send(byte[] buffer)
{
if (_mainSocket is null)
throw new NullReferenceException("TCP socket is null! You have to call Connect() befure calling Send()");
_mainSocket.Send(buffer);
}
public int Recieve(ref byte[] buffer)
{
if (_mainSocket is null)
throw new NullReferenceException("TCP socket is null! You have to call Connect() befure calling Recieve()");
_mainSocket.Receive(buffer);
}
}

View File

@ -0,0 +1,101 @@
using System.Net;
namespace DTLib.Network;
/// <summary>
/// Multi-threaded TCP
/// </summary>
public class TCPSocketServer : IDisposable
{
public readonly IPEndPoint LocalEndPoint;
public record ConnectionParams(Socket ConnectionSocket, Thread ConnectionThread);
protected Action<ConnectionParams>? _connectionHandler;
protected Action<Exception> _exceptionCallback;
protected Socket? _mainSocket;
/// <param name="localEndpoint">address and port of the server in the local network</param>
/// <param name="incomingConnectionHandler">this delegate is called on every incoming socket connection</param>
/// <param name="exceptionCallback">this delegate is called on every Exception throwed in threads created by the server</param>
public TCPSocketServer(IPEndPoint localEndpoint,
Action<ConnectionParams>? incomingConnectionHandler,
Action<Exception> exceptionCallback)
{
LocalEndPoint = localEndpoint;
_connectionHandler = incomingConnectionHandler;
_exceptionCallback = exceptionCallback;
}
private volatile int acceptLoopRunning = 0;
/// <summary>
/// Starts server on a new thread
/// </summary>
/// <exception cref="Exception">the server is already started</exception>
public virtual void Listen()
{
if (Interlocked.CompareExchange(ref acceptLoopRunning, 1, 1) == 1)
throw new Exception("Server is already started. Stop it to ");
_mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_mainSocket.Bind(LocalEndPoint);
_mainSocket.Listen(64);
Thread loopThread = new Thread(AcceptLoop);
loopThread.Start();
}
/// server runs this loop in special thread
private void AcceptLoop()
{
try
{
// atomic (thread-safe) comparison acceptLoopRunning==1
while (Interlocked.CompareExchange(ref acceptLoopRunning, 1, 1) == 1)
{
Socket incomingConnection = _mainSocket!.Accept();
Thread connectionThread = new Thread(HandleConnection);
connectionThread.Start(new ConnectionParams(incomingConnection, connectionThread));
}
}
catch (Exception e)
{
_exceptionCallback(e);
Stop();
}
}
private void HandleConnection(object? _p)
{
var p = (ConnectionParams)_p!;
try
{
_connectionHandler?.Invoke(p);
}
catch (Exception e)
{
_exceptionCallback(e);
}
finally
{
p.ConnectionSocket.Shutdown(SocketShutdown.Both);
p.ConnectionSocket.Close();
}
}
public virtual void Stop()
{
// atomic (thread-safe) comparison acceptLoopRunning==0 and assignement acceptLoopRunning=0
if (Interlocked.CompareExchange(ref acceptLoopRunning, 0, 1) == 0)
throw new Exception("Server isn't running");
if(_mainSocket is null)
return;
_mainSocket.Shutdown(SocketShutdown.Both);
_mainSocket.Close();
}
public void Dispose() => Stop();
}