重传程序

主要socket和线程

如果不走tun的时候会启下面两个socket 接收源数据

g_Send.m_sRecvSocket  	// 源数据 接收socket
	g_usUdpRecvPort
	g_uiUdpRecvIp

g_Send.m_sRecvSocket2	// 源数据 接收socket
	g_uiUdpRecvIp
	g_usUdpRecvPort2
	



g_Send.m_sPkgSendSocket // 重传数据包 发送socket	
	g_uiPkgSendIp
	g_usPkgSendPort

g_Send.m_sAckRecvSocket // 重传包接收应答socket
	g_uiPkgSendIp
	g_usAckRecvPort	



g_Recv.m_sRecvSocket	// 重传数据包 接收socket
	g_uiPkgRecvIp
	g_usPkgRecvPort

g_Recv.m_sAckSendSocket	// 重传数据包 应答发送socket
	g_uiAckSendIp
	g_usAckSendPort

如果开启 SEND_BY_RAW
	static SOCKET m_hSocket
		IPPROTO_UDP
		g_strUdpSendIp
		g_usUdpSendPort
否则
	g_Recv.m_sSendSocket
		g_uiUdpSendIp
		g_usUdpSendPort

pthread_t 

tidRecv:pthread_create( &tidRecv, 0, ThreadRecverProcessRecv, E_RECV_RECV );
		
			  for ( UCHAR i = 0; i < g_Recv.m_ucSendNum; i++ ){
tidRevSen     :	pthread_create( &tidRevSen, 0, ThreadRecverProcessSend, (LPVOID)i);
			  }
					
			  for ( UCHAR i = 0; i < g_ucRetryNum; i++ ){
tidSendURetry :	pthread_create( &tidSendURetry, 0, ThreadSenderProcessRetry, i);
			  }
					
				if(g_ucRecvByTun == 0){
tidSendRecv   :	pthread_create(&tidSendRecv,0,ThreadSenderPorcessRecv, E_SEND_RECV);
				}
tidACKTimer   :pthread_create(&tidACKTimer	,0,	AckTimerCB,NULL);
tidSenAck     :pthread_create(&tidSenAck	,0,	ThreadSenderPorcessAck,E_SEND_ACK);
tidStaticTimer:pthread_create(&tidStaticTimer,0,	StaticTimerCB			,NULL );
tidHeartTimer :pthread_create(&tidHeartTimer,0,HeartbeatCheckCB		,E_SEND_RECV);
tidStaticSec  :pthread_create(&tidStaticSec,0,	StaticSec,NULL );

tun数据接收

配置文件中g_ucRecvByTun或者g_ucSendByTun开启,会启动tun读取线程:

pthread_create(&tid1,NULL,do_read_fun,&g_index)

do_read_fun函数中进行tun数据的读取,读取出来的数据会放到g_Send.m_pPkgBuf里面。

g_Send.m_pPkgBuf的结构如下:

在do_read_fun函数中会把读取到的数据g_buf[index-1]传入g_readdatafunc函数,即SenderRecv函数

int nread = read(g_fd[index-1],g_buf[index-1],ETH_FRAME_LEN);
if (g_readdatafunc[index-1] != NULL){
	g_readdatafunc[index-1](g_buf[index-1],nread,index);
}

注意:上面的在read函数中传入的缓存区g_buf[index-1]中,而g_buf[index-1]里面存放的缓存区如下:

//设置接收缓存
void SenderSetRecvBuffer(void)
{
	g_uiAddr = g_Send.m_cLastPkgInfo[E_SEND_RECV].GetAddr();
	g_uiAddr++;
	if(g_uiAddr > g_uiSendPkgNum)
	{
		g_uiAddr = 1;
	}
	/*
    * 从这里可以看出:g_buf[index-1] = g_Send.m_pPkgBuf[0].m_abyData
    *			  					g_Send.m_pPkgBuf[1].m_abyData
    *								……
    *								g_Send.m_pPkgBuf[g_uiSendPkgNum].m_abyData
    * 这里面的索引值就是 g_uiAddr 该值的来源为g_Send.m_cLastPkgInfo[E_SEND_RECV].GetAddr()
    * 把接收tun上数据发送出去后,更新该值,设置下个可用接收缓存区到g_buf[index-1]中,这个点后面会讲
    */
	SendPkg &tPkg = *( g_Send.m_pPkgBuf + g_uiAddr - 1 ); // 定位、地址偏移
	
	setbuff(tPkg.m_abyData,1);
	{
		g_buf[index-1] = buff;
	}
}

