TCP client and server
This commit is contained in:
parent
aed71aefe0
commit
86161b5c95
@ -15,7 +15,7 @@
|
||||
<DebugType>embedded</DebugType>
|
||||
<!--language features-->
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>disable</Nullable>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
18
DTLib.Network/FSP2/Client.cs
Normal file
18
DTLib.Network/FSP2/Client.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
13
DTLib.Network/FSP2/Request.cs
Normal file
13
DTLib.Network/FSP2/Request.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace DTLib.Network.FSP2;
|
||||
|
||||
public enum RequestType
|
||||
{
|
||||
Message,
|
||||
DownloadFile,
|
||||
UploadFile
|
||||
}
|
||||
|
||||
public record Request(RequestType Type)
|
||||
{
|
||||
|
||||
}
|
||||
13
DTLib.Network/FSP2/Response.cs
Normal file
13
DTLib.Network/FSP2/Response.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace DTLib.Network.FSP2;
|
||||
|
||||
public enum ResponseStatus
|
||||
{
|
||||
OK,
|
||||
InvalidRequest,
|
||||
AccessDenied
|
||||
}
|
||||
|
||||
public record Response(ResponseStatus Status)
|
||||
{
|
||||
|
||||
}
|
||||
26
DTLib.Network/FSP2/Server.cs
Normal file
26
DTLib.Network/FSP2/Server.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
19
DTLib.Network/Functions.cs
Normal file
19
DTLib.Network/Functions.cs
Normal 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();
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
45
DTLib.Network/TCPSocketClient.cs
Normal file
45
DTLib.Network/TCPSocketClient.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
101
DTLib.Network/TCPSocketServer.cs
Normal file
101
DTLib.Network/TCPSocketServer.cs
Normal 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();
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user