This commit is contained in:
EonaCat 2023-03-24 11:06:57 +01:00
parent a5e2cbaf57
commit ff98074f5b
6 changed files with 165 additions and 214 deletions

View File

@ -18,9 +18,9 @@
<PackageTags>EonaCat, Network, .NET Standard, EonaCatHelpers, Jeroen, Saey, Protocol, Quic, UDP, TCP, Web, Server</PackageTags> <PackageTags>EonaCat, Network, .NET Standard, EonaCatHelpers, Jeroen, Saey, Protocol, Quic, UDP, TCP, Web, Server</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes> <PackageReleaseNotes></PackageReleaseNotes>
<Description>EonaCat Networking library with Quic, TCP, UDP and a Webserver</Description> <Description>EonaCat Networking library with Quic, TCP, UDP and a Webserver</Description>
<Version>1.0.2</Version> <Version>1.0.3</Version>
<AssemblyVersion>1.0.0.2</AssemblyVersion> <AssemblyVersion>1.0.0.3</AssemblyVersion>
<FileVersion>1.0.0.2</FileVersion> <FileVersion>1.0.0.3</FileVersion>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
</PropertyGroup> </PropertyGroup>
@ -50,6 +50,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="EonaCat.Controls" Version="1.0.2" />
<PackageReference Include="EonaCat.Json" Version="1.0.3" /> <PackageReference Include="EonaCat.Json" Version="1.0.3" />
<PackageReference Include="EonaCat.LogSystem" Version="1.0.0" /> <PackageReference Include="EonaCat.LogSystem" Version="1.0.0" />
<PackageReference Include="EonaCat.Matchers" Version="1.0.0" /> <PackageReference Include="EonaCat.Matchers" Version="1.0.0" />

View File