有了上面的知识,我们接着分析,SenderRecv函数

tun上数据发送到对端准备

void SenderRecv(unsigned char* buff,int recvlen,int index)
{
	if ( g_bServiceRun )
	{
		// 发送端接收数据写入缓存, 并发往接收端
		UNIT_ADDR tFrom, tTo;
		tFrom.uiAddr = INADDR_ANY;
		tFrom.usPort = 0;
		tTo.uiAddr = INADDR_ANY;
		tTo.usPort = g_usUdpRecvPort;
		/*
		* 这就是上面在tun read中 已经把接收的数据放入到tPkg.m_abyData中
		*/
		SendPkg &tPkg = *( g_Send.m_pPkgBuf + g_uiAddr - 1 );

        /*
        * 该函数仅仅是填充 重传包 头信息 即填充 m_tHead
        */
		tPkg.PutData(buff, recvlen, tFrom, tTo);
        {
            m_tHead.usDataLen = usLen;
            ULONGLONG timep = ULLGetLocalTime();

            m_tHead.ullRecvTime = timep;
            m_tHead.uiSourceAddr = tFrom.uiAddr;
            m_tHead.usSourcePort = tFrom.usPort;
            m_tHead.uiTargetAddr = tTo.uiAddr;
            m_tHead.usTargetPort = tTo.usPort;
            m_tHead.ucRetryFlag = 0;
            m_tHead.ucCrc=
                CRC8((BYTE*)&m_tHead,sizeof(m_tHead)-sizeof(m_tHead.ucCrc));
        }
		g_Send.m_cLastPkgInfo[E_SEND_RECV].SetInfo(g_uiAddr); // 位置更新

        // g_ucRetryNum 存放的重传次数,一般我们设置为3次
		if ( g_ucRetryNum )
		{
			// 仅当重传检查空闲时,设置各线程开始位置 该部分单独分析
			UINT uiNextAddr = g_Send.m_cLastPkgInfo[ 0 ].GetAddr();
			if ( !uiNextAddr )
			{
				printf( "[%u] Starting retry check.\n", E_SEND_RECV );

				for ( UCHAR i = 0; i < g_ucRetryNum; i++ )
				{
					g_Send.m_cLastPkgInfo[i].SetInfo( g_uiAddr );
				}
			}
		}

		if ((g_lSendMaxLen == 0) || (g_lSenderSend < g_lSendMaxLen))
		{
            // 该函数是把数据发送到对端 后面分析
			g_Send.SendToRecv(g_uiAddr, tPkg); 
			g_lSenderSend += recvlen  + sizeof(PKG_HEAD);
		}
		// 设置下个可用的接收缓存
		SenderSetRecvBuffer();
	}	
}

上面反复提到g_Send.m_cLastPkgInfo[0 ].GetAddr()函数,这里介绍下:

enum E_GROUP_TYPE
{
    E_SEND_RECV = MAX_RETRY_NUM,                   // 发送端源数据接收线程 MAX_RETRY_NUM=5
    E_SEND_ACK,                                     // 发送端分包应答接收线程
    E_SEND_MAX,

    E_RECV_RECV,                                    // 接收端接收线程
    E_RECV_SEND,                                    // 接收端发送线程
};

接上面代码,假如我们设置重传次数为3 即g_ucRetryNum=3

if ( g_ucRetryNum )
	{
        UINT uiNextAddr = g_Send.m_cLastPkgInfo[ 0 ].GetAddr();
        if ( !uiNextAddr )
        {
            printf( "[%u] Starting retry check.\n", E_SEND_RECV );

            for ( UCHAR i = 0; i < g_ucRetryNum; i++ )
            {
                g_Send.m_cLastPkgInfo[i].SetInfo( g_uiAddr );
            }
        }
	}

如果uiNextAddr=0(ps:刚开始就是0)执行完上面代码后就如下

