private void UdpListenIteration() { IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, _port); byte[] datagram = _udpClient.Receive(ref endPoint); int connectionId = GetConnectionId(endPoint); UdpPackage udpPackage = new UdpPackage(); udpPackage.FromByteArray(datagram, 0); if (udpPackage.Reliability == Reliability.Reliable) { UdpPackage confirmationPackage = new UdpPackage(UdpCommand.Confirmation, Reliability.Unreliable, udpPackage.ConfirmationToken, null); byte[] confirmationPackageBytes = new byte[confirmationPackage.Length]; confirmationPackage.ToByteArray(confirmationPackageBytes, 0); _udpClient.Send(confirmationPackageBytes, confirmationPackageBytes.Length, endPoint); } switch (udpPackage.Command) { case UdpCommand.Connect: int pos = 0; ulong connectionCode = BitConverter.ToUInt64(udpPackage.Payload, pos); pos += sizeof(ulong); int nameLength = BitConverter.ToInt32(udpPackage.Payload, pos); pos += sizeof(int); string username = Encoding.Unicode.GetString(udpPackage.Payload, pos, nameLength); pos += nameLength; byte[] userResponse = NewConnectionResponse?.Invoke(); byte[] response; response = userResponse != null ? new byte[userResponse.Length + sizeof(ulong)] : new byte[sizeof(long)]; byte[] connectionCodeBytes = BitConverter.GetBytes(connectionCode); Array.Copy(connectionCodeBytes, 0, response, 0, connectionCodeBytes.Length); if (userResponse != null) { Array.Copy(userResponse, 0, response, sizeof(ulong), userResponse.Length); } if (connectionId != -1) { // Client is reconnecting SendData(endPoint, UdpCommand.Connect, response, Reliability.Unreliable); Debug.WriteLine($"Client {connectionId} (${endPoint}) reconnected"); lock (_reconnectQueue) { _reconnectQueue.Enqueue(connectionId); } } else { // Check connection code. May be this is an old client trying to reconnect int codeId = GetConnectionCodeId(connectionCode); if (codeId != -1) { _connections[codeId] = endPoint; SendData(endPoint, UdpCommand.Connect, response, Reliability.Unreliable); // This client is reconnecting. We need to replace old EndPoint with new one Debug.WriteLine( $"Client {connectionId} (${endPoint}) reconnected with different EndPoint"); lock (_reconnectQueue) { _reconnectQueue.Enqueue(codeId); } break; } else if (NewConnectionsEnabled) { // New connection int newId = InsertNewEndPoint(endPoint, out ulong code); // Answering with private connection code and Connect command. This means that server accepted connection SendData(endPoint, UdpCommand.Connect, response, Reliability.Unreliable); Debug.WriteLine($"New client with id {newId} and name {username} ({endPoint}) connected"); lock (_newConnectionQueue) { _newConnectionQueue.Enqueue(newId); _usernamesQueue.Enqueue(username); } } else { // Client didn't provide connection code. Ignoring Debug.WriteLine($"Client (${endPoint}) rejected. No connection code provided"); } } break; case UdpCommand.Disconnect: Debug.WriteLine($"Client with id {connectionId} (${endPoint}) disconnected by client's will"); lock (_disconnectQueue) { _disconnectQueue.Enqueue(connectionId); } _connections[connectionId] = null; break; case UdpCommand.Data: Debug.WriteLine($"Client with id {connectionId} (${endPoint}) data received"); lock (_receiveDataQueue) { _receiveIdQueue.Enqueue(connectionId); _receiveDataQueue.Enqueue(udpPackage.Payload); } break; case UdpCommand.Confirmation: ulong token = udpPackage.ConfirmationToken; lock (_retransmissionQueue) { int retransmissionIndex = _retransmissionQueue.FindIndex(r => r.ConfirmationToken == token); if (retransmissionIndex == -1) { } else { _retransmissionQueue[retransmissionIndex].MarkedAsSuccessful = true; _retransmissionQueue.RemoveAt(retransmissionIndex); } } //LogManager.RuntimeLogger.Log($"Confirmation token {token} received!"); break; default: Debug.WriteLine($"Unknown command {udpPackage.Command}"); break; } }
private void ListeningIteration() { IPEndPoint sender = new IPEndPoint(IPAddress.Any, _serverEndPoint.Port); byte[] datagram = _udpClient.Receive(ref sender); if (!Equals(sender.Address, _serverEndPoint.Address) || sender.Port != _serverEndPoint.Port) { // Ignore any data that was sent by anybody except server Debug.WriteLine("Datagram ignored"); return; } UdpPackage udpPackage = new UdpPackage(); udpPackage.FromByteArray(datagram, 0); //Debug.WriteLine($"[UdpNetworkClient] Command {udpPackage.Command}"); if (udpPackage.Reliability == Reliability.Reliable) { UdpPackage confirmationPackage = new UdpPackage(UdpCommand.Confirmation, Reliability.Unreliable, udpPackage.ConfirmationToken, null); byte[] confirmationPackageBytes = new byte[confirmationPackage.Length]; confirmationPackage.ToByteArray(confirmationPackageBytes, 0); _udpClient.Send(confirmationPackageBytes, confirmationPackageBytes.Length, _serverEndPoint); } switch (udpPackage.Command) { case UdpCommand.Connect: _isConnected = true; _connectionCode = BitConverter.ToUInt64(udpPackage.Payload, 0); byte[] serverResponseAux = new byte[udpPackage.Payload.Length - sizeof(ulong)]; Array.Copy(udpPackage.Payload, sizeof(ulong), serverResponseAux, 0, serverResponseAux.Length); //Debug.WriteLine($"Connected to server with code {_connectionCode}"); lock (_commandQueue) { Debug.WriteLine($"Enqueueing connection command"); _commandQueue.Enqueue(UdpCommand.Connect); _dataQueue.Enqueue(serverResponseAux); } break; case UdpCommand.Disconnect: _isConnected = false; //Debug.WriteLine($"Server sent disconnect command"); lock (_commandQueue) { _commandQueue.Enqueue(UdpCommand.Disconnect); } break; case UdpCommand.Data: //Debug.WriteLine($"[UdpNetworkClient] Data from server received. Entering LOCK"); lock (_commandQueue) { _commandQueue.Enqueue(UdpCommand.Data); _dataQueue.Enqueue(udpPackage.Payload); } break; case UdpCommand.Confirmation: ulong token = udpPackage.ConfirmationToken; //Debug.WriteLine($"[UdpNetworkClient] Confirmation for token {token} received"); lock (_retransmissions) { int retransmissionIndex = _retransmissions.FindIndex(r => r.ConfirmationToken == token); _retransmissions.RemoveAt(retransmissionIndex); } break; default: Debug.WriteLine($"Unknown command {udpPackage.Command}"); break; } }