@ -6,7 +6,7 @@ namespace EonaCat.Sockets
{ {
public delegate void SendBytesTo(byte[] data, EndPoint point); public delegate void SendBytesTo(byte[] data, EndPoint point);
public class ClientConnection : IDisposable public class Client : IDisposable
{ {
/// <summary> /// <summary>
/// Unique client id. /// Unique client id.
@ -43,10 +43,7 @@ namespace EonaCat.Sockets
public event SendBytesTo SendData; public event SendBytesTo SendData;
/// <summary> public Client()
/// ClientConnection class constructor.
/// </summary>
public ClientConnection()
{ {
Buffer = new byte[BufferSize]; Buffer = new byte[BufferSize];
} }

View File

@ -5,7 +5,7 @@ using System.Net.Sockets;
namespace EonaCat.Sockets namespace EonaCat.Sockets
{ {
public class SocketClient : ClientConnection, IDisposable public class SocketClient : Client, IDisposable
{ {
/// <summary> /// <summary>
/// Client type /// Client type

View File

@ -5,7 +5,8 @@ using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading.Tasks;
using EonaCat.Helpers.Controls;
namespace EonaCat.Sockets namespace EonaCat.Sockets
{ {
@ -29,7 +30,7 @@ namespace EonaCat.Sockets
/// <summary> /// <summary>
/// List of connected clients. /// List of connected clients.
/// </summary> /// </summary>
public List<ClientConnection> ConnectedClients { get; set; } = new List<ClientConnection>(); public List<Client> ConnectedClients { get; set; } = new List<Client>();
/// <summary> /// <summary>
/// Interval to check for disconnected clients /// Interval to check for disconnected clients
@ -44,9 +45,8 @@ namespace EonaCat.Sockets
/// <summary> /// <summary>
/// Determines if the socket is in blocking mode /// Determines if the socket is in blocking mode
/// (default: true)
/// </summary> /// </summary>
public bool IsSocketInBlockingMode { get; set; } = true; public bool IsSocketBlockingDisabled { get; set; }
/// <summary> /// <summary>
@ -60,9 +60,9 @@ namespace EonaCat.Sockets
/// <summary> /// <summary>
/// Only for UDP. /// Only for UDP.
/// Required to use SocketClient class for this. /// (default: true)
/// </summary> /// </summary>
public bool UDPClientManage { get; set; } = true; public bool UseClientManager { get; set; } = true;
/// <summary> /// <summary>
/// Only for UDP. /// Only for UDP.
@ -96,18 +96,20 @@ namespace EonaCat.Sockets
/// </summary> /// </summary>
public event EventHandler OnStop; public event EventHandler OnStop;
private Socket Listener { get; set; } private Socket Server { get; set; }
private System.Timers.Timer DisconnectTimer { get; set; } private EonaCatTimer DisconnectTimerUdp { get; set; }
private EonaCatTimer DisconnectTimerTcp { get; set; }
private Dictionary<EndPoint, double> LastDataReceivedTime { get; set; } private Dictionary<EndPoint, double> LastDataReceivedTime { get; set; }
private double TimeNow { get => (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; } private double TimeNow { get => (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; }
public SocketServer(NetworkType type, string address, int port) public SocketServer(NetworkType type, string address, int port)
{ {
if (IPAddress.TryParse(address, out var ipAddress)) if (!IPAddress.TryParse(address, out var ipAddress))
{ {
new SocketServer(type, ipAddress, port); throw new Exception("Invalid IP address specified");
} }
throw new Exception("Invalid IP address specified");
SetupSocketServer(type, ipAddress, port);
} }
/// <summary> /// <summary>
@ -117,12 +119,63 @@ namespace EonaCat.Sockets
/// <param name="address">Server listening ip</param> /// <param name="address">Server listening ip</param>
/// <param name="port">Server listening port</param> /// <param name="port">Server listening port</param>
public SocketServer(NetworkType type, IPAddress address, int port) public SocketServer(NetworkType type, IPAddress address, int port)
{
SetupSocketServer(type, address, port);
}
private void SetupSocketServer(NetworkType type, IPAddress address, int port)
{ {
Guid = System.Guid.NewGuid().ToString(); Guid = System.Guid.NewGuid().ToString();
ServerType = type; ServerType = type;
IPAddress = address; IPAddress = address;
Port = port; Port = port;
DisconnectTimer = new System.Timers.Timer(DisconnectionCheckInterval); }
private readonly List<EndPoint> _removedEndpoints = new List<EndPoint>();
private void UdpCheckCallback()
{
_removedEndpoints.Clear();
var now = TimeNow;
foreach (var kp in LastDataReceivedTime.Where(kp => now - kp.Value > _UDPDataInterval / 1000))
{
lock (ConnectedClients)
{
var client = ConnectedClients.FirstOrDefault(x => x.Guid == kp.Key.ToString());
if (client == null)
{
continue;
}
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
ConnectedClients.Remove(client);
_removedEndpoints.Add(kp.Key);
}
}
lock (LastDataReceivedTime)
{
foreach (var r in _removedEndpoints)
{
LastDataReceivedTime.Remove(r);
}
}
}
private void TcpCheckCallback()
{
lock (ConnectedClients)
{
ConnectedClients.RemoveAll(x =>
{
if (x.IsConnected)
{
return false;
}
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = x });
return true;
});
}
} }
public void Dispose() public void Dispose()
@ -133,96 +186,52 @@ namespace EonaCat.Sockets
/// <summary> /// <summary>
/// Start server /// Start server
/// </summary> /// </summary>
public void Start() public async Task StartAsync()
{ {
try try
{ {
switch (ServerType) switch (ServerType)
{ {
case NetworkType.Tcp: case NetworkType.Tcp:
Listener = new Socket(IPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); Server = new Socket(IPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
ConfigureSocketOptions(); ConfigureSocketOptions();
// Check if we need to override the socket with a custom socket // Check if we need to override the socket with a custom socket
if (CustomTcpSocket != null) if (CustomTcpSocket != null)
{ {
Listener = CustomTcpSocket; Server = CustomTcpSocket;
} }
DisconnectTimer.Elapsed += (s, e) => DisconnectTimerTcp = new EonaCatTimer(TimeSpan.FromMilliseconds(DisconnectionCheckInterval), TcpCheckCallback);
{ DisconnectTimerTcp.Start();
lock (ConnectedClients)
{
ConnectedClients.RemoveAll(x =>
{
if (x.IsConnected)
{
return false;
}
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = x });
return true;
});
}
};
break; break;
case NetworkType.Udp: case NetworkType.Udp:
{ {
if (UDPClientManage) if (UseClientManager)
{ {
LastDataReceivedTime = new Dictionary<EndPoint, double>(); LastDataReceivedTime = new Dictionary<EndPoint, double>();
DisconnectTimer.Elapsed += (s, e) => DisconnectTimerUdp = new EonaCatTimer(TimeSpan.FromMilliseconds(DisconnectionCheckInterval), UdpCheckCallback);
{ DisconnectTimerUdp.Start();
var now = TimeNow;
var times = LastDataReceivedTime;
var removed = new List<EndPoint>();
foreach (var kp in times)
{
if (now - kp.Value > _UDPDataInterval / 1000)
{
lock (ConnectedClients)
{
var client = ConnectedClients.Where(x => x.Guid == kp.Key.ToString());
if (!client.Any())
{
continue;
}
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client.First() });
ConnectedClients.Remove(client.First());
removed.Add(kp.Key);
}
}
}
lock (LastDataReceivedTime)
{
foreach (var r in removed)
{
LastDataReceivedTime.Remove(r);
}
}
};
} }
Listener = new Socket(IPAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp); Server = new Socket(IPAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
ConfigureSocketOptions(); ConfigureSocketOptions();
// Check if we need to override the socket with a custom socket // Check if we need to override the socket with a custom socket
if (CustomUdpSocket != null) if (CustomUdpSocket != null)
{ {
Listener = CustomUdpSocket; Server = CustomUdpSocket;
} }
break; break;
} }
} }
Listener.ReceiveBufferSize = ClientConnection.BufferSize; Server.ReceiveBufferSize = Client.BufferSize;
Listener.SendBufferSize = ClientConnection.BufferSize; Server.SendBufferSize = Client.BufferSize;
Listener.Bind(new IPEndPoint(IPAddress, Port)); Server.Bind(new IPEndPoint(IPAddress, Port));
if (ServerType == NetworkType.Tcp) if (ServerType == NetworkType.Tcp)
{ {
Listener.Listen(Backlog); Server.Listen(Backlog);
} }
DisconnectTimer.Start(); await Task.Run(ListenLoop);
ListenerLoop();
} }
catch (SocketException se) catch (SocketException se)
{ {
@ -261,14 +270,14 @@ namespace EonaCat.Sockets
private void ConfigureSocketOptions() private void ConfigureSocketOptions()
{ {
// Disable ICMP packet shutdown // Disable ICMP packet shutdown
SetConnectionReset(Listener); SetConnectionReset(Server);
if (ReuseAddress) if (ReuseAddress)
{ {
Listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
} }
Listener.Blocking = IsSocketInBlockingMode; Server.Blocking = IsSocketBlockingDisabled;
} }
/// <summary> /// <summary>
@ -278,7 +287,8 @@ namespace EonaCat.Sockets
{ {
try try
{ {
DisconnectTimer.Stop(); DisconnectTimerUdp?.Stop();
DisconnectTimerTcp?.Stop();
foreach (var client in ConnectedClients) foreach (var client in ConnectedClients)
{ {
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client }); OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
@ -286,7 +296,7 @@ namespace EonaCat.Sockets
} }
ConnectedClients.Clear(); ConnectedClients.Clear();
OnStop?.Invoke(this, EventArgs.Empty); OnStop?.Invoke(this, EventArgs.Empty);
Listener.Close(); Server.Close();
} }
catch (SocketException socketException) catch (SocketException socketException)
{ {
@ -294,35 +304,29 @@ namespace EonaCat.Sockets
} }
} }
private void ListenerLoop() private void ListenLoop()
{ {
try try
{ {
OnStart?.Invoke(this, CustomTcpSocket != null || CustomUdpSocket != null ? "Custom socket was used" : string.Empty); OnStart?.Invoke(this, CustomTcpSocket != null || CustomUdpSocket != null ? "Custom socket was used" : string.Empty);
if (ServerType == NetworkType.Tcp)
{
Listener.BeginAccept(AcceptCallback, Listener);
}
if (ServerType != NetworkType.Udp) switch (ServerType)
{ {
return; case NetworkType.Tcp:
} Server.BeginAccept(AcceptCallback, Server);
var loopbackAddress = Listener.AddressFamily == AddressFamily.InterNetworkV6 break;
? IPAddress.IPv6Loopback
: IPAddress.Loopback;
if (UDPClientManage) case NetworkType.Udp:
{ var loopbackAddress = Server.AddressFamily == AddressFamily.InterNetworkV6
var buffer = new byte[ClientConnection.BufferSize]; ? IPAddress.IPv6Loopback
EndPoint remoteIp = new IPEndPoint(loopbackAddress, 0); : IPAddress.Loopback;
Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remoteIp, AcceptCallbackUDP, new object[] { buffer, remoteIp, Listener });
} var buffer = new byte[Client.BufferSize];
else EndPoint remoteIp = new IPEndPoint(loopbackAddress, 0);
{ Server.BeginReceiveFrom(buffer, 0, Client.BufferSize, 0, ref remoteIp, UseClientManager ? AcceptClientsCallbackUDP : ReadClientCallbackUdp, new object[] { buffer, remoteIp, Server });
var buffer = new byte[ClientConnection.BufferSize]; break;
EndPoint remoteEp = new IPEndPoint(loopbackAddress, 0); default:
Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remoteEp, ReadCallbackUDP, new object[] { buffer, remoteEp, Listener }); throw new NotSupportedException("Protocol type not supported");
} }
} }
catch (SocketException se) catch (SocketException se)
@ -337,7 +341,7 @@ namespace EonaCat.Sockets
{ {
var listener = (Socket)ar.AsyncState; var listener = (Socket)ar.AsyncState;
var handler = listener.EndAccept(ar); var handler = listener.EndAccept(ar);
var client = new ClientConnection() var client = new Client()
{ {
Guid = System.Guid.NewGuid().ToString(), Guid = System.Guid.NewGuid().ToString(),
Socket = handler Socket = handler
@ -347,8 +351,8 @@ namespace EonaCat.Sockets
ConnectedClients.Add(client); ConnectedClients.Add(client);
} }
OnConnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client }); OnConnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
client.Socket.BeginReceive(client.Buffer, 0, ClientConnection.BufferSize, 0, ReadCallback, client); client.Socket.BeginReceive(client.Buffer, 0, Client.BufferSize, 0, ReadCallback, client);
listener.BeginAccept(AcceptCallback, Listener); listener.BeginAccept(AcceptCallback, Server);
} }
catch (SocketException se) catch (SocketException se)
{ {
@ -361,7 +365,7 @@ namespace EonaCat.Sockets
{ {
try try
{ {
var client = (ClientConnection)ar.AsyncState; var client = (Client)ar.AsyncState;
var bytesRead = client.Socket.EndReceive(ar); var bytesRead = client.Socket.EndReceive(ar);
if (bytesRead > 0) if (bytesRead > 0)
{ {
@ -369,8 +373,7 @@ namespace EonaCat.Sockets
Array.Copy(client.Buffer, 0, data, 0, bytesRead); Array.Copy(client.Buffer, 0, data, 0, bytesRead);
OnReceived?.Invoke(this, new SocketServerDataEventArgs() { Client = client, Data = data }); OnReceived?.Invoke(this, new SocketServerDataEventArgs() { Client = client, Data = data });
} }
Thread.Sleep(DisconnectionCheckInterval); client.Socket.BeginReceive(client.Buffer, 0, Client.BufferSize, 0, ReadCallback, client);
client.Socket.BeginReceive(client.Buffer, 0, ClientConnection.BufferSize, 0, ReadCallback, client);
} }
catch (SocketException se) catch (SocketException se)
{ {
@ -379,7 +382,7 @@ namespace EonaCat.Sockets
catch (ObjectDisposedException) { } catch (ObjectDisposedException) { }
} }
private void AcceptCallbackUDP(IAsyncResult ar) private void AcceptClientsCallbackUDP(IAsyncResult ar)
{ {
try try
{ {
@ -388,27 +391,28 @@ namespace EonaCat.Sockets
var remoteIp = objects[1] as EndPoint; var remoteIp = objects[1] as EndPoint;
var listener = objects[2] as Socket; var listener = objects[2] as Socket;
var bytesRead = listener.EndReceiveFrom(ar, ref remoteIp); var bytesRead = listener.EndReceiveFrom(ar, ref remoteIp);
var clients = ConnectedClients.Where(x => x.Guid == remoteIp.ToString()).ToList();
ClientConnection client; Client client;
if (clients.Count == 0) lock (ConnectedClients)
{ {
client = new ClientConnection() client = ConnectedClients.FirstOrDefault(x => x.Guid == remoteIp.ToString());
if (client == null)
{ {
Guid = remoteIp.ToString(), client = new Client()
Socket = new Socket(remoteIp.AddressFamily, SocketType.Dgram, ProtocolType.Udp) {
}; Guid = remoteIp.ToString(),
client.SendData += SendData; Socket = new Socket(remoteIp.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
client.Socket.Connect(remoteIp); };
if (client.IsConnected) client.SendData += SendData;
{ client.Socket.Connect(remoteIp);
ConnectedClients.Add(client); if (client.IsConnected)
OnConnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client }); {
ConnectedClients.Add(client);
OnConnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
}
} }
} }
else
{
client = clients.First();
}
if (bytesRead > 1) if (bytesRead > 1)
{ {
var data = new byte[bytesRead]; var data = new byte[bytesRead];
@ -417,16 +421,20 @@ namespace EonaCat.Sockets
} }
else else
{ {
if (LastDataReceivedTime.ContainsKey(remoteIp)) lock (LastDataReceivedTime)
{ {
LastDataReceivedTime[remoteIp] = TimeNow; if (LastDataReceivedTime.ContainsKey(remoteIp))
} {
else LastDataReceivedTime[remoteIp] = TimeNow;
{ }
LastDataReceivedTime.Add(remoteIp, TimeNow); else
{
LastDataReceivedTime.Add(remoteIp, TimeNow);
}
} }
} }
Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remoteIp, AcceptCallbackUDP, new object[] { buffer, remoteIp, Listener });
Server.BeginReceiveFrom(buffer, 0, Client.BufferSize, 0, ref remoteIp, AcceptClientsCallbackUDP, new object[] { buffer, remoteIp, listener });
} }
catch (SocketException se) catch (SocketException se)
{ {
@ -435,22 +443,30 @@ namespace EonaCat.Sockets
catch (ObjectDisposedException) { } catch (ObjectDisposedException) { }
} }
private void ReadCallbackUDP(IAsyncResult ar) private void ReadClientCallbackUdp(IAsyncResult ar)
{ {
try try
{ {
var objects = (object[])ar.AsyncState; var objects = (object[])ar.AsyncState;
var buffer = objects[0] as byte[]; var buffer = objects[0] as byte[];
var remote_ip = objects[1] as EndPoint; var remoteIp = objects[1] as EndPoint;
var listener = objects[2] as Socket; var listener = objects[2] as Socket;
var bytesRead = listener.EndReceiveFrom(ar, ref remote_ip); var bytesRead = listener.EndReceiveFrom(ar, ref remoteIp);
if (bytesRead > 0) if (bytesRead > 0)
{ {
var data = new byte[bytesRead]; var data = new byte[bytesRead];
Array.Copy(buffer, 0, data, 0, bytesRead); Array.Copy(buffer, 0, data, 0, bytesRead);
OnReceived?.Invoke(this, new SocketServerDataEventArgs() { Data = data }); OnReceived?.Invoke(this, new SocketServerDataEventArgs()
{
Data = data,
Client = new Client()
{
Guid = remoteIp.ToString(),
Socket = new Socket(remoteIp.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
}
});
} }
Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remote_ip, ReadCallbackUDP, new object[] { buffer, remote_ip, Listener }); Server.BeginReceiveFrom(buffer, 0, Client.BufferSize, 0, ref remoteIp, ReadClientCallbackUdp, new object[] { buffer, remoteIp, Server });
} }
catch (SocketException se) catch (SocketException se)
{ {
@ -463,7 +479,7 @@ namespace EonaCat.Sockets
{ {
try try
{ {
var client = (ClientConnection)ar.AsyncState; var client = (Client)ar.AsyncState;
client.Socket.EndDisconnect(ar); client.Socket.EndDisconnect(ar);
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client }); OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
lock (ConnectedClients) lock (ConnectedClients)
@ -481,7 +497,7 @@ namespace EonaCat.Sockets
{ {
try try
{ {
Listener.BeginSendTo(data, 0, data.Length, 0, point, EndSendTo, null); Server.BeginSendTo(data, 0, data.Length, 0, point, EndSendTo, null);
} }
catch (SocketException se) catch (SocketException se)
{ {
@ -493,7 +509,7 @@ namespace EonaCat.Sockets
{ {
try try
{ {
Listener.EndSendTo(ar); Server.EndSendTo(ar);
} }
catch (SocketException se) catch (SocketException se)
{ {

View File

@ -5,11 +5,12 @@ namespace EonaCat.Sockets
public class SocketServerDataEventArgs : EventArgs public class SocketServerDataEventArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Client connection. /// Client.
/// </summary> /// </summary>
public ClientConnection Client { get; set; } public Client Client { get; set; }
/// <summary> /// <summary>
/// Recieved data. /// Received data.
/// </summary> /// </summary>
public byte[] Data { get; set; } public byte[] Data { get; set; }
} }
@ -19,6 +20,6 @@ namespace EonaCat.Sockets
/// <summary> /// <summary>
/// Client connection. /// Client connection.
/// </summary> /// </summary>
public ClientConnection Client { get; set; } public Client Client { get; set; }
} }
} }

View File

@ -1,64 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace EonaCat.Sockets
{
public class SocketServerPool : IDisposable
{
public List<SocketServer> Servers { get; set; } = new List<SocketServer>();
public SocketServer this[int index]
{
get
{
if (Servers.Count < index)
return Servers[index];
return null;
}
}
public SocketServer this[string name]
{
get
{
var server = Servers.Where(x => x.Guid == name);
if (server.Count() > 0)
return server.First();
return null;
}
}
public void Dispose()
{
Servers.ForEach(x => x.Stop());
Servers.Clear();
}
public SocketServer Add(NetworkType type, IPAddress ip, int port)
{
SocketServer server = new SocketServer(type, ip, port);
Servers.Add(server);
return server;
}
public bool Remove(string name)
{
int count = Servers.RemoveAll(x => x.Guid == name);
return count > 0 ? true : false;
}
public bool Remove(int index)
{
try
{
Servers.RemoveAt(index);
}
catch
{
return false;
}
return true;
}
}
}