如果uiNextAddr=1 执行完上面代码后就如下

所以综上所述:

  1. tun上接收数据前(假设第一次),会首先调用一次SenderSetRecvBuffer函数

    g_uiAddr = g_Send.m_cLastPkgInfo[E_SEND_RECV].GetAddr();  //g_uiAddr=0
    g_uiAddr++
    SendPkg &tPkg = *( g_Send.m_pPkgBuf + g_uiAddr - 1 ); // 取 g_Send.m_pPkgBuf[0]
    
    g_buf[index-1] = tPkg.m_abyData;
    //此时g_uiAddr=1
    

    目的是把g_buf[index-1]=g_Send.m_pPkgBuf[0].m_abyData

  2. tun 上read函数,把数据从内核态 copy到 g_buf[index-1]中,即g_Send.m_pPkgBuf[0].m_abyData

  3. 然后调用SenderRecv函数:

    • 填充重传数据包头信息 m_tHead

    • 设置g_Send.m_cLastPkgInfo[E_SEND_RECV].SetInfo(g_uiAddr)

      g_Send.m_cLastPkgInfo[5].SetInfo(1)

    • 更新下面的:

      uiNextAddr = g_Send.m_cLastPkgInfo[ 0 ].GetAddr() // 此时肯定为0
      

      执行完

  4. 然后调用g_Send.SendToRecv(g_uiAddr, tPkg);函数把数据发送到对端

  5. 然后再次调用SenderSetRecvBuffer()函数,

    g_uiAddr = g_Send.m_cLastPkgInfo[E_SEND_RECV].GetAddr();  //g_uiAddr=1
    g_uiAddr++
    SendPkg &tPkg = *( g_Send.m_pPkgBuf + g_uiAddr - 1 ); //  g_Send.m_pPkgBuf[1]
    
    g_buf[index-1] = tPkg.m_abyData;
    //此时g_uiAddr=2
    
  6. 如此反复循环

tun上数据发送到对端

下面主要分析g_Send.SendToRecv(g_uiAddr, tPkg)函数

void SendCtrl::SendToRecv( UINT uiAddr, SendPkg &tPkg )
{
    // 准备将数据发往接收端

    // 发送端统计
    g_ulSenderTotal++;
    g_ulSenderRecvCount++;


    BOOL bSend = TRUE;

    if ( g_ucLostTest )
    {
        if ( g_usLostCount >= g_usLostGroup )
        {
            SetupLost();
        }

        g_usLostCount++;
        USHORT usIndex = g_usLostCount - 1;

        if (g_abLost[ usIndex ] )
        {
            bSend = FALSE;
        }
    }
    
    SOCKADDR_IN to;
    
    if( SEND_TRANS_MODE_ZUBO != g_ucSendTransMode )
    {
        
        // 遍历接收端发送分包

        PUNIT_ADDR pRecvAddr = m_pRecvAddrSet;
        PUNIT_HEART pUnitHeart = m_pHeartSet;
        UCHAR* pucAckFlag = tPkg.m_pucAckFlag;
        // SOCKADDR_IN to;
		/* 根据 m_ucRecvNum 数量 把数据 分发到不同对端
        * 这里存在三个变量
        * m_pRecvAddrSet: 解析RECV01里面的数据得到
        * m_pucAckFlag  : 解析RECV01里面的数据得到
        * m_pHeartSet   : 
        * :
        */
        for ( UCHAR i = 1; i <= m_ucRecvNum;
             i++, pRecvAddr++, pUnitHeart++, pucAckFlag++ )
        {
            UNIT_ADDR& tRecvAddr = *pRecvAddr;
            UNIT_HEART& tUnitHeart = *pUnitHeart;
            UCHAR& tucAckFlag = *pucAckFlag;

            tPkg.Lock();
            tucAckFlag = FALSE; // 默认为FALSE 指示该包是否需要重传的
            tPkg.Unlock();

            //printf( "sendto -> %u : %u.\n", tRecvAddr.uiAddr, tRecvAddr.usPort );

            SETSOCKADDR( to, tRecvAddr.uiAddr, tRecvAddr.usPort );

            if ( bSend && tUnitHeart.bUnitIsLive )
            {
                tPkg.SendPkgToLock( m_sPkgSendSocket, to );
            }
        }
    }
    else
    {
        // 这里是主播 先不考虑 遍历接收端发送分包

        UCHAR* pucAckFlag = tPkg.m_pucAckFlag;
        // SOCKADDR_IN to;

        for ( UCHAR i = 1; i <= m_ucRecvNum; i++, pucAckFlag++ )
        {
            UCHAR& tucAckFlag = *pucAckFlag;

            tPkg.Lock();
            tucAckFlag = FALSE;
            tPkg.Unlock();
        }

        //printf( "sendto -> %u : %u.\n", g_uiSendTransMcIp, g_usSendTransMcPort );

        SETSOCKADDR( to, g_uiSendTransMcIp, g_usSendTransMcPort );

        tPkg.SendPkgToLock( m_sPkgSendSocket, to );
    }                 
}

