コード例 #1
0
 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");
 }
コード例 #2
0
        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);
        }
コード例 #3
0
        public ByteArray GetRate()
        {
            ByteArray sendData = Recycler.Grab();

            sendData.length = 4;
            WriteInt32(settings.connectionDownload, sendData.data, 0);
            return(GetUDPMessageData(10, 0, sendData));
        }
コード例 #4
0
        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);
            }
        }
コード例 #5
0
 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");
     }
 }
コード例 #6
0
        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);
        }
コード例 #7
0
        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);
            }
        }
コード例 #8
0
        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);
        }
コード例 #9
0
        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);
        }