花了一天时间分析了一下UDT的代码,总体感觉代码不是很高深,下面是一些总结
一. 报文发送
1.CSndQueue::worker中调用CChannel::sendto发送数据报文。
2.CSndQueue::sendto中调用CChannel::sendto发送其他报文, 种类较多主要有:
1)CUDT::connect中调用CSndQueue::sendto发送建立连接请求。
2) CUDT::sendCtrl中调用CSndQueue::sendto发送控制报文。
3) CUDT::listen对客户端过来的连接建立握手包给予应答,也是调用CSndQueue::sendto发送报文。
二. 报文接收
CRcvQueue::worker 是所有报文的接收函数,调用CChannel::recvfrom从网络上接收报文。
对收到的包分别处理:
1) Rendezvous类型的连接请求包和建立过程包都会调用 CRcvQueue::storePkt 缓存起来; UDT::connect 中有一个接收点,调用CRcvQueue::recvfrom从这个缓冲区里面取. 非Rendezvous类型的连接请求和建立过程包会交给m_pListener的CUDT::listen处理。
2) 根据目的socketID能查到已经建立好的连接,这样的包还分数据包和控制包两种,分别调用 CUDT::processData , CUDT::processCtrl加以处理.
三. 定时器控制
1. 定时器变量和数值
CTimer::rdtsc在linux下返回的是微秒(即1/ 1000000秒); 而在windows下则返回一个高精度计数,s_ullCPUFrequency为每微妙高精度计数个数。
经换算,一些变量的默认值:
m_ullSYNInt : 10ms
m_ullACKInt: 10ms 含义应该是一旦收到数据包,那么最多10ms后就会发出ACK
m_iRTT: 初始网络RTT为100ms
m_iRTTVar: 初始RTT变幅为 50ms
m_ullNAKInt: m_iRTT+4×m_iRTTVar 因此初始值为300ms
m_ullMinEXPInt: 初始值为100ms
2. 定位器处理
定时器处理和接收处理共用线程CRcvQueue::worker。底层socket在linux下被设置成了非阻塞,windows下是设置了1ms接收超时, 因此接收处于不断查询中,每接收一次返回处理后就进入定时器处理,调用的是CUDT::checkTimers, 主要的处理:
1) 确认应答(ACK)
如果超过设定的下次应答时间,或者连续接收到的数据包数超过设定的应答间隔,发送应答包ACK, 发送之后更新下次应答时间,把连续接收数据包的计数清零。
如果通过CCC设置的应答时间间隔和应答个数间隔都非常大,流程看还会每m_iSelfClockInterval(64)个包发一次应答.
2) 定应答(NAK)
如果有丢包,并且超过设定的下否定应答时间,则发送NAK, 并且更新时间。
3) 到期(老化)处理
如果超过设定的下次到期时间
1)连续16次超时并且,总无应答时间超过10秒,则关闭连接。
2)将发出后没有收到应答的包序号填入丢失列表,并马上触发重发
3)如果没有包在发送,则发送keep-alive包
4)到期次数加1,最小到期间隔根据到期次数加大间隔(倍速增长),保证最小为100ms.
更新下次到期时间.
5) 相关流程
到期次数m_iEXPCount在收到数据或者控制包后都会重置为1,同时下次到期时间也会被更新。一旦长时间无应答,m_iEXPCount就会持续增加,直到连接老化。
四. 报文缓存buffer组织及窗口机制
CCC窗口参数m_dCWndSize对应的内部变量是CUDT. m_dCongestionWindow, 在CUDT::packData函数内,这个变量起到了流控的作用(同时另外一个起流控作用的参数是m_dPktSndPeriod,控制发包之间的间隔时间(因为windows下使用的定时器精确度在15ms左右,因此实际上导致如果发包间隔不为0,则发包间隔一定大于这个值,因此更合理的是按每秒发包个数来控制). UDTsocket设置UDT_SNDBUF参数则控制了发送缓冲区的大小,发送缓冲区不够时,如果设置了非同步方式,则失败返回;阻塞式连接情况下,如果设置了发送超时,则超时返回,否则一直阻塞。
而接收缓冲区一旦不够用时,UDT会把通道内的包接收起来直接丢弃,其影响相当于网络丢包。
五. UDT线程
总共三个线程, 执行函数分别是:
CSndQueue::worker : 发送处理
CRcvQueue::worker: 接收和定时器处理
CUDTUnited::garbageCollect: 垃圾(如残连接)清理