上面提到的m_pRecvAddrSetm_pucAckFlagm_pHeartSet解析如下:


// 数据目标地址定义
typedef struct tagUnitAddr
{
    UINT                uiAddr;                     // 地址
    USHORT              usPort;                     // 端口
} UNIT_ADDR, *PUNIT_ADDR;

// 目标心跳定义
typedef struct tagUnitHeart
{
    UINT                uiLastHeartTime;            // 最后心跳时间
    BOOL                bUnitIsLive;                // 心跳激活标志
} UNIT_HEART, *PUNIT_HEART;

// 下面是重要的数据地址保存,针对多连接
g_Send.m_pRecvAddrSet 	= new UNIT_ADDR[ g_Send.m_ucRecvNum ];
g_Send.m_pHeartSet 		= new UNIT_HEART[ g_Send.m_ucRecvNum ];

RECV01格式如下:
RECV01 = 对端ip:PKG_RECV_PORT:ACK_SEND_PORT

RECV_NUM记录有多少个重传单元,每个重传单元从RECV01开始,一直到RECV_NUM数量,举例如下
RECV_NUM = 2
RECV01 = 10.129.43.7:24602:24603
RECV02 = 10.129.43.7:24602:24603

g_Send.m_pRecvAddrSet[0]-------》解析 RECV01 得到 {.uiAddr=对端ip .usPort=PKG_RECV_PORT}
	   m_pRecvAddrSet[1]-------》解析 RECV02 得到 {.uiAddr=对端ip .usPort=PKG_RECV_PORT}
	   m_pRecvAddrSet[……]------》解析 RECV…… 得到 {.uiAddr=对端ip .usPort=PKG_RECV_PORT}
	   m_pRecvAddrSet[RECV_NUM]》解析 RECV0n 得到 {.uiAddr=对端ip .usPort=PKG_RECV_PORT}


  1. tPkg.m_pucAckFlag

    一个数据包中就会有RECV_NUM个,代表着多个接收端

  2. g_Send.m_pRecvAddrSet

    解析下面的数据,

    # 接收端数目(范围1-100,默认1)
    RECV_NUM = 1
    
    # 接收端信息:目标地址,分包接收端口,应答发送端口(默认127.0.0.1:20002:20003)
    RECV01 = 10.129.43.7:24602:24603
    

    当接收tun上数据后,会遍历该地址进行 分别发送

  3. g_Send.m_pHeartSet

对端接收处理

重传数据包接收线程ThreadRecverProcessRecv,对应的ip和端口如下:

