/// <summary> /// Initializes a new instance of the <see cref="ClientConnectionContainer"/> class. /// </summary> /// <param name="tcpConnection">The TCP connection.</param> /// <param name="udpConnection">The UDP connection.</param> internal ClientConnectionContainer(TcpConnection tcpConnection, UdpConnection udpConnection) : base(tcpConnection.IPRemoteEndPoint.Address.ToString(), tcpConnection.IPRemoteEndPoint.Port) { this.tcpConnection = tcpConnection; this.udpConnection = udpConnection; TryConnect(); }
/// <summary> /// Handle the network's packets. /// </summary> /// <param name="packet">The packet to handle.</param> private void HandleDefaultPackets(Packet packet) { if (packet.GetType().Equals(typeof(PingRequest))) { Send(new PingResponse()); return; } else if (packet.GetType().Equals(typeof(PingResponse))) { long elapsedTime = currentPingStopWatch.ElapsedMilliseconds; currentPingStopWatch.Reset(); nextPingStopWatch.Restart(); Ping = elapsedTime / 2; RTT = elapsedTime; return; } else if (packet.GetType().Equals(typeof(CloseRequest))) { CloseRequest closeRequest = (CloseRequest)packet; readStreamThread.Abort(); writeStreamThread.Abort(); connectionClosed?.Invoke(closeRequest.CloseReason, this); invokePacketThread.Abort(); CloseSocket(); return; } else if (packet.GetType().Equals(typeof(EstablishUdpRequest))) { EstablishUdpRequest establishUdpRequest = (EstablishUdpRequest)packet; IPEndPoint udpEndPoint = new IPEndPoint(IPAddress.Any, GetFreePort()); Send(new EstablishUdpResponse(udpEndPoint.Port)); UdpConnection udpConnection = new UdpConnection(new UdpClient(udpEndPoint), new IPEndPoint(IPRemoteEndPoint.Address, establishUdpRequest.UdpPort), true); pendingUDPConnections.Enqueue(udpConnection); connectionEstablished?.Invoke((TcpConnection)this, udpConnection); return; } else if (packet.GetType().Equals(typeof(EstablishUdpResponseACK))) { UdpConnection udpConnection = null; while (!pendingUDPConnections.TryDequeue(out udpConnection)) { Thread.Sleep(CPU_SAVE); } udpConnection.WriteLock = false; return; } if (!packetHandler.ContainsKey(packet.GetType())) { CloseHandler(CloseReason.UnknownPacket); return; } packetHandler[packet.GetType()].Invoke(packet, this); }
/// <summary> /// Opens the new UDP connection and applies the already registered packet handlers. /// </summary> private async Task <bool> OpenNewUDPConnection() { Tuple <UdpConnection, ConnectionResult> result = await ConnectionFactory.CreateUdpConnectionAsync(tcpConnection); if (result.Item2 != ConnectionResult.Connected) { Reconnect(); return(false); } udpConnection = result.Item1; udpPacketHandlerBuffer.ForEach(u => udpConnection.RegisterPacketHandler(u.Item1, u.Item2)); udpConnection.ConnectionClosed += (c, cc) => { Reconnect(); connectionLost?.Invoke(udpConnection, ConnectionType.UDP, c); }; sendFastBuffer.ForEach(s => udpConnection.Send(s)); connectionEstablished?.Invoke(udpConnection, ConnectionType.UDP); return(true); }
/// <summary> /// Handles when a <see cref="UdpConnection"/> successfully connects to the server. /// </summary> /// <param name="tcpConnection">The parent <see cref="TcpConnection"/>.</param> /// <param name="udpConnection">The connected <see cref="UdpConnection"/>.</param> private void udpConnectionReceived(TcpConnection tcpConnection, UdpConnection udpConnection) { if (!AllowUDPConnections || this[tcpConnection].Count >= UDPConnectionLimit) { CloseReason closeReason = (this[tcpConnection].Count >= UDPConnectionLimit) ? CloseReason.UdpLimitExceeded : CloseReason.InvalidUdpRequest; tcpConnection.Close(closeReason, true); return; } this[tcpConnection].Add(udpConnection); udpConnection.NetworkConnectionClosed += connectionClosed; KnownTypes.ForEach(udpConnection.AddExternalPackets); //Inform all subscribers. if (connectionEstablished != null && connectionEstablished.GetInvocationList().Length > 0) { connectionEstablished(udpConnection, ConnectionType.UDP); } }
/// <summary> /// Asynchronously creates a <see cref="SecureUdpConnection"/> with the given parent <see cref="TcpConnection"/>. /// </summary> /// <param name="tcpConnection">The <see cref="TcpConnection"/> via which to connect the <see cref="SecureUdpConnection"/>.</param> /// <param name="rsaPair">The RSA key-pair to use.</param> /// <returns> /// A <see cref="Task"/> representing the asynchronous operation with the promise of a tuple holding the created /// <see cref="SecureUdpConnection"/> and <see cref="ConnectionResult"/> on completion. /// </returns> /// <exception cref="ArgumentException">The given <see cref="TcpConnection"/> isn't connected.</exception> public static async Task <Tuple <UdpConnection, ConnectionResult> > CreateSecureUdpConnectionAsync(TcpConnection tcpConnection, RSAPair rsaPair) { UdpConnection udpConnection = null; ConnectionResult connectionResult = ConnectionResult.Connected; CancellationTokenSource cancellationToken = new CancellationTokenSource(); cancellationToken.CancelAfter(CONNECTION_TIMEOUT); if (tcpConnection == null || !tcpConnection.IsAlive) { return(new Tuple <UdpConnection, ConnectionResult>(udpConnection, ConnectionResult.TCPConnectionNotAlive)); } tcpConnection.EstablishUdpConnection((localEndPoint, RemoteEndPoint) => udpConnection = new SecureUdpConnection(new UdpClient(localEndPoint), RemoteEndPoint, rsaPair)); while (udpConnection == null && !cancellationToken.IsCancellationRequested) { await Task.Delay(25); } if (udpConnection == null && cancellationToken.IsCancellationRequested) { connectionResult = ConnectionResult.Timeout; } return(new Tuple <UdpConnection, ConnectionResult>(udpConnection, connectionResult)); }
/// <summary> /// Creates a new instance of a udp connection async. /// </summary> /// <param name="tcpConnection">The tcp connection to establish the udp connection.</param> /// <returns>Task<UdpConnection>.</returns> /// <exception cref="ArgumentException">TcpConnection is not connected to the endpoint.</exception> public static async Task <Tuple <UdpConnection, ConnectionResult> > CreateUdpConnectionAsync(TcpConnection tcpConnection) { UdpConnection udpConnection = null; ConnectionResult connectionResult = ConnectionResult.Connected; CancellationTokenSource cancellationToken = new CancellationTokenSource(); cancellationToken.CancelAfter(CONNECTION_TIMEOUT); if (tcpConnection == null || !tcpConnection.IsAlive) { throw new ArgumentException("TcpConnection is not connected to the endpoint."); } tcpConnection.EstablishUdpConnection((localEndPoint, RemoteEndPoint) => udpConnection = new UdpConnection(new UdpClient(localEndPoint), RemoteEndPoint)); while (udpConnection == null && !cancellationToken.IsCancellationRequested) { await Task.Delay(25); } if (udpConnection == null && cancellationToken.IsCancellationRequested) { connectionResult = ConnectionResult.Timeout; } return(new Tuple <UdpConnection, ConnectionResult>(udpConnection, connectionResult)); }
/// <summary> /// Creates a <see cref="SecureClientConnectionContainer"/> with the given <see cref="TcpConnection"/> and <see cref="UdpConnection"/>. /// </summary> /// <param name="tcpConnection">The <see cref="TcpConnection"/> to use.</param> /// <param name="udpConnection">The <see cref="UdpConnection"/> to use.</param> /// <param name="keySize">The size of the RSA keys.</param> /// <returns>The created <see cref="SecureClientConnectionContainer"/>.</returns> /// <exception cref="ArgumentException">Thrown if the given <see cref="TcpConnection"/> is not connected.</exception> public static ClientConnectionContainer CreateSecureClientConnectionContainer(TcpConnection tcpConnection, UdpConnection udpConnection, int keySize = 2048) => CreateSecureClientConnectionContainer(tcpConnection, udpConnection, RSAKeyGeneration.Generate(keySize));
/// <summary> /// Creates a <see cref="ClientConnectionContainer"/> with the given <see cref="TcpConnection"/> and <see cref="UdpConnection"/>. /// </summary> /// <param name="tcpConnection">The <see cref="TcpConnection"/> to use.</param> /// <param name="udpConnection">The <see cref="UdpConnection"/> to use.</param> /// <returns>The created <see cref="ClientConnectionContainer"/>.</returns> /// <exception cref="ArgumentException">Thrown if the given <see cref="TcpConnection"/> is not connected.</exception> public static ClientConnectionContainer CreateClientConnectionContainer(TcpConnection tcpConnection, UdpConnection udpConnection) { if (tcpConnection == null || !tcpConnection.IsAlive) { throw new ArgumentException("TCP connection must be connected to an endpoint."); } var clientConnectionContainer = new ClientConnectionContainer(tcpConnection, udpConnection); clientConnectionContainer.Initialize(); return(clientConnectionContainer); }
/// <summary> /// Creates a new instance of a connection container. /// </summary> /// <param name="tcpConnection">The TCP connection.</param> /// <param name="udpConnection">The UDP connection.</param> /// <returns>ConnectionContainer.</returns> /// <exception cref="System.ArgumentException">TCP and UDP connection must be connected to an endpoint.</exception> public static ClientConnectionContainer CreateClientConnectionContainer(TcpConnection tcpConnection, UdpConnection udpConnection) { if (tcpConnection == null ||!tcpConnection.IsAlive) throw new ArgumentException("TCP connection must be connected to an endpoint."); return new ClientConnectionContainer(tcpConnection, udpConnection); }
/// <summary> /// Creates a new instance of a udp connection async. /// </summary> /// <param name="tcpConnection">The tcp connection to establish the udp connection.</param> /// <returns>Task<UdpConnection>.</returns> /// <exception cref="ArgumentException">TcpConnection is not connected to the endpoint.</exception> public static async Task<Tuple<UdpConnection, ConnectionResult>> CreateUdpConnectionAsync(TcpConnection tcpConnection) { UdpConnection udpConnection = null; ConnectionResult connectionResult = ConnectionResult.Connected; CancellationTokenSource cancellationToken = new CancellationTokenSource(); cancellationToken.CancelAfter(CONNECTION_TIMEOUT); if (tcpConnection == null || !tcpConnection.IsAlive) throw new ArgumentException("TcpConnection is not connected to the endpoint."); tcpConnection.EstablishUdpConnection((localEndPoint, RemoteEndPoint) => udpConnection = new UdpConnection(new UdpClient(localEndPoint), RemoteEndPoint)); while (udpConnection == null && !cancellationToken.IsCancellationRequested) await Task.Delay(25); if (udpConnection == null && cancellationToken.IsCancellationRequested) connectionResult = ConnectionResult.Timeout; return new Tuple<UdpConnection, ConnectionResult>(udpConnection, connectionResult); }
/// <summary> /// Opens the new UDP connection and applies the already registered packet handlers. /// </summary> private async Task<bool> OpenNewUDPConnection() { Tuple<UdpConnection, ConnectionResult> result = await ConnectionFactory.CreateUdpConnectionAsync(tcpConnection); if (result.Item2 != ConnectionResult.Connected) { Reconnect(); return false; } udpConnection = result.Item1; udpPacketHandlerBuffer.ForEach(u => udpConnection.RegisterPacketHandler(u.Item1, u.Item2)); udpConnection.ConnectionClosed += (c, cc) => { Reconnect(); connectionLost?.Invoke(udpConnection, ConnectionType.UDP, c); }; sendFastBuffer.ForEach(s => udpConnection.Send(s)); connectionEstablished?.Invoke(udpConnection, ConnectionType.UDP); return true; }
/// <summary> /// Creates a <see cref="SecureClientConnectionContainer"/> with the given <see cref="TcpConnection"/> and <see cref="UdpConnection"/>. /// </summary> /// <param name="tcpConnection">The <see cref="TcpConnection"/> to use.</param> /// <param name="udpConnection">The <see cref="UdpConnection"/> to use.</param> /// <param name="publicKey">The public RSA key in xml format. (https://superdry.apphb.com/tools/online-rsa-key-converter)</param> /// <param name="privateKey">The private RSA key in xml format. (https://superdry.apphb.com/tools/online-rsa-key-converter)</param> /// <param name="keySize">The size of the RSA keys.</param> /// <returns>The created <see cref="SecureClientConnectionContainer"/>.</returns> /// <exception cref="ArgumentException">Thrown if the given <see cref="TcpConnection"/> is not connected.</exception> public static ClientConnectionContainer CreateSecureClientConnectionContainer(TcpConnection tcpConnection, UdpConnection udpConnection, string publicKey, string privateKey, int keySize = 2048) => CreateSecureClientConnectionContainer(tcpConnection, udpConnection, new RSAPair(publicKey, privateKey, keySize));
/// <summary> /// Gets the <see cref="TcpConnection"/> with the specified UDP connection. /// </summary> /// <param name="udpConnection">The UDP connection.</param> /// <returns>TcpConnection.</returns> public TcpConnection this[UdpConnection udpConnection] { get { return connections.Single(c => c.Value.Count(uc => uc.GetHashCode().Equals(udpConnection.GetHashCode())) > 0).Key; } }
/// <summary> /// A UDP connection has been established. /// </summary> /// <param name="arg1">The arg1.</param> /// <param name="arg2">The arg2.</param> /// <exception cref="NotImplementedException"></exception> private void udpConnectionReceived(TcpConnection tcpConnection, UdpConnection udpConnection) { if (!AllowUDPConnections || this[tcpConnection].Count >= UDPConnectionLimit) { CloseReason closeReason = (this[tcpConnection].Count >= UDPConnectionLimit) ? CloseReason.UdpLimitExceeded : CloseReason.InvalidUdpRequest; tcpConnection.Close(closeReason); tcpOrUdpConnectionClosed(closeReason, tcpConnection); return; } this[tcpConnection].Add(udpConnection); udpConnection.ConnectionClosed += tcpOrUdpConnectionClosed; //Inform all subscribers. if (connectionEstablished != null && connectionEstablished.GetInvocationList().Length > 0) connectionEstablished(udpConnection, ConnectionType.UDP); }
/// <summary> /// Creates a new instance of a secure-connection container. (RSA Encryption) /// </summary> /// <param name="tcpConnection">The TCP connection.</param> /// <param name="udpConnection">The UDP connection.</param> /// <param name="keySize">The keySize.</param> /// <returns>ConnectionContainer.</returns> /// <exception cref="System.ArgumentException">TCP and UDP connection must be connected to an endpoint.</exception> public static ClientConnectionContainer CreateSecureClientConnectionContainer(TcpConnection tcpConnection, UdpConnection udpConnection, int keySize = 2048, bool UseUDP = false) => CreateSecureClientConnectionContainer(tcpConnection, udpConnection, RSAKeyGeneration.Generate(keySize), UseUDP);
/// <summary> /// Handles all default <see cref="Packet"/>s that are in the library. /// </summary> /// <param name="packet">The <see cref="Packet"/> to be handled.</param> private void HandleDefaultPackets(Packet packet) { if (packet.GetType().Equals(typeof(PingRequest))) { Send(new PingResponse()); return; } else if (packet.GetType().Equals(typeof(PingResponse))) { long elapsedTime = currentPingStopWatch.ElapsedMilliseconds; currentPingStopWatch.Reset(); nextPingStopWatch.Restart(); Ping = elapsedTime / 2; RTT = elapsedTime; return; } else if (packet.GetType().Equals(typeof(CloseRequest))) { CloseRequest closeRequest = (CloseRequest)packet; ExternalClose(closeRequest.CloseReason); return; } else if (packet.GetType().Equals(typeof(EstablishUdpRequest))) { EstablishUdpRequest establishUdpRequest = (EstablishUdpRequest)packet; IPEndPoint udpEndPoint = new IPEndPoint(IPAddress.Any, GetFreePort()); Send(new EstablishUdpResponse(udpEndPoint.Port, establishUdpRequest)); UdpConnection udpConnection = CreateUdpConnection(udpEndPoint, new IPEndPoint(IPRemoteEndPoint.Address, establishUdpRequest.UdpPort), true); pendingUDPConnections.Enqueue(udpConnection); connectionEstablished?.Invoke((TcpConnection)this, udpConnection); return; } else if (packet.GetType().Equals(typeof(EstablishUdpResponseACK))) { UdpConnection udpConnection = null; while (!pendingUDPConnections.TryDequeue(out udpConnection)) { Thread.Sleep(IntPerformance); } udpConnection.AcknowledgePending = false; return; } else if (packet.GetType().Equals(typeof(AddPacketTypeRequest))) { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName == ((AddPacketTypeRequest)packet).AssemblyName).SingleOrDefault(); if (assembly == null) { CloseHandler(CloseReason.AssemblyDoesNotExist); } else { AddExternalPackets(assembly); } Send(new AddPacketTypeResponse(typeByte.Values.ToList(), (AddPacketTypeRequest)packet)); return; } else if (packet.GetType().Equals(typeof(AddPacketTypeResponse))) { List <Tuple <Packet, object> > internalSendQueue = new List <Tuple <Packet, object> >(); AddPacketTypeResponse addPacketTypeResponse = (AddPacketTypeResponse)packet; //Remove all packets of this type and send them :) while (true) { Tuple <Packet, object> toSend = null; while (!pendingUnknownPackets.TryPeek(out toSend) && pendingUnknownPackets.Count > 0) { Thread.Sleep(IntPerformance); //Wait till we got a result. } //If the other connection contains that packet, send it. if (toSend != null && addPacketTypeResponse.LocalDict.Contains(typeByte[toSend.Item1.GetType()])) { while (!pendingUnknownPackets.TryDequeue(out toSend)) { Thread.Sleep(IntPerformance); //Wait till we got a result. } internalSendQueue.Add(new Tuple <Packet, object>(toSend.Item1, toSend.Item2)); continue; } //Now the pendingUnknownPackets queue doesn't contain those elements any more. internalSendQueue.ForEach(i => Send(i.Item1, i.Item2)); return; } } //Receiving raw data from the connection. else if (packet.GetType().Equals(typeof(RawData))) { RawData rawData = (RawData)packet; if (packetHandlerMap[rawData.Key] == null) { Logger.Log($"RawData packet has no listener. Key: {rawData.Key}", LogLevel.Warning); } else { packetHandlerMap[rawData.Key].DynamicInvoke(new object[] { packet, this }); } return; } try { if (packet.GetType().IsSubclassOf(typeof(ResponsePacket)) && packetHandlerMap[packet.ID] != null) { packetHandlerMap[packet.ID].DynamicInvoke(new object[] { packet, this }); } else if (packetHandlerMap[packet.GetType()] != null) { packetHandlerMap[packet.GetType()].DynamicInvoke(new object[] { packet, this }); } else { PacketWithoutHandlerReceived(packet); } } catch (Exception exception) { Logger.Log("Provided delegate contains a bug. Packet invocation thread crashed.", exception, LogLevel.Exception); } }
/// <summary> /// Opens the new UDP connection and applies the already registered packet handlers. /// </summary> private async Task OpenNewUDPConnection() { Tuple <UdpConnection, ConnectionResult> result = await CreateUdpConnection(); if (result.Item2 != ConnectionResult.Connected) { Reconnect(); return; } udpConnection = result.Item1; //Restore old state by adding old packets udpConnection.RestorePacketHandler(udpPacketHandlerBackup); //Restore new state by adding packets the user wanted to register while the connection was dead. udpPacketHandlerBuffer.ForEach(t => { MethodInfo registerPacketHandler = typeof(Connection).GetMethod("RegisterPacketHandler", BindingFlags.NonPublic | BindingFlags.Instance); registerPacketHandler = registerPacketHandler.MakeGenericMethod(t.Item1); registerPacketHandler.Invoke(udpConnection, new object[2] { t.Item2, t.Item3 }); }); udpStaticPacketHandlerBuffer.ForEach(t => { MethodInfo registerPacketHandler = typeof(Connection).GetMethod("RegisterStaticPacketHandler", BindingFlags.NonPublic | BindingFlags.Instance); registerPacketHandler = registerPacketHandler.MakeGenericMethod(t.Item1); registerPacketHandler.Invoke(udpConnection, new object[] { t.Item2 }); }); udpConnection.ConnectionClosed += (c, cc) => { udpPacketHandlerBackup = cc.ObjectMapper; connectionLost?.Invoke(udpConnection, ConnectionType.UDP, c); Reconnect(); }; sendFastBuffer.ForEach(udpConnection.Send); sendFastObjectBuffer.ForEach(p => udpConnection.Send(p.Item1, p.Item2)); //Restore new state by removing the packets the user wanted to unregister while the connection was dead. udpUnPacketHandlerBuffer.ForEach(t => { MethodInfo unRegisterPacketHandler = typeof(Connection).GetMethod("UnRegisterPacketHandler"); unRegisterPacketHandler = unRegisterPacketHandler.MakeGenericMethod(t.Item1); unRegisterPacketHandler.Invoke(udpConnection, new object[] { t.Item2 }); }); udpStaticUnPacketHandlerBuffer.ForEach(t => { MethodInfo unRegisterPacketHandler = typeof(Connection).GetMethod("UnRegisterStaticPacketHandler"); unRegisterPacketHandler = unRegisterPacketHandler.MakeGenericMethod(t); unRegisterPacketHandler.Invoke(udpConnection, null); }); KnownTypes.ForEach(UdpConnection.AddExternalPackets); //Clear the buffers since we added and removed the packet types. sendFastBuffer.Clear(); sendFastObjectBuffer.Clear(); udpPacketHandlerBuffer.Clear(); udpUnPacketHandlerBuffer.Clear(); udpStaticPacketHandlerBuffer.Clear(); udpStaticUnPacketHandlerBuffer.Clear(); if (!UdpConnection.IsAlive) { return; //Connection could already be dead because of the prePackets. } connectionEstablished?.Invoke(udpConnection, ConnectionType.UDP); }
/// <summary> /// Creates a <see cref="SecureClientConnectionContainer"/> with the given <see cref="TcpConnection"/> and <see cref="UdpConnection"/>. /// </summary> /// <param name="tcpConnection">The <see cref="TcpConnection"/> to use.</param> /// <param name="udpConnection">The <see cref="UdpConnection"/> to use.</param> /// <param name="rsaPair">The RSA key-pair to use.</param> /// <returns>The created <see cref="SecureClientConnectionContainer"/>.</returns> /// <exception cref="ArgumentException">Thrown if the given <see cref="TcpConnection"/> is not connected.</exception> public static ClientConnectionContainer CreateSecureClientConnectionContainer(TcpConnection tcpConnection, UdpConnection udpConnection, RSAPair rsaPair) { if (tcpConnection == null || !tcpConnection.IsAlive) { throw new ArgumentException("TCP connection must be connected to an endpoint."); } var secureClientConnectionContainer = new SecureClientConnectionContainer(tcpConnection, udpConnection, rsaPair); secureClientConnectionContainer.Initialize(); return(secureClientConnectionContainer); }
/// <summary> /// Returns the parent <see cref="TcpConnection"/> of the given <see cref="UdpConnection"/>. /// </summary> /// <param name="udpConnection">The <see cref="UdpConnection"/> whose parent <see cref="TcpConnection"/> to return.</param> /// <returns>The <see cref="TcpConnection"/> which owns the given <see cref="UdpConnection"/>.</returns> public TcpConnection this[UdpConnection udpConnection] { get { return(connections.SingleOrDefault(c => c.Value.Count(uc => uc.GetHashCode().Equals(udpConnection.GetHashCode())) > 0).Key); } }