SRT协议控制包处理源码解读

SRT协议控制包处理源码解读

本文为个人在看协议栈时候的理解,不一定对,还望发现错误能够及时指正

ACK包

1. 如果ACK包长度为SEND_LITE_ACK(4)

该包在srt中被称为SEND LITE ACK,仅包含ACK的seq

取出当前收到ACK seq(ack),同已发送的ACK seq(m_iSndLastAck)比较,使用seqcmp函数比较,seqcmp比较方式如下

(abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1)

其中m_iSeqNoTH = 0x3FFFFFFF为最大seq,超过最大seq从1开始

若比较返回值值大于等于0,则作出以下修改

  1. 滑动窗口大小(m_iFlowWindowSize)

    重设为m_iFlowWindowSize - CSeqNo::seqoff(m_iSndLastAck, ack)

    其中seqoffcommon.h中定义的获取调整值的公用函数

  2. 上一个发送的ACK seq(m_iSndLastAck)调整,供下一次使用,m_iSndLastAck = ack

  3. 上一次回复ACK时间(m_ullLastRspAckTime_tk)调整为m_ullLastRspAckTime_tk = currtime_tk

  4. 重传包数量(m_iReXmitCount)重置,由于ACK seq已置为收到的ACK seq,所以无需重传,m_iReXmitCount = 1

处理结束

2. 普通ACK包

普通ACK包处理过程

  1. 判断是否需要回复ACKACK包,两个判断条件其中之一为真则进行如下处理
    1. 回复ACK的ACKACK包回复时间是否大于最小时间间隔(COMM_SYN_INTERVAL_US = 10000)(推测应该是为了防止每次都发送ACKACK,同ACK一段时间再回复的原理一致),大于该间隔则重发ACKACK
    2. 判断ACK是否同上次回复的ACKACK相同,如果相同,说明ACKACK对方没有收到,重发
    3. 重发做两件事情,第一是重置ACKACK seq(m_iSndLastAck2)为当前回复的ACK seq,第二则是记录该ACKACK包回复时间(m_ullSndLastAck2Time)为当前时间,方便下次判断
  2. 验证ACK包seq的有效性,无效包则进行如下处理
    1. 设置m_bBroken为真,连接会断开
    2. m_iBrokenCounter置0(作用暂时未知)
  3. 取出ACK seq,同上一次的ACK seq(m_iSndLastAck)比较,依旧是使用seqcmp函数比较
    1. 滑动窗口m_iFlowWindowSize设置为ackdata[ACKD_BUFFERLEFT]的值,推测是及时调整滑动窗口大小来达到限速的目的
    2. m_iSndLastAck调整为当前ACK seq的值
    3. 上一次回复ACK时间(m_ullLastRspAckTime_tk)调整为m_ullLastRspAckTime_tk = currtime_tk
    4. 重传包数量(m_iReXmitCount)重置,由于ACK seq已置为收到的ACK seq,所以无需重传,m_iReXmitCount = 1
  4. 判断ACK是否重复,重复则进行如下操作
    1. 直接丢弃当前包,不处理
  5. 处理至此,说明该ACK包前的序列为完整,则设置m_iSndLastFullAck = ack
  6. 判断上一个ACK seq和当前ACK seq有无偏移,偏移大于0则做如下处理
    1. 移除当前的ACK seq之前的数据,并且调整上一个ACK seq
    2. 对srt信息做记录,总发送时间等
    3. 将当前ACK seq的包从重发列表移除
  7. 告知所有等待的epoll更新其状态,开始写
  8. 将当前socket插入等待队列(如果socket没有在队列的话)
  9. 对控制包长度进行判断
    1. 如果包长不是int32_t长度的整数倍,截取到整数倍为止
    2. 如果小于最小ACK包长度,报错,丢弃
  10. 获取ACK包中的rtt,进行调整RTT方差和RTT
    1. m_iRTTVar = (m_iRTTVar * (4 - 1) + abs(rtt - m_iRTT)) / 4
    2. m_iRTT = (m_iRTT * (4 - 1) + rtt) / 4
  11. 判断包长度是否大于最小ACK包长度,如果这意味着ACKD_RCVSPEED和ACKD_BANDWIDTH字段是可用的
    1. m_iBandwidth = (m_iBandwidth * (8 - 1) + bandwidth) / 8
    2. m_iDeliveryRate = (m_iDeliveryRate * (8 - 1) + pktps) / 8
    3. m_iByteDeliveryRate = (m_iByteDeliveryRate * (8 - 1) + bytesps) / 8
  12. 更新srt的总的状态信息

