Beispiel #1
0
        /// <summary>
        ///     Resend packets starting from seq if the corresponding bit in the mask
        ///     field is set.
        /// </summary>
        private void DoRetryOnSACKMask(Session sess, uint mask, byte seq)
        {
            lock (sess)
            {
                log.AddLog(LogType.DPLAY_MSG,
                           "s>c retry on sack mask client={0} seq={1:X} mask={2:X} user_data_pending_ack={3}",
                           sess.Client, seq, mask, sess.UserDataPendingAck.Count);

                string seqs = "user_data_pending_ack=[";
                foreach (byte key in sess.UserDataPendingAck.Keys)
                {
                    seqs += String.Format("{0:X} ", key);
                }
                seqs += "]";
                log.AddLog(LogType.DPLAY_MSG, seqs);

                byte resend_seq = seq;
                for (int i = 1; i != 0; i <<= 1, resend_seq++)
                {
                    if ((mask & i) == i)
                    {
                        if (sess.UserDataPendingAck.ContainsKey(resend_seq))
                        {
                            Session.Pkt pkt = sess.UserDataPendingAck[resend_seq];

                            pkt.RetryCount++;
                            if (pkt.RetryCount < 3)
                            {
                                pkt.RetryTime = DateTime.UtcNow.AddMilliseconds(200 + 50 * pkt.RetryCount);
                            }
                            else
                            {
                                pkt.RetryTime =
                                    DateTime.UtcNow.AddMilliseconds(200 + 50 * pkt.RetryCount * pkt.RetryCount);
                            }

                            pkt.SendTime = DateTime.UtcNow;

                            log.AddLog(LogType.DPLAY_MSG, "s>c send retry client={0} seq={1:X} retry_count={2}",
                                       sess.Client, pkt.Data[2], pkt.RetryCount);
                            pkt.Data[1]  |= 0x01;
                            pkt.Data[3]   = sess.NextRxSeq;
                            sess.BytesTx += pkt.Data.Length;
                            TxStart(pkt.Data, sess.Client);

                            sess.LostTx++;
                        }
                    }
                }
            }
        }
        /// <summary>
        ///     Try to send a dframe or a keep alive if no user data is waiting to be sent.
        /// </summary>
        /// <param name="sess"></param>
        /// <returns>Return true if a dframe was sent</returns>
        public bool SendDFrame(Session sess)
        {
            lock (sess)
            {
                if (sess.UserData.Count > 0)
                {
                    // If the window is full, don't send any thing
                    if (IsAckWindowFull(sess))
                    {
                        return(false);
                    }

                    // If we have sent a user data message that had to be carried in multiple
                    // dframes then stop sending. We can't have more than one of these on the
                    // wire at one time (if I've intepreted the specs correctly).
                    if (sess.MultipleDframePacket)
                    {
                        return(false);
                    }

                    // The retry time should start at 100 + rtt * 2.5 according to the specs but we use
                    // 2 as this is a round number.
                    uint retry_time = 100 + (sess.Rtt * 2);
                    while (sess.UserData.Count > 0)
                    {
                        byte[] ud = sess.UserData.First();
                        sess.UserData.RemoveFirst();

                        // Break the user data block into sizes smaller than the ethernet mtu. We
                        // assume an MTU of 1450 as some infrastructure steals some bytes.
                        int  offset       = 0;
                        bool first_packet = true;
                        bool last_packet  = false;
                        while (offset < ud.Length)
                        {
                            int length;
                            if ((ud.Length - offset) > 1450)
                            {
                                length = 1450;
                                sess.MultipleDframePacket = true;
                            }
                            else
                            {
                                length      = ud.Length - offset;
                                last_packet = true;
                            }

                            byte[] pkt = { 0x07, 0x00, sess.NextTxSeq, sess.NextRxSeq };

                            // If this is the first packet, set the flag to indicate this.
                            if (first_packet)
                            {
                                pkt[0] |= 0x10;
                            }

                            // If this is the last packet, set the flag to indicate this
                            if (last_packet)
                            {
                                pkt[0] |= 0x20;
                            }

                            // If the session isn't fully connected then this must be a session establishment
                            // message
                            if (sess.SessionState == Session.State.CONNECTING_SESSINFO)
                            {
                                pkt[0] |= 0x40;
                            }

                            FLMsgType.AddArray(ref pkt, ud, offset, length);

                            var spkt = new Session.Pkt();
                            spkt.Data      = pkt;
                            spkt.RetryTime = DateTime.UtcNow.AddMilliseconds(retry_time);
                            spkt.SendTime  = DateTime.UtcNow;

                            sess.UserDataPendingAck[sess.NextTxSeq] = spkt;
                            sess.BytesTx += pkt.Length;
                            TxStart(pkt, sess.Client);

                            // Increase the retry times if multiple packets are sent so that
                            // we're less likely to send a massive burst of packets to retry.
                            retry_time += 5;

                            sess.NextTxSeq++;

                            first_packet = false;
                            offset      += length;

                            // fixme: it's possible for a multi-dframe user data message to overrun
                            // the valid seq window size. this is bad and the connection will fail.
                        }

                        // If we have sent a user data message that had to be carried in multiple
                        // dframes then stop sending. We can't have more than one of these on the
                        // wire at one time (if I've intepreted the specs correctly).
                        if (sess.MultipleDframePacket)
                        {
                            break;
                        }

                        // If the window is full, don't send any more
                        if (IsAckWindowFull(sess))
                        {
                            break;
                        }
                    }

                    return(true);
                }
            }

            return(false);
        }