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); } }
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, globalLimit); Connection newConnection = new Connection(connectionID, settings, newClient, this, newBucket); newConnection.sendEndpoint = receiveAddress; connections[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 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); }