ACKACK包

回复ACK的包

  1. 调整RTT方差

    m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2

  2. 调整RTT

    m_iRTT = (m_iRTT * 7 + rtt) >> 3

  3. 比较ACKACK和上一个收到的ACKACK,如果较新,则更新

LOSSREPORT包

只有该包会导致重传,用于报告重传的包

  1. 判断losslist的长度,losslist可能存在多个list,list可能是单个seq,也可能为一个range

  2. 循环处理单个seq或者一个seq range,判别依据为第一位是否设置,设置则为seq range

    1. seq range

      获取seq low和seq high,检查和上一个发送的ACK seq之间的差距,将差的部分添加至m_pSndLosslist,记录重发包数量

    2. 单个seq

      判断是否大于上一个ACK,大于才重发,插入m_pSndLosslist,记录重发包数量

    3. 更新srt的重发包数量

    4. 通知m_pSndUList立即更新,发送这些丢失的包

    5. 更新srt状态中NAK包的数量

CGWARNING包

延时增加的警告包,srt自身并不会发送该类型的包,可能由用户控制,预测ACKACK有一个地方可能会用到这种类型的包,但是被注释掉了

  1. 调整包间时间间隔m_ullInterval_tk = (uint64_t)ceil(m_ullInterval_tk * 1.125)
  2. 更新上一次减缓发送的seq序号m_iLastDecSeq = m_iSndCurrSeqNo

KEEPALIVE包

不做任何处理,仅是一个提示包,1.3.4版本此包内部无逻辑

HANDSHAKE

握手的包有好几种,分别为INDUCTIONCONCLUSIONWAVEAHANDAGREEMENT,其中INDUCTIONWAVEAHAND两种类型的包为第一部分握手发送的包,CONCLUSION为第二部分握手时候发送的包,AGREEMENT为额外的握手包

    // Note: the client-server connection uses:
    // --> INDUCTION (empty)
    // <-- INDUCTION (cookie)
    // --> CONCLUSION (cookie)
    // <-- CONCLUSION (ok)

    // The rendezvous HSv4 (legacy):
    // --> WAVEAHAND (effective only if peer is also connecting)
    // <-- CONCLUSION (empty) (consider yourself connected upon reception)
    // --> AGREEMENT (sent as a response for conclusion, requires no response)

    // The rendezvous HSv5 (using SRT extensions):
    // --> WAVEAHAND (with cookie)
    // --- (selecting INITIATOR/RESPONDER by cookie contest - comparing one another's cookie)
    // <-- CONCLUSION (without extensions, if RESPONDER, with extensions, if INITIATOR)
    // --> CONCLUSION (with response extensions, if RESPONDER)
    // <-- AGREEMENT (sent exclusively by INITIATOR upon reception of CONCLUSIOn with response extensions)
  1. 判断包为第一部分的包(INDUCTIONWAVEAHAND)或者rendezvous模式下的非AGREEMENT包,如果均不是,则说明包非HANDSHAKE,忽略

  2. 初始化ISNMSSsocket等,回复包类型设置为URQ_INDUCTIONURQ_AGREEMENT(依据连接方式决定)

  3. 判断版本为UDT4还是UDT5,将version更新到信息中

    如果版本为UDT5,除了设置版本之外,还将做额外的工作,判断解析内容,如果内容解析出现问题,则认为是非法的握手包,version置0,设置回复类型为SRT_REJ_ROGUE

  4. 初始化extension参数,表明回复中有额外内容,如果是reject的包则该值为false

  5. 组织response,设置回复包为handshake包,申请空间,填充信息,PeerID,时间戳等,并插入队列等待发送,更新上一个包发送时间为当前时间,解析完成

