重传程序
主要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 执行完上面代码后就如下

所以综上所述:
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_abyDatatun 上read函数,把数据从内核态 copy到
g_buf[index-1]中,即g_Send.m_pPkgBuf[0].m_abyData中然后调用
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
执行完

然后调用
g_Send.SendToRecv(g_uiAddr, tPkg);函数把数据发送到对端然后再次调用
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
如此反复循环
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_pRecvAddrSet、m_pucAckFlag和m_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}
tPkg.m_pucAckFlag一个数据包中就会有RECV_NUM个,代表着多个接收端

g_Send.m_pRecvAddrSet解析下面的数据,
# 接收端数目(范围1-100,默认1) RECV_NUM = 1 # 接收端信息:目标地址,分包接收端口,应答发送端口(默认127.0.0.1:20002:20003) RECV01 = 10.129.43.7:24602:24603
当接收tun上数据后,会遍历该地址进行 分别发送
g_Send.m_pHeartSet
对端接收处理
重传数据包接收线程ThreadRecverProcessRecv,对应的ip和端口如下:

接收过程如下:
首先获取一个缓存区
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的结构如下:
recvfrom 接收数据
recvlen = recvfrom( g_Recv.m_sRecvSocket, (char*)tPkgUnit.GetHeadAddr(), (int)tPkgUnit.GetPkgBufLen(), 0, (SOCKADDR*)&from, &fromlen );
接收的数据放到对应的PkgUnit中
放入数据处理单元
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; } }
接下来分析
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}
接上分析,当找到接收单元后,开始把数据传给对应的接收单元处理
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的配置如下:


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
STA2
AC