Esempio n. 1
0
        /// <summary>
        /// Receives typical data packets before dispatching them for consumption by the rest of SteamKit
        /// </summary>
        /// <param name="packet">The packet.</param>
        private void ReceiveData(UdpPacket packet)
        {
            // Data packets are unexpected if a valid connection has not been established
            if ( state != (int)State.Connected && state != (int)State.Disconnecting )
                return;

            // If we receive a packet that we've already processed (e.g. it got resent due to a lost ack)
            // or that is already waiting to be processed, do nothing.
            if ( packet.Header.SeqThis <= inSeqHandled || inPackets.ContainsKey(packet.Header.SeqThis) )
                return;

            inPackets.Add(packet.Header.SeqThis, packet);

            while ( DispatchMessage() ) ;
        }
Esempio n. 2
0
        /// <summary>
        /// Receives the challenge and responds with a Connect request
        /// </summary>
        /// <param name="packet">The packet.</param>
        private void ReceiveChallenge(UdpPacket packet)
        {
            if ( Interlocked.CompareExchange( ref state, (int)State.ConnectSent, (int)State.ChallengeReqSent ) != (int)State.ChallengeReqSent )
                return;

            ChallengeData cr = new ChallengeData();
            cr.Deserialize(packet.Payload);

            ConnectData cd = new ConnectData();
            cd.ChallengeValue = cr.ChallengeValue ^ ConnectData.CHALLENGE_MASK;

            MemoryStream ms = new MemoryStream();
            cd.Serialize(ms);
            ms.Seek(0, SeekOrigin.Begin);

            SendSequenced(new UdpPacket(EUdpPacketType.Connect, ms));

            inSeqHandled = packet.Header.SeqThis;
        }
Esempio n. 3
0
        /// <summary>
        /// Receives the notification of an accepted connection and sets the connection id that will be used for the
        /// connection's duration.
        /// </summary>
        /// <param name="packet">The packet.</param>
        private void ReceiveAccept(UdpPacket packet)
        {
            if ( Interlocked.CompareExchange( ref state, (int)State.Connected, (int)State.ConnectSent ) != (int)State.ConnectSent )
                return;

            DebugLog.WriteLine("UdpConnection", "Connection established");
            remoteConnId = packet.Header.SourceConnID;
            inSeqHandled = packet.Header.SeqThis;

            OnConnected( EventArgs.Empty );
        }
Esempio n. 4
0
        /// <summary>
        /// Processes incoming packets, maintains connection consistency, and oversees outgoing packets.
        /// </summary>
        private void NetLoop(object param)
        {
            // Variables that will be used deeper in the function; locating them here avoids recreating
            // them since they don't need to be.
            var userRequestedDisconnect = false;
            EndPoint packetSender = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
            byte[] buf = new byte[2048];

            var epTask = param as Task<IPEndPoint>;
            try
            {
                if ( epTask != null )
                {
                    remoteEndPoint = epTask.Result;
                }
                else
                {
                    DebugLog.WriteLine("UdpConnection", "Invalid endpoint supplied for connection: {0}", param);
                }
                
            }
            catch ( AggregateException ae )
            {
                foreach ( var ex in ae.Flatten().InnerExceptions )
                {
                    DebugLog.WriteLine("UdpConnection", "Endpoint task threw exception: {0}", ex);
                }
            }

            if ( remoteEndPoint != null )
            {
                timeOut = DateTime.Now.AddSeconds(TIMEOUT_DELAY);
                nextResend = DateTime.Now.AddSeconds(RESEND_DELAY);

                if ( Interlocked.CompareExchange(ref state, (int)State.ChallengeReqSent, (int)State.Disconnected) != (int)State.Disconnected )
                {
                    state = (int)State.Disconnected;
                    userRequestedDisconnect = true;
                }
                else
                {
                    // Begin by sending off the challenge request
                    SendPacket(new UdpPacket(EUdpPacketType.ChallengeReq));
                }
            }

            while ( state != (int)State.Disconnected )
            {
                try
                {
                    // Wait up to 150ms for data, if none is found and the timeout is exceeded, we're done here.
                    if ( !sock.Poll(150000, SelectMode.SelectRead)
                        && DateTime.Now > timeOut )
                    {
                        DebugLog.WriteLine("UdpConnection", "Connection timed out");

                        state = (int)State.Disconnected;
                        break;
                    }

                    // By using a 10ms wait, we allow for multiple packets sent at the time to all be processed before moving on
                    // to processing output and therefore Acks (the more we process at the same time, the fewer acks we have to send)
                    while ( sock.Poll(10000, SelectMode.SelectRead) )
                    {
                        int length = sock.ReceiveFrom(buf, ref packetSender);

                        // Ignore packets that aren't sent by the server we're connected to.
                        if ( !packetSender.Equals(remoteEndPoint) )
                            continue;

                        // Data from the desired server was received; delay timeout
                        timeOut = DateTime.Now.AddSeconds(TIMEOUT_DELAY);

                        MemoryStream ms = new MemoryStream(buf, 0, length);
                        UdpPacket packet = new UdpPacket(ms);

                        ReceivePacket(packet);
                    }
                }
                catch (IOException ex)
                {
                    DebugLog.WriteLine("UdpConnection", "Exception occurred while reading packet: {0}", ex);

                    state = (int)State.Disconnected;
                    break;
                }
                catch ( SocketException e )
                {
                    DebugLog.WriteLine("UdpConnection", "Critical socket failure: " + e.ErrorCode);

                    state = (int)State.Disconnected;
                    break;
                }

                // Send or resend any sequenced packets; a call to ReceivePacket can set our state to disconnected
                // so don't send anything we have queued in that case
                if ( state != (int)State.Disconnected )
                    SendPendingMessages();

                // If we received data but had no data to send back, we need to manually Ack (usually tags along with
                // outgoing data); also acks disconnections
                if ( inSeq != inSeqAcked )
                    SendAck();

                // If a graceful shutdown has been requested, nothing in the outgoing queue is discarded.
                // Once it's empty, we exit, since the last packet was our disconnect notification.
                if ( state == (int)State.Disconnecting && outPackets.Count == 0 )
                {
                    DebugLog.WriteLine("UdpConnection", "Graceful disconnect completed");

                    state = (int)State.Disconnected;
                    userRequestedDisconnect = true;
                    break;
                }
            }

            DebugLog.WriteLine("UdpConnection", "Calling OnDisconnected");
            OnDisconnected( new DisconnectedEventArgs( userRequestedDisconnect ) );
        }
