private void ReceiveLoop() { try { while (connected) { int bytesRead = tcpClient.GetStream().Read(readBuffer, 0, readBuffer.Length); if (bytesRead > 0) { udpSendQueue.Enqueue(Recycler.FromBytes(readBuffer, bytesRead)); Interlocked.Add(ref queuedBytes, bytesRead); udpServer.SendEvent(); } else { Disconnect("Receive connection closed"); } } } catch (Exception e) { Disconnect($"Exception {e}"); } Disconnect("Receive loop exited for shutdown"); }
private ByteArray GetUDPMessageData(short type, short sequence, ByteArray payload) { ByteArray byteArray = Recycler.Grab(); byteArray.length = 16; //Write magic header, DTT2 byteArray.data[0] = 68; byteArray.data[1] = 84; byteArray.data[2] = 84; byteArray.data[3] = 50; WriteInt32(connectionID, byteArray.data, 4); WriteInt16(type, byteArray.data, 8); if (payload == null) { WriteInt16(0, byteArray.data, 10); } else { byteArray.length += payload.length; WriteInt16(payload.length, byteArray.data, 10); Array.Copy(payload.data, 0, byteArray.data, 16, payload.length); Recycler.Release(payload); } WriteInt16(sequence, byteArray.data, 12); WriteInt16(receiveSequence, byteArray.data, 14); return(byteArray); }
public ByteArray GetRate() { ByteArray sendData = Recycler.Grab(); sendData.length = 4; WriteInt32(settings.connectionDownload, sendData.data, 0); return(GetUDPMessageData(10, 0, sendData)); }
public void HandleUDPData(short messageType, short messageLength, short messageSequence, short messageACK, ByteArray payload, IPEndPoint receiveEndpoint) { //We're the client and now we have the connection to the server if (sendEndpoint == null) { sendEndpoint = receiveEndpoint; } sendAck = messageACK; disconnectTime = DateTime.UtcNow.Ticks + (TimeSpan.TicksPerSecond * 5); bool releaseData = true; if (messageType == 1) { if (messageSequence == receiveSequence) { releaseData = false; receiveSequence++; tcpSendQueue.Enqueue(payload); while (heldMessages.TryRemove(receiveSequence, out ByteArray heldData)) { Console.WriteLine($"Playback message sequence: {receiveSequence}, left: {heldMessages.Count}"); receiveSequence++; tcpSendQueue.Enqueue(heldData); } tcpSendEvent.Set(); } else { if (AckGreaterThan(messageSequence, receiveSequence)) { if (!heldMessages.ContainsKey(messageSequence)) { //A message from the future out of sequence heldMessages.TryAdd(messageSequence, payload); Console.WriteLine($"Store message sequence: {messageSequence}"); releaseData = false; } } } } if (payload != null && releaseData) { Recycler.Release(payload); } }
private void SendLoop() { try { while (connected) { tcpSendEvent.WaitOne(); while (tcpSendQueue.TryDequeue(out ByteArray sendBytes)) { tcpClient.GetStream().Write(sendBytes.data, 0, sendBytes.length); Recycler.Release(sendBytes); } } } catch { Disconnect("Sending connection closed"); } }
private void Process(byte[] data, int length, IPEndPoint receiveAddress) { if (length < 16) { //Console.WriteLine($"Rejecting short message from {receiveAddress}"); return; } if (data[0] != 68 || data[1] != 84 || data[2] != 84 || data[3] != 50) { //Console.WriteLine($"Rejecting non TCPTunnel traffic from {receiveAddress}"); return; } int connectionID = BitConverter.ToInt32(data, 4); short messageType = BitConverter.ToInt16(data, 8); short messageLength = BitConverter.ToInt16(data, 10); short messageSequence = BitConverter.ToInt16(data, 12); short messageACK = BitConverter.ToInt16(data, 14); if (BitConverter.IsLittleEndian) { connectionID = IPAddress.NetworkToHostOrder(connectionID); messageType = IPAddress.NetworkToHostOrder(messageType); messageLength = IPAddress.NetworkToHostOrder(messageLength); messageSequence = IPAddress.NetworkToHostOrder(messageSequence); messageACK = IPAddress.NetworkToHostOrder(messageACK); } if (messageLength != length - 16) { //Console.WriteLine($"Rejecting bad payload TCPTunnel traffic from {receiveAddress}"); return; } //Payload is limited to 500 bytes if (messageLength > 500) { //Console.WriteLine($"Broken payload from {connectionID}"); return; } if (!connections.ContainsKey(connectionID)) { //We're the server so we need to grab a new tcp connection for this client if (ConnectLocalTCPConnection != null) { TcpClient newClient = ConnectLocalTCPConnection(); if (newClient == null) { //Server down? //Console.WriteLine($"Unable to connect to {settings.tcpPort}, is the server down?"); return; } Bucket newBucket = new Bucket(settings.connectionUpload, settings.connectionUpload, null); Connection newConnection = new Connection(connectionID, settings, newClient, this, newBucket, statistics); newConnection.sendEndpoint = receiveAddress; connections.TryAdd(connectionID, newConnection); Console.WriteLine($"New connection {connectionID} from {receiveAddress} mapped to {newClient.Client.LocalEndPoint}"); } else { //We can't do anything client side if we don't have an existing TCP connection, ignore the messages //Console.WriteLine($"Unknown UDP connection {connectionID} in client mode"); return; } } Connection c = connections[connectionID]; ByteArray payload = null; if (messageLength > 0) { payload = Recycler.Grab(); Array.Copy(data, 16, payload.data, 0, messageLength); payload.length = messageLength; } c.HandleUDPData(messageType, messageLength, messageSequence, messageACK, payload, receiveAddress); }
public void HandleUDPData(short messageType, short messageLength, short messageSequence, short messageACK, ByteArray payload, IPEndPoint receiveEndpoint) { //We're the client and now we have the connection to the server if (sendEndpoint == null) { sendEndpoint = receiveEndpoint; } sendAck = messageACK; disconnectTime = DateTime.UtcNow.Ticks + (TimeSpan.TicksPerSecond * 5); bool releaseData = true; if (messageType == 1) { if (messageSequence == receiveSequence) { releaseData = false; receiveSequence++; statistics.receivedUniquePackets++; statistics.receivedUniqueBytes += payload.length; tcpSendQueue.Enqueue(payload); while (heldMessages.TryRemove(receiveSequence, out ByteArray heldData)) { receiveSequence++; statistics.receivedUniquePackets++; statistics.receivedUniqueBytes += payload.length; tcpSendQueue.Enqueue(heldData); } tcpSendEvent.Set(); } else { if (AckGreaterThan(messageSequence, receiveSequence)) { if (!heldMessages.ContainsKey(messageSequence)) { //A message from the future out of sequence heldMessages.TryAdd(messageSequence, payload); releaseData = false; } } } } if (messageType == 10) { if (payload.length == 4) { int remoteDownloadSpeed = BitConverter.ToInt32(payload.data, 0); if (BitConverter.IsLittleEndian) { remoteDownloadSpeed = IPAddress.NetworkToHostOrder(remoteDownloadSpeed); } remoteDownloadSpeed = remoteDownloadSpeed * 1024; uploadBucket.LimitRate(remoteDownloadSpeed, remoteDownloadSpeed); sendRateAcknowledge = true; } } if (messageType == 11) { receivedRateMessage = true; } if (payload != null && releaseData) { Recycler.Release(payload); } }
public bool GetUDPMessage(out ByteArray sendMessage) { sendMessage = null; //Not enough data to send a packet if (!uploadBucket.TestBytes(500)) { return(false); } long currentTime = DateTime.UtcNow.Ticks; //Let the other side know they can stop sending the rate setup message if (sendRateAcknowledge) { sendRateAcknowledge = false; sendMessage = GetRateAcknowledge(); return(true); } //Connection setup, prevents the other side from sending too fast if (!receivedRateMessage && currentTime > nextRateMessageTime) { nextRateMessageTime = currentTime + TimeSpan.TicksPerSecond; sendMessage = GetRate(); return(true); } //If we have more than 1 second of data to send, or our bucket is low, let's skip the double send double uploadMaxed = uploadBucket.bucketBytes / (double)uploadBucket.bucketMax; bool efficencyMode = queuedBytes > uploadBucket.bucketMax || uploadMaxed < 0.5d; //Retransmit half as often long efficencyOffset = 0; if (efficencyMode) { efficencyOffset = TimeSpan.TicksPerMillisecond * settings.retransmit; } OutgoingMessage om; //Send retransmits if (sendMessage == null && udpRetransmitSendQueue.TryPeek(out om)) { if (currentTime > om.sendTime + efficencyOffset) { bool skipping = true; while (skipping) { if (udpRetransmitSendQueue.TryDequeue(out om)) { //Don't send messages they have acknowledged if (AckGreaterThan(om.sequence, sendAck)) { //Send back to the retransmit queue om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); //Send sendMessage = om.data; break; } else { Recycler.Release(om.data); } } else { skipping = false; } } } } //Send double sends if (sendMessage == null) { bool skipping = true; while (skipping) { if (udpDoubleSendQueue.TryPeek(out om)) { //This isn't ready to transmit yet. if (om.sendTime > currentTime + efficencyOffset) { break; } } if (udpDoubleSendQueue.TryDequeue(out om)) { if (AckGreaterThan(om.sequence, sendAck)) { //Send to the retransmit queue om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); //Send sendMessage = om.data; break; } else { //Already ACK'd, skip it Recycler.Release(om.data); } } else { skipping = false; } } } //Take new waiting data and build a message if we have less than 10000 queue'd packets int ackDiff = sendSequence - sendAck; if (ackDiff < 0) { ackDiff = ackDiff + ushort.MaxValue; } if (sendMessage == null && ackDiff < 10000 && udpSendQueue.Count > 0) { ByteArray payload = Recycler.Grab(); ByteArray addMessage; //Grab upto 500 bytes while (udpSendQueue.TryPeek(out addMessage)) { if (payload.length + addMessage.length > 500) { break; } else { udpSendQueue.TryDequeue(out addMessage); Interlocked.Add(ref queuedBytes, -addMessage.length); Array.Copy(addMessage.data, 0, payload.data, payload.length, addMessage.length); payload.length += addMessage.length; Recycler.Release(addMessage); } } om = new OutgoingMessage(); om.sequence = sendSequence++; om.data = GetUDPMessageData(1, om.sequence, payload); //Send it to the correct retransmit queue if (!efficencyMode && settings.initialRetransmit > 0) { om.sendTime = currentTime + (settings.initialRetransmit * TimeSpan.TicksPerMillisecond); udpDoubleSendQueue.Enqueue(om); } else { om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); } //Send sendMessage = om.data; statistics.sentUniquePackets++; statistics.sentUniqueBytes += payload.length; } //Send heartbeats if (sendMessage == null && currentTime > sendTime) { sendMessage = GetHeartbeat(); } //Send if (sendMessage != null) { sendTime = currentTime + (TimeSpan.TicksPerMillisecond * 100); //Update ACK WriteInt16(receiveSequence - 1, sendMessage.data, 14); while (!uploadBucket.RequestBytes(sendMessage.length)) { Thread.Sleep(1); } return(true); } return(false); }
public bool GetUDPMessage(out ByteArray sendMessage) { sendMessage = null; //Not enough data to send a packet if (!uploadBucket.TestBytes(500)) { return(false); } long currentTime = DateTime.UtcNow.Ticks; //Send retransmits OutgoingMessage om; if (udpDoubleSendQueue.TryPeek(out om)) { if (currentTime > om.sendTime) { if (udpDoubleSendQueue.TryDequeue(out om)) { //Send to the retransmit queue om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); //Send sendMessage = om.data; } } } if (sendMessage == null && udpRetransmitSendQueue.TryPeek(out om)) { if (currentTime > om.sendTime) { bool skipping = true; while (skipping) { if (udpRetransmitSendQueue.TryDequeue(out om)) { //Don't send messages they have acknowledged if (AckGreaterThan(om.sequence, sendAck)) { //Send back to the retransmit queue om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); //Send sendMessage = om.data; break; } else { Recycler.Release(om.data); } } else { skipping = false; } } } } //Take new waiting data and build a message if we have less than 1000 queue'd packets int ackDiff = sendSequence - sendAck; if (ackDiff < 0) { ackDiff = ackDiff + ushort.MaxValue; } if (sendMessage == null && ackDiff < 1000 && udpSendQueue.Count > 0) { ByteArray payload = Recycler.Grab(); ByteArray addMessage; //Grab upto 500 bytes while (udpSendQueue.TryPeek(out addMessage)) { if (payload.length + addMessage.length > 500) { break; } else { udpSendQueue.TryDequeue(out addMessage); Array.Copy(addMessage.data, 0, payload.data, payload.length, addMessage.length); payload.length += addMessage.length; Recycler.Release(addMessage); } } om = new OutgoingMessage(); om.sequence = sendSequence++; om.data = GetUDPMessageData(1, om.sequence, payload); //Send it to the correct retransmit queue if (settings.initialRetransmit > 0) { om.sendTime = currentTime + (settings.initialRetransmit * TimeSpan.TicksPerMillisecond); udpDoubleSendQueue.Enqueue(om); } else { om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); } //Send sendMessage = om.data; } //Send heartbeats if (sendMessage == null && currentTime > sendTime) { sendMessage = GetHeartbeat(); } if (sendMessage != null) { sendTime = currentTime + (TimeSpan.TicksPerMillisecond * 100); //Update ACK WriteInt16(receiveSequence - 1, sendMessage.data, 14); while (!uploadBucket.RequestBytes(sendMessage.length)) { Thread.Sleep(1); } return(true); } return(false); }