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: 垃圾(如残连接)清理

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的C语言UDT服务端代码示例,实现了接收客户端发来的消息并回复客户端: ```c #include <stdio.h> #include <string.h> #include <udt.h> int main(int argc, char* argv[]) { // 初始化UDTUDT::startup(); // 创建UDT套接字 UDTSOCKET serv_sock = UDT::socket(AF_INET, SOCK_STREAM, 0); // 绑定端口号 struct sockaddr_in my_addr; memset(&my_addr, 0, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(9000); // 绑定9000端口号 my_addr.sin_addr.s_addr = INADDR_ANY; UDT::bind(serv_sock, (struct sockaddr*)&my_addr, sizeof(my_addr)); // 监听端口号 UDT::listen(serv_sock, 10); // 最多允许10个客户端连接 // 等待客户端连接 struct sockaddr_in client_addr; int addrlen = sizeof(client_addr); UDTSOCKET client_sock = UDT::accept(serv_sock, (struct sockaddr*)&client_addr, &addrlen); // 接收客户端发来的消息 char buf[1024]; memset(buf, 0, sizeof(buf)); int len = UDT::recv(client_sock, buf, sizeof(buf), 0); // 输出接收到的消息 printf("Received message: %s\n", buf); // 回复客户端 const char* reply = "Hello, client!"; UDT::send(client_sock, reply, strlen(reply) + 1, 0); // 关闭客户端套接字 UDT::close(client_sock); // 关闭服务端套接字 UDT::close(serv_sock); // 关闭UDTUDT::cleanup(); return 0; } ``` 该代码使用UDT库创建了一个UDT服务端套接字,并绑定到9000端口号。服务端监听客户端连接请求,并在接收到连接请求后,接收客户端发来的消息,并回复客户端。最后,关闭客户端套接字和服务端套接字,关闭UDT库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值