Esempio n. 5
0
        /// <summary>
        /// Receives the packet, performs all sanity checks and then passes it along as necessary.
        /// </summary>
        /// <param name="packet">The packet.</param>
        private void ReceivePacket(UdpPacket packet)
        {
            // Check for a malformed packet
            if ( !packet.IsValid )
                return;
            else if ( remoteConnId > 0 && packet.Header.SourceConnID != remoteConnId )
                return;

            DebugLog.WriteLine("UdpConnection", "<- Recv'd {0} Seq {1} Ack {2}; {3} bytes; Message: {4} bytes {5} packets",
                packet.Header.PacketType, packet.Header.SeqThis, packet.Header.SeqAck,
                packet.Header.PayloadSize, packet.Header.MsgSize, packet.Header.PacketsInMsg);

            // Throw away any duplicate messages we've already received, making sure to
            // re-ack it in case it got lost.
            if ( packet.Header.PacketType == EUdpPacketType.Data && packet.Header.SeqThis < inSeq )
            {
                SendAck();
                return;
            }

            // When we get a SeqAck, all packets with sequence numbers below that have been safely received by
            // the server; we are now free to remove our copies
            if ( outSeqAcked < packet.Header.SeqAck )
            {
                outSeqAcked = packet.Header.SeqAck;

                // outSeqSent can be less than this in a very rare case involving resent packets.
                if ( outSeqSent < outSeqAcked )
                    outSeqSent = outSeqAcked;

                outPackets.RemoveAll( x => x.Header.SeqThis <= outSeqAcked );
                nextResend = DateTime.Now.AddSeconds(RESEND_DELAY);
            }

            // inSeq should always be the latest value that we can ack, so advance it as far as is possible.
            if ( packet.Header.SeqThis == inSeq + 1 )
                do
                    inSeq++;
                while ( inPackets.ContainsKey(inSeq + 1) );

            switch ( packet.Header.PacketType )
            {
                case EUdpPacketType.Challenge:
                    ReceiveChallenge(packet);
                    break;

                case EUdpPacketType.Accept:
                    ReceiveAccept(packet);
                    break;

                case EUdpPacketType.Data:
                    ReceiveData(packet);
                    break;

                case EUdpPacketType.Disconnect:
                    DebugLog.WriteLine("UdpConnection", "Disconnected by server");
                    state = (int)State.Disconnected;
                    return;

                case EUdpPacketType.Datagram:
                    break;

                default:
                    DebugLog.WriteLine("UdpConnection", "Received unexpected packet type " + packet.Header.PacketType);
                    break;
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Sends the packets as one sequenced, reliable net message.
        /// </summary>
        /// <param name="packets">The packets that make up the single net message</param>
        private void SendSequenced(UdpPacket[] packets)
        {
            uint msgStart = outSeq;

            foreach ( UdpPacket packet in packets )
            {
                SendSequenced(packet);

                // Correct for any assumptions made for the single-packet case.
                packet.Header.PacketsInMsg = (uint) packets.Length;
                packet.Header.MsgStartSeq = msgStart;
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Sends a packet immediately.
        /// </summary>
        /// <param name="packet">The packet.</param>
        private void SendPacket(UdpPacket packet)
        {
            packet.Header.SourceConnID = sourceConnId;
            packet.Header.DestConnID = remoteConnId;
            packet.Header.SeqAck = inSeqAcked = inSeq;

            DebugLog.WriteLine("UdpConnection", "Sent -> {0} Seq {1} Ack {2}; {3} bytes; Message: {4} bytes {5} packets",
                packet.Header.PacketType, packet.Header.SeqThis, packet.Header.SeqAck,
                packet.Header.PayloadSize, packet.Header.MsgSize, packet.Header.PacketsInMsg);

            byte[] data = packet.GetData();

            try
            {
                sock.SendTo(data, remoteEndPoint);
            }
            catch ( SocketException e )
            {
                DebugLog.WriteLine("UdpConnection", "Critical socket failure: " + e.ErrorCode);

                state = (int)State.Disconnected;
                return;
            }

            // If we've been idle but completely acked for more than two seconds, the next sent
            // packet will trip the resend detection. This fixes that.
            if ( outSeqSent == outSeqAcked )
                nextResend = DateTime.Now.AddSeconds(RESEND_DELAY);

            // Sending should generally carry on from the packet most recently sent, even if it was a
            // resend (who knows what else was lost).
            if ( packet.Header.SeqThis > 0 )
                outSeqSent = packet.Header.SeqThis;
        }
Esempio n. 8
0
        /// <summary>
        /// Sends the packet as a sequenced, reliable packet.
        /// </summary>
        /// <param name="packet">The packet.</param>
        private void SendSequenced(UdpPacket packet)
        {
            packet.Header.SeqThis = outSeq;
            packet.Header.MsgStartSeq = outSeq;
            packet.Header.PacketsInMsg = 1;

            outPackets.Add(packet);

            outSeq++;
        }
Esempio n. 9
0
        /// <summary>
        /// Sends the data sequenced as a single message, splitting it into multiple parts if necessary.
        /// </summary>
        /// <param name="ms">The data to send.</param>
        private void SendData( MemoryStream ms )
        {
            UdpPacket[] packets = new UdpPacket[ ( ms.Length / UdpPacket.MAX_PAYLOAD ) + 1 ];

            for ( int i = 0 ; i < packets.Length ; i++ )
            {
                long index = i * UdpPacket.MAX_PAYLOAD;
                long length = Math.Min( UdpPacket.MAX_PAYLOAD, ms.Length - index );

                packets[ i ] = new UdpPacket( EUdpPacketType.Data, ms, length );
                packets[ i ].Header.MsgSize = ( uint )ms.Length;
            }

            SendSequenced( packets );
        }
        /// <summary>
        /// Receives the notification of an accepted connection and sets the connection id that will be used for the
        /// connection's duration.
        /// </summary>
        /// <param name="packet">The packet.</param>
        private void ReceiveAccept(UdpPacket packet)
        {
            if ( state != State.ConnectSent )
                return;

            DebugLog.WriteLine("UdpConnection", "Connection established");

            state = State.Connected;
            remoteConnId = packet.Header.SourceConnID;
            inSeqHandled = packet.Header.SeqThis;

            OnConnected( EventArgs.Empty );
        }
Esempio n. 11
0
        /// <summary>
        /// Receives the packet, performs all sanity checks and then passes it along as necessary.
        /// </summary>
        /// <param name="packet">The packet.</param>
        private void ReceivePacket(UdpPacket packet)
        {
            // Check for a malformed packet
            if (!packet.IsValid)
                return;
            else if (remoteConnId > 0 && packet.Header.SourceConnID != remoteConnId)
                return;

            DebugLog.WriteLine("UdpConnection", "<- Recv'd {0} Seq {1} Ack {2}; {3} bytes; Message: {4} bytes {5} packets",
                packet.Header.PacketType, packet.Header.SeqThis, packet.Header.SeqAck,
                packet.Header.PayloadSize, packet.Header.MsgSize, packet.Header.PacketsInMsg);

            // Throw away any duplicate messages we've already received, making sure to
            // re-ack it in case it got lost.
            if (packet.Header.PacketType == EUdpPacketType.Data && packet.Header.SeqThis < inSeq)
            {
                SendAck();
                return;
            }

            // When we get a SeqAck, all packets with sequence numbers below that have been safely received by
            // the server; we are now free to remove our copies
            if (outSeqAcked < packet.Header.SeqAck)
            {
                outSeqAcked = packet.Header.SeqAck;

                // outSeqSent can be less than this in a very rare case involving resent packets.
                if (outSeqSent < outSeqAcked)
                    outSeqSent = outSeqAcked;

                outPackets.RemoveAll(x => x.Header.SeqThis <= outSeqAcked);
                nextResend = DateTime.Now.AddSeconds(RESEND_DELAY);
            }

            // inSeq should always be the latest value that we can ack, so advance it as far as is possible.
            if (packet.Header.SeqThis == inSeq + 1)
                do
                    inSeq++;
                while (inPackets.ContainsKey(inSeq + 1));

            switch (packet.Header.PacketType)
            {
                case EUdpPacketType.Challenge:
                    ReceiveChallenge(packet);
                    break;

                case EUdpPacketType.Accept:
                    ReceiveAccept(packet);
                    break;

                case EUdpPacketType.Data:
                    ReceiveData(packet);
                    break;

                case EUdpPacketType.Disconnect:
                    DebugLog.WriteLine("UdpConnection", "Disconnected by server");
                    state = State.Disconnected;
                    return;

                case EUdpPacketType.Datagram:
                    break;

                default:
                    DebugLog.WriteLine("UdpConnection", "Received unexpected packet type " + packet.Header.PacketType);
                    break;
            }
        }
Esempio n. 12
0
        /// <summary>
        /// Processes incoming packets, maintains connection consistency, and oversees outgoing packets.
        /// </summary>
        private void NetLoop()
        {
            // Variables that will be used deeper in the function; locating them here avoids recreating
            // them since they don't need to be.
            EndPoint packetSender = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
            byte[] buf = new byte[2048];

            timeOut = DateTime.Now.AddSeconds(TIMEOUT_DELAY);
            nextResend = DateTime.Now.AddSeconds(RESEND_DELAY);

            // Begin by sending off the challenge request
            SendPacket(new UdpPacket(EUdpPacketType.ChallengeReq));
            state = State.ChallengeReqSent;

            while (state != State.Disconnected)
            {
                try
                {
                    // Wait up to 150ms for data, if none is found and the timeout is exceeded, we're done here.
                    if (!sock.Poll(150000, SelectMode.SelectRead)
                        && DateTime.Now > timeOut)
                    {
                        DebugLog.WriteLine("UdpConnection", "Connection timed out");

                        state = State.Disconnected;
                        break;
                    }

                    // By using a 10ms wait, we allow for multiple packets sent at the time to all be processed before moving on
                    // to processing output and therefore Acks (the more we process at the same time, the fewer acks we have to send)
                    while (sock.Poll(10000, SelectMode.SelectRead))
                    {
                        int length = sock.ReceiveFrom(buf, ref packetSender);

                        // Ignore packets that aren't sent by the server we're connected to.
                        if (!packetSender.Equals(remoteEndPoint))
                            continue;

                        // Data from the desired server was received; delay timeout
                        timeOut = DateTime.Now.AddSeconds(TIMEOUT_DELAY);

                        MemoryStream ms = new MemoryStream(buf, 0, length);
                        UdpPacket packet = new UdpPacket(ms);

                        ReceivePacket(packet);
                    }
                }
                catch (SocketException e)
                {
                    DebugLog.WriteLine("UdpConnection", "Critical socket failure: " + e.ErrorCode);

                    state = State.Disconnected;
                    break;
                }

                // Send or resend any sequenced packets; a call to ReceivePacket can set our state to disconnected
                // so don't send anything we have queued in that case
                if (state != State.Disconnected)
                    SendPendingMessages();

                // If we received data but had no data to send back, we need to manually Ack (usually tags along with
                // outgoing data); also acks disconnections
                if (inSeq != inSeqAcked)
                    SendAck();

                // If a graceful shutdown has been requested, nothing in the outgoing queue is discarded.
                // Once it's empty, we exit, since the last packet was our disconnect notification.
                if (state == State.Disconnecting && outPackets.Count == 0)
                {
                    DebugLog.WriteLine("UdpConnection", "Graceful disconnect completed");

                    state = State.Disconnected;
                }
            }

            DebugLog.WriteLine("UdpConnection", "Calling OnDisconnected");
            OnDisconnected(EventArgs.Empty);
        }