接收过程如下:

  1. 首先获取一个缓存区

     PkgUnit& tPkgUnit = *( g_Recv.m_pPkgUnitBuf + g_Recv.m_uiPkgUnitPutAddrNum - 1 );
    

    默认初始化g_Recv.m_uiPkgUnitPutAddrNum=1的。所以这里相当于

    tPkgUnit = g_Recv.m_pPkgUnitBuf[0]
    

    注意:g_Recv.m_pPkgUnitBuf的结构如下:

  2. recvfrom 接收数据

    recvlen = recvfrom( g_Recv.m_sRecvSocket, 
    					(char*)tPkgUnit.GetHeadAddr(), 										(int)tPkgUnit.GetPkgBufLen(),
                        0, 
                        (SOCKADDR*)&from,
                        &fromlen );
    

    接收的数据放到对应的PkgUnit中

  3. 放入数据处理单元

    if ( g_Recv.ProcessPkgUnit( g_Recv.m_uiPkgUnitPutAddrNum, tHead, tFrom ) )
    {
        // 接收缓存 移动下个空闲位置
        g_Recv.m_uiPkgUnitPutAddrNum++;
    
        if ( g_Recv.m_uiPkgUnitPutAddrNum > g_uiRecvPkgTotalNum )
        {
            g_Recv.m_uiPkgUnitPutAddrNum = 1;
        }
    }
    
  4. 接下来分析g_Recv.ProcessPkgUnit函数

    遍历所有的接收单元,找到与接收数据对应的单元,进行处理

    BOOL RecvCtrl::ProcessPkgUnit( UINT uiPkgUnitAddrNum, PKG_HEAD& tHead, UNIT_ADDR& tFrom )
    {
        RecvUnit* pUnit = m_pUnitSet;
        for ( UCHAR i = 1; i <= m_ucSendNum; i++, pUnit++ )
        {
            RecvUnit& tUnit = *pUnit;
            // 找到接收单元
            if ( tUnit.m_tSendAddr.uiAddr == tFrom.uiAddr 
                && tUnit.m_tSendAddr.usPort == tFrom.usPort )
            {
                bNotFound = FALSE;
    
                // 接收端接收分包写入缓存
                tUnit.PutIntoIndex( uiPkgUnitAddrNum, tHead, i );
                break; 
            }
    
        } 
    }
    
    g_Recv.m_ucSendNum = g_ucSendNum
    g_Recv.m_pUnitSet = new RecvUnit[ g_Recv.m_ucSendNum ];
    SEND01 =对端ip:PKG_SEND_PORT:ACK_RECV_PORT																		 
    g_Recv.m_pUnitSet = 
    RecvUnit[0]-解析SEND01得到-》m_pIndexBuf = RecvIndex[0] [……] RecvIndex[g_uiRecvPkgNum]
    						  m_tSendAddr ={.uiAddr=对端ip .usPort=PKG_SEND_PORT}
    						  m_tAckAddr  ={.uiAddr=对端ip .usPort=ACK_RECV_PORT}
    RecvUnit[2]-解析SEND02得到-》m_pIndexBuf = RecvIndex[0] [……] RecvIndex[g_uiRecvPkgNum]
    						  m_tSendAddr ={.uiAddr=对端ip .usPort=PKG_SEND_PORT}
    						  m_tAckAddr  ={.uiAddr=对端ip .usPort=ACK_RECV_PORT}
    

  5. 接上分析,当找到接收单元后,开始把数据传给对应的接收单元处理

    void RecvUnit::PutIntoIndex( UINT uiPkgUnitAddrNum, PKG_HEAD& tHead, UCHAR ucUnitIndex )
    {
        // 目标索引
        RecvIndex &tIndex = *( m_pIndexBuf + tHead.uiAddrNum - 1 );
    
        // 判断是否出现时间跳变
        BOOL bTimeGapBig = FALSE;
        // 当前差值
        LONGLONG llDelayTime;
    
        // 对非重传包进行校时处理
        if ( !tHead.ucRetryFlag )
        {
            ULONGLONG timep = ULLGetLocalTime();
    
            // 对于新收到的分包, 需要考虑发送端与接收端的时差
            //printf("  timep = %lld , tHead.ullRecvTime = %lld \n",timep,tHead.ullRecvTime);
    
            // 当前差值
            llDelayTime = timep - tHead.ullRecvTime;
            //printf("llDelayTime = %lld , timep = %lld , tHead.ullRecvTime = %lld \n",llDelayTime,timep,tHead.ullRecvTime);
    
            // 时差变化量
            LONGLONG llDelayHold = llDelayTime - m_llDelayFromSender;
    
            // 1毫秒   =       10,000 百纳秒
            llDelayHold = llDelayHold;// / 10000;
    
            // 与发送端时差调整门限比较, 毫秒
            if ( -llDelayHold >= g_uiDelayHold || llDelayHold >= g_uiDelayHold )
            {
                bTimeGapBig = TRUE;
            }
        }
    
        // 更新索引
        BOOL bRet = tIndex.UpdateIndex( uiPkgUnitAddrNum, bTimeGapBig );
    
        // 记录下当前位置
        SetNextRecvInfo( tHead.uiAddrNum, tHead.ullRecvTime, bTimeGapBig );
    
        bRet &= ( m_cNextSendInfo.GetAddr() == tHead.uiAddrNum );
    
        if ( !bRet && m_bRecvOverflow )
        {
            printf( "[%u] recv_unit[%#2u] addr<%#6u> buffer over-write.\n"
                , E_RECV_RECV, ucUnitIndex, tHead.uiAddrNum );
    
            m_bRecvOverflow = FALSE;
            m_cNextSendInfo.SetInfo( 0 );
        }
    
        m_bRecvOverflow = bRet;
    
        // 发送应答
        SendAck( tHead.uiAddrNum, tHead.ullRecvTime );
    
    
        if ( bTimeGapBig )
        {
            m_llDelayFromSender = llDelayTime;
    
            printf( "[%u] .... the time gap between recv_unit[%#2u][ %#6u, %#14llu ] and Sender[%s:%u] is %u ms.\n"
                , E_RECV_RECV, ucUnitIndex, tHead.uiAddrNum, tHead.ullRecvTime
                , m_achSendIp, m_tSendAddr.usPort, m_llDelayFromSender );// / 10000 );
    
    	 m_cNextSendInfo.SetInfo( 0 );
        }
    
    
    
        // 仅当接收缓存空闲时, 设置各线程开始位置
        UINT uiNextAddr;
        uiNextAddr = m_cNextSendInfo.GetAddr();
    
        if ( !uiNextAddr )
        {
            printf( "[%u] recv_unit[%#2u] Starting send progress.\n", E_RECV_RECV, ucUnitIndex );
    
            m_cNextSendInfo.SetInfo( tHead.uiAddrNum );
        }
    }
    

