private static void TaskVoiceServerHeartbeat( Logger logger, CancellationToken cancelToken, ClientConnectionData connection, BlockingCollection <IMsgPackTypeName> transmitQueue) { logger.Debug(nameof(TaskVoiceServerHeartbeat) + " started"); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); var keepAlive = new HeartbeatDto() { Callsign = connection.Callsign }; while (!cancelToken.IsCancellationRequested) { if (stopWatch.ElapsedMilliseconds > 3000) { transmitQueue.Add(keepAlive); stopWatch.Restart(); } Thread.Sleep(500); } logger.Debug(nameof(TaskVoiceServerHeartbeat) + " stopped"); }
public ClientConnection(string apiServer) { connection = new ClientConnectionData(); connection.ApiServerConnection = new ApiServerConnection(apiServer); connection.ReceiveAudio = true; connection.Callsign = null; //client = new RestClient(apiServer); VoiceServerTransmitQueue = new BlockingCollection <IMsgPackTypeName>(); VoiceServerReceiveQueue = new BlockingCollection <IMsgPackTypeName>(); //DataServerTransmitQueue = new BlockingCollection<IMsgPack>(); //DataServerReceiveQueue = new BlockingCollection<IMsgPack>(); logger.Debug(nameof(ClientConnection) + " instantiated"); }
private static void TaskServerConnectionCheck( Logger logger, CancellationToken cancelToken, ClientConnectionData connection, Action <DisconnectReasons> disconnectReason) { logger.Debug(nameof(TaskServerConnectionCheck) + " started"); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); while (!cancelToken.IsCancellationRequested) { if (stopWatch.ElapsedMilliseconds > 3000) { if (connection.IsConnected && !connection.VoiceServerAlive) { logger.Error("Lost connection to Voice Server"); disconnectReason(DisconnectReasons.LostConnection); } if (connection.IsConnected && connection.TaskVoiceServerHeartbeat?.Status != TaskStatus.Running) { logger.Error("TaskVoiceServerHeartbeat not running"); disconnectReason(DisconnectReasons.InternalLibraryError10); } if (connection.IsConnected && connection.TaskVoiceServerReceive?.Status != TaskStatus.Running) { logger.Error("TaskVoiceServerReceive not running"); disconnectReason(DisconnectReasons.InternalLibraryError20); } if (connection.IsConnected && connection.TaskVoiceServerTransmit?.Status != TaskStatus.Running) { logger.Error("TaskVoiceServerTransmit not running"); disconnectReason(DisconnectReasons.InternalLibraryError30); } stopWatch.Restart(); } Thread.Sleep(500); } logger.Debug(nameof(TaskServerConnectionCheck) + " stopped"); }
private static void TaskVoiceServerReceive( Logger logger, CancellationToken cancelToken, ClientConnectionData connection, UdpClient udpClient, BlockingCollection <IMsgPackTypeName> receiveQueue) { logger.Debug(nameof(TaskVoiceServerReceive) + " started"); try { IPEndPoint sender = new IPEndPoint(IPAddress.Any, 60005); byte[] data; while (!cancelToken.IsCancellationRequested) { data = udpClient.Receive(ref sender); //UDP is a datagram protocol, not a streaming protocol, so it's whole datagrams here. if (data.Length < 30 || data.Length > 1500) { continue; } //Could check that the sender has the right IP - but NAT-ing significantly reduces the attack surface here connection.VoiceServerBytesReceived += data.Length; var deserializer = CryptoDtoDeserializer.DeserializeIgnoreSequence(connection.VoiceCryptoChannel, data); if (!deserializer.IsSequenceValid()) { logger.Debug("Duplicate or old packet received"); continue; //If a duplicate packet received (because it's UDP) - ignore it } //Crypto DTO stream is only concerned with duplicated or old packets, it doesn't discard out-of-order. Need to find out if Opus discards OOO packets. var dtoName = deserializer.GetDtoName(); if (dtoName == "AR") { dtoName = RadioRxDto.TypeNameConst; //The server will be sending RadioRxDto as "AR" to maintain backwards compatibility for a year or two until old clients get updated. } switch (dtoName) { case RadioRxDto.TypeNameConst: { var dto = deserializer.GetDto <RadioRxDto>(); if (connection.ReceiveAudio && connection.IsConnected) { receiveQueue.Add(dto); } break; } case nameof(CallRequestDto): case ShortDtoNames.CallRequest: { var dto = deserializer.GetDto <CallRequestDto>(); if (connection.ReceiveAudio && connection.IsConnected) { receiveQueue.Add(dto); } break; } case nameof(CallResponseDto): case ShortDtoNames.CallResponse: { var dto = deserializer.GetDto <CallResponseDto>(); if (connection.ReceiveAudio && connection.IsConnected) { receiveQueue.Add(dto); } break; } case nameof(HeartbeatAckDto): case ShortDtoNames.HeartbeatAckDto: connection.LastVoiceServerHeartbeatAckUtc = DateTime.UtcNow; logger.Trace("Received voice server heartbeat"); break; } } } catch (SocketException sex) { if (connection.IsConnected) //If the socket exception occurs whilst disconnected, it's likely to just be a forced socket closure from doing disconnect(). { logger.Error(sex); } } catch (Exception ex) { logger.Error(ex); } logger.Debug(nameof(TaskVoiceServerReceive) + " stopped"); }
private static void TaskVoiceServerTransmit( Logger logger, CancellationToken cancelToken, ClientConnectionData connection, UdpClient udpClient, IPEndPoint server, BlockingCollection <IMsgPackTypeName> transmitQueue) { logger.Debug(nameof(TaskVoiceServerTransmit) + " started"); CryptoDtoSerializer serializer = new CryptoDtoSerializer(); try { byte[] dataBytes; while (!cancelToken.IsCancellationRequested) { if (transmitQueue.TryTake(out IMsgPackTypeName obj, 250, cancelToken)) { if (connection.IsConnected) { switch (obj.GetType().Name) { case nameof(RadioTxDto): if (logger.IsTraceEnabled) { logger.Trace(((RadioTxDto)obj).ToDebugString()); } dataBytes = serializer.Serialize(connection.VoiceCryptoChannel, CryptoDtoMode.ChaCha20Poly1305, (RadioTxDto)obj); udpClient.Send(dataBytes, dataBytes.Length, server); connection.VoiceServerBytesSent += dataBytes.Length; break; case nameof(CallRequestDto): if (logger.IsTraceEnabled) { logger.Trace("Sending CallRequestDto"); } dataBytes = serializer.Serialize(connection.VoiceCryptoChannel, CryptoDtoMode.ChaCha20Poly1305, (CallRequestDto)obj); udpClient.Send(dataBytes, dataBytes.Length, server); connection.VoiceServerBytesSent += dataBytes.Length; break; case nameof(CallResponseDto): if (logger.IsTraceEnabled) { logger.Trace("Sending CallResponseDto"); } dataBytes = serializer.Serialize(connection.VoiceCryptoChannel, CryptoDtoMode.ChaCha20Poly1305, (CallResponseDto)obj); udpClient.Send(dataBytes, dataBytes.Length, server); connection.VoiceServerBytesSent += dataBytes.Length; break; case nameof(HeartbeatDto): if (logger.IsTraceEnabled) { logger.Trace("Sending voice server heartbeat"); } dataBytes = serializer.Serialize(connection.VoiceCryptoChannel, CryptoDtoMode.ChaCha20Poly1305, (HeartbeatDto)obj); udpClient.Send(dataBytes, dataBytes.Length, server); connection.VoiceServerBytesSent += dataBytes.Length; break; } } } } } catch (OperationCanceledException) { } catch (Exception ex) { logger.Error(ex); } logger.Debug(nameof(TaskVoiceServerTransmit) + " stopped"); }