diff --git a/MistoxServer/Client/MistoxClient.cs b/MistoxServer/Client/MistoxClient.cs index 4466ffa..040fb4b 100644 --- a/MistoxServer/Client/MistoxClient.cs +++ b/MistoxServer/Client/MistoxClient.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Net; +using System.Net.Sockets; using Mistox.ResevedPackets; namespace Mistox.Client { @@ -10,9 +12,16 @@ namespace Mistox.Client { public string ServerName { get; set; } = ""; // The Name of the server public bool Alive { get; set; } = true; // Is the server running public mTCPClient ServerConnection; - public ConcurrentDictionary clientConnections; + public UdpClient ClientSocket; + public UDPReceiver UdpReceiver; + public ConcurrentDictionary clientConnections; // IP Address, Client public MistoxClient(IPEndPoint Server) { + // Create and listen on UDP + ClientSocket = new UdpClient( Server.Port ); + UdpReceiver = new UDPReceiver( this ); + + // Create and connect to server on TCP Console.WriteLine( "The client is initilized and trying to connect to the server at ip : " + Server.Address ); try { ServerConnection = new mTCPClient( this, Server ); @@ -25,37 +34,35 @@ namespace Mistox.Client { Alive = true; } +///////////////////////////// +// Receive Methods +///////////////////////////// + public void UserConnected(UserConnectedPacket packet) { mUDPClient user = new mUDPClient(this, packet.ClientIPAddress, packet.ClientPort) { UserName = packet.UserName }; - clientConnections[packet.UserName] = user; + clientConnections[user.GetConnectionKey()] = user; InvokeOnUserConnected(user); } public void UserDisconnected(UserDisconnectedPacket packet) { - mUDPClient copy = clientConnections[packet.UserName]; - clientConnections[packet.UserName].Alive = false; - clientConnections[packet.UserName].Dispose(); - clientConnections[packet.UserName] = null; - InvokeOnUserDisconnected( copy ); + mUDPClient client; + bool found = clientConnections.TryGetValue(packet.ConnectionKey, out client); + if (found) { + InvokeOnUserDisconnected( client ); + client.Alive = false; + client.Dispose(); + clientConnections[packet.ConnectionKey] = null; + } } + ///////////////////////////// // Events - - public event Action onServerConnected; - public void InvokeOnServerConnected(){ - Alive = true; - onServerConnected?.Invoke(); - } - - public event Action onUserConnected; - public void InvokeOnUserConnected(mUDPClient ConnectedUser){ - onUserConnected?.Invoke(ConnectedUser); - } + ///////////////////////////// public event Action onDataReceived; - public void InvokeOnReceived(dynamic DataPacket){ + public void InvokeOnReceived(dynamic DataPacket, IPEndPoint remote = null){ switch (DataPacket as object) { case UserConnectedPacket packet: UserConnected(packet); @@ -72,6 +79,17 @@ namespace Mistox.Client { } } + public event Action onServerConnected; + public void InvokeOnServerConnected(){ + Alive = true; + onServerConnected?.Invoke(); + } + + public event Action onUserConnected; + public void InvokeOnUserConnected(mUDPClient ConnectedUser){ + onUserConnected?.Invoke(ConnectedUser); + } + public event Action onUserDisconnected; public void InvokeOnUserDisconnected(mUDPClient DisconnectedUser){ onUserDisconnected?.Invoke(DisconnectedUser); diff --git a/MistoxServer/Client/UDPClient.cs b/MistoxServer/Client/UDPClient.cs index 975b79c..0cf5a62 100644 --- a/MistoxServer/Client/UDPClient.cs +++ b/MistoxServer/Client/UDPClient.cs @@ -1,72 +1,44 @@ using System; using System.Net; -using System.Net.Sockets; -using System.Threading; using System.Threading.Tasks; -// Network Sync - namespace Mistox.Client { public class mUDPClient : IDisposable { public string UserName { get; set; } = ""; // The remote players UserName public bool Alive { get; set; } = true; // Is the connection alive + public IPEndPoint IPAddress { get; set; } // The remote IP Address - Socket _udpClient; IPEndPoint _remoteHost; MistoxClient _parent; - public mUDPClient( MistoxClient parent, long RemoteIP, int Port ) { + public mUDPClient( MistoxClient parent, IPAddress RemoteIP, int Port ) { _parent = parent; _remoteHost = new IPEndPoint( RemoteIP, Port ); - _udpClient = new Socket( AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp ); - _udpClient.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.ReuseAddress, true ); - _udpClient.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, 128 ); - _udpClient.Bind( new IPEndPoint( IPAddress.Any, Port ) ); _parent.Alive = true; - Thread Client = new Thread(async() => { await ReceiveThread(); }); - Client.Start(); - } - - async Task ReceiveThread() { - byte[] buffer = new byte[65535]; - while( _parent.Alive ) { - try { - - // Read the data out of the UDP packet with a buffer that has the maximum possible UDP packet size - // As UDP reads the whole next packet then drops. Im guaranteed to receive 1 packet at a time - int bytesRead = await _udpClient.ReceiveAsync( buffer ); - - // Create the data readers - ReadOnlyMemory dataMemory = buffer.AsMemory(0, bytesRead); - ReadOnlySpan dataPacket = dataMemory.Span; - - // Split apart the packet - byte dataType = dataPacket[0]; - ushort DataLength = (ushort)((dataPacket[1] << 8) | dataPacket[2]); - ReadOnlyMemory data = dataMemory.Slice(DataPacket.HeaderLength, DataLength); - - // Extract the object - dynamic buildObj = DataPacket.Deserialize( dataType, DataLength, data ); - - // Return the object - _parent.InvokeOnReceived( buildObj ); - - } catch( Exception e ) { - Console.WriteLine( "UDP Receive exception : " + e.Message ); - } - } } public async Task Send( T Data ) { byte[] byteData = DataPacket.Serialize( Data ); - await _udpClient.SendToAsync( byteData, SocketFlags.None, _remoteHost ); + await _parent.ClientSocket.SendAsync( byteData, byteData.Length, _remoteHost ); + } + + public static UInt128 GetConnectionKey(IPAddress ipAddress) { + byte[] bytes = ipAddress.GetAddressBytes(); + if (bytes.Length == 4) { + bytes = ipAddress.MapToIPv6().GetAddressBytes(); + } + return BitConverter.ToUInt128(bytes, 0); + } + + public UInt128 GetConnectionKey() { + return GetConnectionKey( IPAddress.Address ); } public void Dispose() { _parent.Alive = false; - _udpClient.Close(); - _udpClient = null; + _parent.ClientSocket.Close(); + _parent.ClientSocket = null; } } } \ No newline at end of file diff --git a/MistoxServer/Client/UDPReceiver.cs b/MistoxServer/Client/UDPReceiver.cs new file mode 100644 index 0000000..267be72 --- /dev/null +++ b/MistoxServer/Client/UDPReceiver.cs @@ -0,0 +1,62 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Mistox.Client { + public class UDPReceiver : IDisposable { + + MistoxClient _parent; + + public UDPReceiver( MistoxClient parent) { + _parent = parent; + Thread Client = new Thread(async() => { await ReceiveThread(); }); + Client.Start(); + } + + async Task ReceiveThread() { + while( _parent.Alive ) { + try { + // Read the data out of the UDP packet with a buffer that has the maximum possible UDP packet size + // As UDP reads the whole next packet then drops. Im guaranteed to receive 1 packet at a time + UdpReceiveResult udpPacket = await _parent.ClientSocket.ReceiveAsync(); + + // Compute the connectionKey + UInt128 ConnectionKey = mUDPClient.GetConnectionKey(udpPacket.RemoteEndPoint.Address); + + // Try find the user + mUDPClient client = null; + bool clientExists = _parent.clientConnections.TryGetValue(ConnectionKey, out client); + if (clientExists) { + + // Create the data readers + ReadOnlyMemory dataMemory = udpPacket.Buffer.AsMemory(); + ReadOnlySpan dataPacket = dataMemory.Span; + + // Split apart the packet + byte dataType = dataPacket[0]; + ushort DataLength = (ushort)((dataPacket[1] << 8) | dataPacket[2]); + ReadOnlyMemory data = dataMemory.Slice(DataPacket.HeaderLength, DataLength); + + // Extract the object + dynamic buildObj = DataPacket.Deserialize( dataType, DataLength, data ); + + // Map the IPEndPoint to a user + IPEndPoint endpoint = udpPacket.RemoteEndPoint; + + // Return the object + _parent.InvokeOnReceived( buildObj, udpPacket.RemoteEndPoint ); + + } + } catch( Exception e ) { + Console.WriteLine( "UDP Receive exception : " + e.Message ); + } + } + } + + public void Dispose() { + _parent.Alive = false; + } + } +} \ No newline at end of file diff --git a/MistoxServer/Interface/ClientInterface.cs b/MistoxServer/Interface/ClientInterface.cs index c5f72e7..bf17ebc 100644 --- a/MistoxServer/Interface/ClientInterface.cs +++ b/MistoxServer/Interface/ClientInterface.cs @@ -36,13 +36,18 @@ namespace Mistox { } public async Task SendAllClients(Packet data) { - foreach(KeyValuePair cur in serverInstance.clientConnections){ + foreach( KeyValuePair cur in serverInstance.clientConnections ){ await cur.Value.Send( data ); } } public async Task SendClient(string UserName, Packet data) { - await serverInstance.clientConnections[UserName].Send( data ); + foreach( KeyValuePair cur in serverInstance.clientConnections) { + if (cur.Value.UserName.ToLower() == UserName.ToLower()) { + await cur.Value.Send( data ); + break; + } + } } public async Task SendServer(Packet data) { diff --git a/MistoxServer/mPacket/ResevedPackets.cs b/MistoxServer/mPacket/ResevedPackets.cs index d646557..34e6505 100644 --- a/MistoxServer/mPacket/ResevedPackets.cs +++ b/MistoxServer/mPacket/ResevedPackets.cs @@ -1,3 +1,5 @@ +using System; +using System.Net; using Mistox.Attributes; namespace Mistox.ResevedPackets { @@ -11,13 +13,13 @@ namespace Mistox.ResevedPackets { [mPacket(0xFE)] public class UserConnectedPacket { public string UserName { get; set; } - public long ClientIPAddress { get; set; } + public IPAddress ClientIPAddress { get; set; } public int ClientPort { get; set; } } [mPacket(0xFD)] public class UserDisconnectedPacket { - public string UserName { get; set; } + public UInt128 ConnectionKey { get; set; } } } \ No newline at end of file