对端通过tun把数据发送出去

ThreadRecverProcessSend线程中,把数据通过tun发送出去

void *ThreadRecverProcessSend( LPVOID lpThreadParameter )
{
    //SOCKADDR_IN to;
    struct timeval tv;

    DWORD dwTemp = reinterpret_cast<DWORD>( lpThreadParameter );
    UCHAR ucUnitIndex = static_cast<UCHAR>( dwTemp );

    RecvUnit& tUnit = *( g_Recv.m_pUnitSet + ucUnitIndex );

    UINT uiNextAddr, uiStopAddr;

    while( g_bServiceRun )
    {
        //printf(" ProcessSend \n ");       
        
        // 当前待处理分包的地址
        uiNextAddr = tUnit.m_cNextSendInfo.GetAddr();

        // 接收结束位置的分包地址(就是最后一个分包)
        uiStopAddr = tUnit.m_cNextRecvInfo.GetAddr();
     
        //printf( "[%u] .... recv_unit[%#2u] 接收缓存[%#6u]-[%#6u].\n"
        //                    , E_RECV_SEND, ucUnitIndex, uiStopAddr, uiNextAddr);
        
        // 缓存是否空闲:
        // 1、条件1:已知待处理分包的地址;
        // 2、条件2:已知待处理分包的地址相对接收结束位置是不同的位置, 或者接收结束位置的分包(就是最后一个分包)还没有处理;
        if ( uiNextAddr && ( uiNextAddr != uiStopAddr || tUnit.m_bMoveNext ) )
        {        
            //printf(" ProcessSend 1 \n ");
            
            if ( !tUnit.m_bMoveNext )
            {
                // 接收结束位置已改变, 发送应该处理下一个分包
                uiNextAddr++;
                if ( uiNextAddr > g_uiRecvPkgNum )
                {
                    uiNextAddr = 1;
                }

                tUnit.m_cNextSendInfo.SetInfo( uiNextAddr );

                // 默认分包地址递增, 待当前分包处理完之后再判断是不是最后一个分包
                tUnit.m_bMoveNext = TRUE;
            }

            // 当前时间


            ULONGLONG timep = ULLGetLocalTime();

            // 定位当前索引
            RecvIndex &tIndex = *( tUnit.m_pIndexBuf + uiNextAddr - 1 );

            // 默认立即处理当前分包
            BOOL bNoWait = TRUE;

            if ( tIndex.IsUsed() )
            {
                PkgUnit& tPkgUnit = *( g_Recv.m_pPkgUnitBuf + tIndex.GetPkgUnitAddrNum() - 1 );


                if ( ( tPkgUnit.GetHeadRecvTime() + tUnit.m_llDelayFromSender + g_ullDelayPeriod  ) > timep )
                {
                    // 启动时间未到
                    bNoWait = FALSE;
                }
            }

            if ( bNoWait )
            {

                tIndex.SendData( tUnit.m_tSendAddr, ucUnitIndex );
                
                if ( ValidPrint( PRINT_FLAG_SPEED ) )
                {
                    if ( 0 == uiNextAddr % g_usGroupSize )
                    {
                        DWORD dwNew1 = GetTickCount();

                        printf( "[%u] .... recv_unit[%#2u] ---- ---- ---> %#6u ticks, 起始 [%#6u], group of PKGs sent by Recver.\n"
                            , E_RECV_SEND, ucUnitIndex, dwNew1 - tUnit.m_dwLastTick, uiNextAddr - g_usGroupSize + 1 );

                        tUnit.m_dwLastTick = dwNew1;
                    }
                }

                if ( ValidPrint( PRINT_FLAG_GAP ) )
                {
                    if ( 0 == uiNextAddr % g_usGroupSize )
                    {
  
                        UINT uiAddrCap = CalcAddrGap( uiNextAddr, uiStopAddr );

                        float fUsed = (float) 100 * uiAddrCap / (float) g_uiRecvPkgNum;

                        if ( fUsed > tUnit.m_fMaxUsed )
                        {
                            tUnit.m_fMaxUsed = fUsed;
                        }

                        printf( "[%u] .... recv_unit[%#2u] Recver buffer [%#6u]-[%#6u] length = %#6u(%#2.1f%%-%#2.1f%%).\n"
                            , E_RECV_SEND, ucUnitIndex, uiStopAddr, uiNextAddr
                            , uiAddrCap, fUsed, tUnit.m_fMaxUsed );
                    }
                }                

                if ( uiNextAddr == uiStopAddr )
                {
                    // 所有接收的分包都已发送完, 不再继续处理下一个
                    tUnit.m_bMoveNext = FALSE;

                    // only for test
                    //printf( "last addr: send[%#6u], recv[%#6u].\n", g_Send.GetAddr(), uiNextAddr );
                }
                else
                {
                    uiNextAddr++;
                    if ( uiNextAddr > g_uiRecvPkgNum )
                    {
                        uiNextAddr = 1;
                    }

                    tUnit.m_cNextSendInfo.SetInfo( uiNextAddr );
                }

                continue;   // while() 

            }   // if ()

        }   // if ( && )
    
        //tUnit.WaitNotifyHeart();
        tv.tv_sec=0;
        //tv.tv_usec=1000;
        tv.tv_usec = g_uiSelectWaitUsec;
        select(0,NULL,NULL,NULL,&tv);

    }   // while( g_bServiceRun )

    return (void *)E_RECV_SEND;
}

多连接todo

假如有两台STA链接AC的时候,STA的配置如下:

  1. STA1

    [SENDER]
    # 本地ip,用该ip发送 重传包
    PKG_SEND_IP = 192.168.1.110
    
    # 重传包数据发送端口 也就是对端的接收端口
    PKG_SEND_PORT = 13502
    
    # 接收端数目 对于STA来说,只有AC一个
    RECV_NUM = 1
    
    # 接收端信息:AC地址,AC分包接收端口,AC发送到本地应答包的发送端口
    RECV01 = 10.129.43.7:24602:24603
    
    [RECVER]
    # STA接收AC 重传包IP 本地IP
    PKG_RECV_IP = 192.168.1.110
    
    # STA分包数据接收端口 对端AC数据包往这个端口发
    PKG_RECV_PORT = 24602
    # 发送端数目
    SEND_NUM = 1
    
    # 发送端信息:AC地址,AC分包发送端口,AC接收STA应答接收端口(默认127.0.0.1:10002:10003)
    SEND01 = 10.129.43.7:13502:13503
    
  2. STA2

  3. AC