SHUTDOWN

收到SHUTDOWN包,连接将会断开

  1. 设置shutdownclosingbrokenbrokenCounter等参数,为关闭做准备
  2. 给发送者和接受者发送信号,如果他们处于等待数据的状态,其实就是个上锁做一些工作再释放的过程
  3. 更新事件,让事件队列清空
  4. 通知事件线程

DROPREQ

丢弃包消息,收到该包会将丢包重传等从丢包列表去除,不再重发

  1. 收到该消息,上锁,删除msg
  2. 清理loss list等,重置lossttl等

PEERERROR

用来告知peer端出现错误

  1. 更新PeerHealth为false

EXT

该包作为保留包和用户定义包

  1. 判断包内容是否能够解析,能够解析且只有包的extended type为SRT_CMD_HSREQSRT_CMD_HSRSP才认为包有效并进行操作

源码

源码位于core.cpp

void CUDT::processCtrl(CPacket& ctrlpkt)
{
   // Just heard from the peer, reset the expiration count.
   m_iEXPCount = 1;
   uint64_t currtime_tk;
   CTimer::rdtsc(currtime_tk);
   m_ullLastRspTime_tk = currtime_tk;
   bool using_rexmit_flag = m_bPeerRexmitFlag;

   HLOGC(mglog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " ("
       << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID);

   switch (ctrlpkt.getType())
   {
   case UMSG_ACK: //010 - Acknowledgement
      {
      int32_t ack;
      int32_t* ackdata = (int32_t*)ctrlpkt.m_pcData;

      // process a lite ACK
      if (ctrlpkt.getLength() == (size_t)SEND_LITE_ACK)
      {
         ack = *ackdata;
         if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0)
         {
            m_iFlowWindowSize -= CSeqNo::seqoff(m_iSndLastAck, ack);
            HLOGC(mglog.Debug, log << CONID() << "ACK covers: " << m_iSndLastDataAck << " - " << ack << " [ACK=" << m_iSndLastAck << "] (FLW: " << m_iFlowWindowSize << ") [LITE]");

            m_iSndLastAck = ack;
            m_ullLastRspAckTime_tk = currtime_tk;
            m_iReXmitCount = 1;       // Reset re-transmit count since last ACK
         }

         break;
      }

       // read ACK seq. no.
      ack = ctrlpkt.getAckSeqNo();

      // send ACK acknowledgement
      // number of ACK2 can be much less than number of ACK
      uint64_t now = CTimer::getTime();
      if ((now - m_ullSndLastAck2Time > (uint64_t)COMM_SYN_INTERVAL_US) || (ack == m_iSndLastAck2))
      {
         sendCtrl(UMSG_ACKACK, &ack);
         m_iSndLastAck2 = ack;
         m_ullSndLastAck2Time = now;
      }

      // Got data ACK
      ack = ackdata[ACKD_RCVLASTACK];

      // New code, with TLPKTDROP

      // protect packet retransmission
      CGuard::enterCS(m_AckLock);

      // check the validation of the ack
      if (CSeqNo::seqcmp(ack, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0)
      {
         CGuard::leaveCS(m_AckLock);
         //this should not happen: attack or bug
         LOGC(glog.Error, log << CONID() << "ATTACK/IPE: incoming ack seq " << ack << " exceeds current "
                 << m_iSndCurrSeqNo << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ack)-1) << "!");
         m_bBroken = true;
         m_iBrokenCounter = 0;
         break;
      }

      if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0)
      {
         // Update Flow Window Size, must update before and together with m_iSndLastAck
         m_iFlowWindowSize = ackdata[ACKD_BUFFERLEFT];
         m_iSndLastAck = ack;
         m_ullLastRspAckTime_tk = currtime_tk;
         m_iReXmitCount = 1;       // Reset re-transmit count since last ACK
      }

      /* 
      * We must not ignore full ack received by peer
      * if data has been artificially acked by late packet drop.
      * Therefore, a distinct ack state is used for received Ack (iSndLastFullAck)
      * and ack position in send buffer (m_iSndLastDataAck).
      * Otherwise, when severe congestion causing packet drops (and m_iSndLastDataAck update)
      * occures, we drop received acks (as duplicates) and do not update stats like RTT,
      * which may go crazy and stay there, preventing proper stream recovery.
      */

      if (CSeqNo::seqoff(m_iSndLastFullAck, ack) <= 0)
      {
         // discard it if it is a repeated ACK
         CGuard::leaveCS(m_AckLock);
         break;
      }
      m_iSndLastFullAck = ack;

      int offset = CSeqNo::seqoff(m_iSndLastDataAck, ack);
      // IF distance between m_iSndLastDataAck and ack is nonempty...
      if (offset > 0) {
          // acknowledge the sending buffer (remove data that predate 'ack')
          m_pSndBuffer->ackData(offset);

          const int64_t currtime = CTimer::getTime();
          // record total time used for sending
          CGuard::enterCS(m_StatsLock);
          m_stats.sndDuration += currtime - m_stats.sndDurationCounter;
          m_stats.m_sndDurationTotal += currtime - m_stats.sndDurationCounter;
          m_stats.sndDurationCounter = currtime;
          CGuard::leaveCS(m_StatsLock);

          HLOGC(mglog.Debug, log << CONID() << "ACK covers: " << m_iSndLastDataAck << " - " << ack
              << " [ACK=" << m_iSndLastAck << "] BUFr=" << m_iFlowWindowSize
              << " RTT=" << ackdata[ACKD_RTT] << " RTT*=" << ackdata[ACKD_RTTVAR]
              << " BW=" << ackdata[ACKD_BANDWIDTH] << " Vrec=" << ackdata[ACKD_RCVSPEED]);
          // update sending variables
          m_iSndLastDataAck = ack;

          // remove any loss that predates 'ack' (not to be considered loss anymore)
          m_pSndLossList->remove(CSeqNo::decseq(m_iSndLastDataAck));
      }

      CGuard::leaveCS(m_AckLock);
      if (m_bSynSending)
      {
          CGuard lk(m_SendBlockLock);
          pthread_cond_signal(&m_SendBlockCond);
      }

      // acknowledde any waiting epolls to write
      s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true);

      // insert this socket to snd list if it is not on the list yet
      m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE);

      size_t acksize = ctrlpkt.getLength(); // TEMPORARY VALUE FOR CHECKING
      bool wrongsize = 0 != (acksize % ACKD_FIELD_SIZE);
      acksize = acksize / ACKD_FIELD_SIZE;  // ACTUAL VALUE

      if ( wrongsize )
      {
          // Issue a log, but don't do anything but skipping the "odd" bytes from the payload.
          LOGC(mglog.Error, log << CONID() << "Received UMSG_ACK payload is not evened up to 4-byte based field size - cutting to " << acksize << " fields");
      }

      // Start with checking the base size.
      if ( acksize < ACKD_TOTAL_SIZE_SMALL )
      {
          LOGC(mglog.Error, log << CONID() << "Invalid ACK size " << acksize << " fields - less than minimum required!");
          // Ack is already interpreted, just skip further parts.
          break;
      }
      // This check covers fields up to ACKD_BUFFERLEFT.

      // Update RTT
      //m_iRTT = ackdata[ACKD_RTT];
      //m_iRTTVar = ackdata[ACKD_RTTVAR];
      // XXX These ^^^ commented-out were blocked in UDT;
      // the current RTT calculations are exactly the same as in UDT4.
      int rtt = ackdata[ACKD_RTT];

      m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT));
      m_iRTT = avg_iir<8>(m_iRTT, rtt);

      /* Version-dependent fields:
       * Original UDT (total size: ACKD_TOTAL_SIZE_SMALL):
       *   ACKD_RCVLASTACK
       *   ACKD_RTT
       *   ACKD_RTTVAR
       *   ACKD_BUFFERLEFT
       * Additional UDT fields, not always attached:
       *   ACKD_RCVSPEED
       *   ACKD_BANDWIDTH
       * SRT extension version 1.0.2 (bstats):
       *   ACKD_RCVRATE
       * SRT extension version 1.0.4:
       *   ACKD_XMRATE
       */

      if (acksize > ACKD_TOTAL_SIZE_SMALL)
      {
          // This means that ACKD_RCVSPEED and ACKD_BANDWIDTH fields are available.
          int pktps = ackdata[ACKD_RCVSPEED];
          int bandwidth = ackdata[ACKD_BANDWIDTH];
          int bytesps;

          /* SRT v1.0.2 Bytes-based stats: bandwidth (pcData[ACKD_XMRATE]) and delivery rate (pcData[ACKD_RCVRATE]) in bytes/sec instead of pkts/sec */
          /* SRT v1.0.3 Bytes-based stats: only delivery rate (pcData[ACKD_RCVRATE]) in bytes/sec instead of pkts/sec */
          if (acksize > ACKD_TOTAL_SIZE_UDTBASE)
              bytesps = ackdata[ACKD_RCVRATE];
          else
              bytesps = pktps * m_iMaxSRTPayloadSize;

          m_iBandwidth = avg_iir<8>(m_iBandwidth, bandwidth);
          m_iDeliveryRate = avg_iir<8>(m_iDeliveryRate, pktps);
          m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate, bytesps);
          // XXX not sure if ACKD_XMRATE is of any use. This is simply
          // calculated as ACKD_BANDWIDTH * m_iMaxSRTPayloadSize.

          // Update Estimated Bandwidth and packet delivery rate
          // m_iRcvRate = m_iDeliveryRate;
          // ^^ This has been removed because with the SrtCongestion class
          // instead of reading the m_iRcvRate local field this will read
          // cudt->deliveryRate() instead.
      }

      checkSndTimers(REGEN_KM);
      updateCC(TEV_ACK, ack);

      CGuard::enterCS(m_StatsLock);
      ++ m_stats.recvACK;
      ++ m_stats.recvACKTotal;
      CGuard::leaveCS(m_StatsLock);

      break;
      }

   case UMSG_ACKACK: //110 - Acknowledgement of Acknowledgement
      {
      int32_t ack = 0;
      int rtt = -1;

      // update RTT
      rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack);
      if (rtt <= 0)
      {
          LOGC(mglog.Error, log << "IPE: ACK node overwritten when acknowledging " <<
              ctrlpkt.getAckSeqNo() << " (ack extracted: " << ack << ")");
          break;
      }

      //if increasing delay detected...
      //   sendCtrl(UMSG_CGWARNING);

      // RTT EWMA
      m_iRTTVar = (m_iRTTVar * 3 + abs(rtt - m_iRTT)) >> 2;
      m_iRTT = (m_iRTT * 7 + rtt) >> 3;

      updateCC(TEV_ACKACK, ack);

      // This function will put a lock on m_RecvLock by itself, as needed.
      // It must be done inside because this function reads the current time
      // and if waiting for the lock has caused a delay, the time will be
      // inaccurate. Additionally it won't lock if TSBPD mode is off, and
      // won't update anything. Note that if you set TSBPD mode and use
      // srt_recvfile (which doesn't make any sense), you'll have e deadlock.
      m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock);

      // update last ACK that has been received by the sender
      if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0)
         m_iRcvLastAckAck = ack;

      break;
      }

   case UMSG_LOSSREPORT: //011 - Loss Report
      {
      int32_t* losslist = (int32_t *)(ctrlpkt.m_pcData);
      size_t losslist_len = ctrlpkt.getLength() / 4;
      updateCC(TEV_LOSSREPORT, EventVariant(losslist, losslist_len));

      bool secure = true;

      // protect packet retransmission
      CGuard::enterCS(m_AckLock);

      // decode loss list message and insert loss into the sender loss list
      for (int i = 0, n = (int)(ctrlpkt.getLength() / 4); i < n; ++ i)
      {
         if (IsSet(losslist[i], LOSSDATA_SEQNO_RANGE_FIRST))
         {
             // Then it's this is a <lo, hi> specification with HI in a consecutive cell.
            int32_t losslist_lo = SEQNO_VALUE::unwrap(losslist[i]);
            int32_t losslist_hi = losslist[i+1];
            // <lo, hi> specification means that the consecutive cell has been already interpreted.
            ++ i;

            HLOGF(mglog.Debug, "received UMSG_LOSSREPORT: %d-%d (%d packets)...",
                    losslist_lo, losslist_hi, CSeqNo::seqoff(losslist_lo, losslist_hi)+1);

            if ((CSeqNo::seqcmp(losslist_lo, losslist_hi) > 0) || (CSeqNo::seqcmp(losslist_hi, m_iSndCurrSeqNo) > 0))
            {
               // seq_a must not be greater than seq_b; seq_b must not be greater than the most recent sent seq
               secure = false;
               // XXX leaveCS: really necessary? 'break' will break the 'for' loop, not the 'switch' statement.
               // and the leaveCS is done again next to the 'for' loop end.
               CGuard::leaveCS(m_AckLock);
               break;
            }

            int num = 0;
            if (CSeqNo::seqcmp(losslist_lo, m_iSndLastAck) >= 0)
               num = m_pSndLossList->insert(losslist_lo, losslist_hi);
            else if (CSeqNo::seqcmp(losslist_hi, m_iSndLastAck) >= 0)
            {
                // This should be theoretically impossible because this would mean
                // that the received packet loss report informs about the loss that predates
                // the ACK sequence.
                // However, this can happen if the packet reordering has caused the earlier sent
                // LOSSREPORT will be delivered after later sent ACK. Whatever, ACK should be
                // more important, so simply drop the part that predates ACK.
               num = m_pSndLossList->insert(m_iSndLastAck, losslist_hi);
            }

            CGuard::enterCS(m_StatsLock);
            m_stats.traceSndLoss += num;
            m_stats.sndLossTotal += num;
            CGuard::leaveCS(m_StatsLock);

         }
         else if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0)
         {
            HLOGF(mglog.Debug, "received UMSG_LOSSREPORT: %d (1 packet)...", losslist[i]);

            if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0)
            {
               //seq_a must not be greater than the most recent sent seq
               secure = false;
               CGuard::leaveCS(m_AckLock);
               break;
            }

            int num = m_pSndLossList->insert(losslist[i], losslist[i]);

            CGuard::enterCS(m_StatsLock);
            m_stats.traceSndLoss += num;
            m_stats.sndLossTotal += num;
            CGuard::leaveCS(m_StatsLock);
         }
      }
      CGuard::leaveCS(m_AckLock);

      if (!secure)
      {
         HLOGF(mglog.Debug, "WARNING: out-of-band LOSSREPORT received; considered bug or attack");
         //this should not happen: attack or bug
         m_bBroken = true;
         m_iBrokenCounter = 0;
         break;
      }

      // the lost packet (retransmission) should be sent out immediately
      m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE);

      CGuard::enterCS(m_StatsLock);
      ++ m_stats.recvNAK;
      ++ m_stats.recvNAKTotal;
      CGuard::leaveCS(m_StatsLock);

      break;
      }

   case UMSG_CGWARNING: //100 - Delay Warning
      // One way packet delay is increasing, so decrease the sending rate
      m_ullInterval_tk = (uint64_t)ceil(m_ullInterval_tk * 1.125);
      m_iLastDecSeq = m_iSndCurrSeqNo;
      // XXX Note as interesting fact: this is only prepared for handling,
      // but nothing in the code is sending this message. Probably predicted
      // for a custom congctl. There's a predicted place to call it under
      // UMSG_ACKACK handling, but it's commented out.

      break;

   case UMSG_KEEPALIVE: //001 - Keep-alive
      // The only purpose of keep-alive packet is to tell that the peer is still alive
      // nothing needs to be done.

      break;

   case UMSG_HANDSHAKE: //000 - Handshake
      {
      CHandShake req;
      req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength());

      HLOGC(mglog.Debug, log << "processCtrl: got HS: " << req.show());

      if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...???
              || (m_bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION
      {
         // The peer side has not received the handshake message, so it keeps querying
         // resend the handshake packet

          // This condition embraces cases when:
          // - this is normal accept() and URQ_INDUCTION was received
          // - this is rendezvous accept() and there's coming any kind of URQ except AGREEMENT (should be RENDEZVOUS or CONCLUSION)
          // - this is any of URQ_ERROR_* - well...
         CHandShake initdata;
         initdata.m_iISN = m_iISN;
         initdata.m_iMSS = m_iMSS;
         initdata.m_iFlightFlagSize = m_iFlightFlagSize;

         // For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT.
         // For client-server we do URQ_INDUCTION --> URQ_CONCLUSION.
         initdata.m_iReqType = (!m_bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT;
         initdata.m_iID = m_SocketID;

         uint32_t kmdata[SRTDATA_MAXSIZE];
         size_t kmdatasize = SRTDATA_MAXSIZE;
         bool have_hsreq = false;
         if ( req.m_iVersion > HS_VERSION_UDT4 )
         {
             initdata.m_iVersion = HS_VERSION_SRT1; // if I remember correctly, this is induction/listener...
             int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
             if ( hs_flags != 0 ) // has SRT extensions
             {
                 HLOGC(mglog.Debug, log << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType) << " WITH SRT ext");
                 have_hsreq = interpretSrtHandshake(req, ctrlpkt, kmdata, &kmdatasize);
                 if ( !have_hsreq )
                 {
                     initdata.m_iVersion = 0;
                     m_RejectReason = SRT_REJ_ROGUE;
                     initdata.m_iReqType = URQFailure(m_RejectReason);
                 }
                 else
                 {
                     // Extensions are added only in case of CONCLUSION (not AGREEMENT).
                     // Actually what is expected here is that this may either process the
                     // belated-repeated handshake from a caller (and then it's CONCLUSION,
                     // and should be added with HSRSP/KMRSP), or it's a belated handshake
                     // of Rendezvous when it has already considered itself connected.
                     // Sanity check - according to the rules, there should be no such situation
                     if (m_bRendezvous && m_SrtHsSide == HSD_RESPONDER)
                     {
                         LOGC(mglog.Error, log << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in handshake phase.");
                     }

                     // The 'extension' flag will be set from this variable; set it to false
                     // in case when the AGREEMENT response is to be sent.
                     have_hsreq = initdata.m_iReqType == URQ_CONCLUSION;
                     HLOGC(mglog.Debug, log << "processCtrl/HS: processing ok, reqtype="
                             << RequestTypeStr(initdata.m_iReqType) << " kmdatasize=" << kmdatasize);
                 }
             }
             else
             {
                 HLOGC(mglog.Debug, log << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType));
             }
         }
         else
         {
             initdata.m_iVersion = HS_VERSION_UDT4;
         }

         initdata.m_extension = have_hsreq;

         HLOGC(mglog.Debug, log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : ""));

         // XXX here interpret SRT handshake extension
         CPacket response;
         response.setControl(UMSG_HANDSHAKE);
         response.allocate(m_iMaxSRTPayloadSize);

         // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE.
         // There is also no possible IPE condition in case of HSv4 - for this version it will always return true.
         if ( createSrtHandshake(Ref(response), Ref(initdata), SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize) )
         {
             response.m_iID = m_PeerID;
             response.m_iTimeStamp = int(CTimer::getTime() - m_stats.startTime);
             int nbsent = m_pSndQueue->sendto(m_pPeerAddr, response);
             if (nbsent)
             {
                 uint64_t currtime_tk;
                 CTimer::rdtsc(currtime_tk);
                 m_ullLastSndTime_tk = currtime_tk;
             }
         }

      }
      else
      {
          HLOGC(mglog.Debug, log << "processCtrl: ... not INDUCTION, not ERROR, not rendezvous - IGNORED.");
      }

      break;
      }

   case UMSG_SHUTDOWN: //101 - Shutdown
      m_bShutdown = true;
      m_bClosing = true;
      m_bBroken = true;
      m_iBrokenCounter = 60;

      // Signal the sender and recver if they are waiting for data.
      releaseSynch();
      // Unblock any call so they learn the connection_broken error
      s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_ERR, true);

      CTimer::triggerEvent();

      break;

   case UMSG_DROPREQ: //111 - Msg drop request
      CGuard::enterCS(m_RecvLock);
      m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag);
      CGuard::leaveCS(m_RecvLock);

      unlose(*(int32_t*)ctrlpkt.m_pcData, *(int32_t*)(ctrlpkt.m_pcData + 4));

      // move forward with current recv seq no.
      if ((CSeqNo::seqcmp(*(int32_t*)ctrlpkt.m_pcData, CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0)
         && (CSeqNo::seqcmp(*(int32_t*)(ctrlpkt.m_pcData + 4), m_iRcvCurrSeqNo) > 0))
      {
         m_iRcvCurrSeqNo = *(int32_t*)(ctrlpkt.m_pcData + 4);
      }

      break;

   case UMSG_PEERERROR: // 1000 - An error has happened to the peer side
      //int err_type = packet.getAddInfo();

      // currently only this error is signalled from the peer side
      // if recvfile() failes (e.g., due to disk fail), blcoked sendfile/send should return immediately
      // giving the app a chance to fix the issue

      m_bPeerHealth = false;

      break;

   case UMSG_EXT: //0x7FFF - reserved and user defined messages
      HLOGF(mglog.Debug, "CONTROL EXT MSG RECEIVED: %08X\n", ctrlpkt.getExtendedType());
      {
          // This has currently two roles in SRT:
          // - HSv4 (legacy) handshake
          // - refreshed KMX (initial KMX is done still in the HS process in HSv5)
          bool understood = processSrtMsg(&ctrlpkt);
          // CAREFUL HERE! This only means that this update comes from the UMSG_EXT
          // message received, REGARDLESS OF WHAT IT IS. This version doesn't mean
          // the handshake version, but the reason of calling this function.
          //
          // Fortunately, the only messages taken into account in this function
          // are HSREQ and HSRSP, which should *never* be interchanged when both
          // parties are HSv5.
          if ( understood )
          {
              updateAfterSrtHandshake(ctrlpkt.getExtendedType(), HS_VERSION_UDT4);
          }
          else
          {
              updateCC(TEV_CUSTOM, &ctrlpkt);
          }
      }
      break;

   default:
      break;
   }
}
发布了89 篇原创文章 · 获赞 96 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/Boring_Wednesday/article/details/102524848
今日推荐