锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 开源技术 / 基于UDP的数据传输协议UDT / UDT源码剖析 UDT自带例程sendfile注释
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

UDT源码剖析(一):UDT自带例程sendfile注释


#ifndef WIN32
   #include <cstdlib>
   #include <netdb.h>
#else
   #include <winsock2.h>
   #include <ws2tcpip.h>
#endif
#include <fstream>
#include <iostream>
#include <cstring>
#include <udt.h>


using namespace std;


#ifndef WIN32
void* sendfile(void*);
#else
DWORD WINAPI sendfile(LPVOID);
#endif


int main(int argc, char* argv[])
{
   //usage: sendfile [server_port]
   if ((2 < argc) || ((2 == argc) && (0 == atoi(argv[1]))))
   {
      cout << "usage: sendfile [server_port]" << endl;
      return 0;
   }


   // use this function to initialize the UDT library
   // 初始化UDT库
   UDT::startup();


   // 地址信息提示
   addrinfo hints;


   // 实际使用的地址信息指针
   addrinfo* res;


   // 内存初始化,全部置为0
   memset(&hints, 0, sizeof(struct addrinfo));


   // 如上表所示,ai_flagsde值范围为0~7,取决于程序如何设置3个标志位,
   // 比如设置ai_flags为 “AI_PASSIVE|AI_CANONNAME”,ai_flags值就为3。三个参数的含义分别为:
   // (1)AI_PASSIVE当此标志置位时,表示调用者将在bind()函数调用中使用返回的地址结构。当此标志不置位时,表示将在connect()函数调用中使用。
   //    当节点名位NULL,且此标志置位,则返回的地址将是通配地址。如果节点名NULL,且此标志不置位,则返回的地址将是回环地址。
   // (2)AI_CANNONAME当此标志置位时,在函数所返回的第一个addrinfo结构中的ai_cannoname成员中,
   //    应该包含一个以空字符结尾的字符串,字符串的内容是节点名的正规名。
   // (3)AI_NUMERICHOST当此标志置位时,此标志表示调用中的节点名必须是一个数字地址字符串。
   hints.ai_flags = AI_PASSIVE;


   // AF_UNIX(本机通信)
   // AF_INET(TCP/IP – IPv4)
   // AF_INET6(TCP/IP – IPv6)
   hints.ai_family = AF_INET;


   // SOCK_STREAM: 提供面向连接的稳定数据传输,即TCP协议。
   // OOB: 在所有数据传送前必须使用connect()来建立连接状态。
   // SOCK_DGRAM: 使用不连续不可靠的数据包连接。
   // SOCK_SEQPACKET: 提供连续可靠的数据包连接。
   // SOCK_RAW: 提供原始网络协议存取。
   // SOCK_RDM: 提供可靠的数据包连接。
   // SOCK_PACKET: 与网络驱动程序直接通信。
   //////////// 注:此处的SOCK_STREAM并不是表示UDT将会使用TCP类型的Socket,在底层将会转化为UDT_STREAM
   //////////// 并且在UDT中仅支持SOCK_STREAM和SOCK_DGRAM,分别对应UDT_STREAM和UDT_DGRAM
   hints.ai_socktype = SOCK_STREAM;


   // 默认服务端口
   string service("9000");
   if (2 == argc)
      service = argv[1];


   // 根据地址信息提示分配实际可用的地址信息
   if (0 != getaddrinfo(NULL, service.c_str(), &hints, &res))
   {
      cout << "illegal port number or port is busy.\n" << endl;
      return 0;
   }


   // 根据实际使用的地址信息创建一个UDT的socket
   // 此处实际上最终调用了CUDTUnited的newSocket,第一个参数会被直接设置到CUDT的m_iIPversion,第二个参数会被映射为UDT的连接类型,第三个参数被忽略,没有实际意义
   // Gu说了函数原型尽量保持与BSD的Socket一致,因此一些对系统没意义的参数也被照搬了进来……
   UDTSOCKET serv = UDT::socket(res->ai_family, res->ai_socktype, res->ai_protocol);


   // Windows UDP issue
   // For better performance, modify HKLM\System\CurrentControlSet\Services\Afd\Parameters\FastSendDatagramThreshold
   // Windows下的UDP的问题
   // 如果想要提高效率,可以修改注册表HKLM\System\CurrentControlSet\Services\Afd\Parameters\FastSendDatagramThreshold
#ifdef WIN32
   int mss = 1052;


   // 第一个参数为要设置的UDTSocket
   // 第二个参数0是会被忽略的,没有实际意义
   // 第三个参数为要设置的参数,有以下几种选项、来自enum UDTOpt:
   //     UDT_MSS 最大传输单位
   //     UDT_SNDSYN 是否阻塞发送
   //     UDT_RCVSYN 是否阻塞接收
   //     UDT_CC 自定义拥塞控制算法
   //     UDT_FC 窗口大小
   //     UDT_SNDBUF 发送队列缓冲最大值
   //     UDT_RCVBUF UDT接收缓冲大小
   //     UDT_LINGER 关闭时等待数据发送完成
   //     UDP_SNDBUF UDP发送缓冲大小
   //     UDP_RCVBUF UDP接收缓冲大小
   //     UDT_RENDEZVOUS 会合连接模式
   //     UDT_SNDTIMEO send()超时
   //     UDT_RCVTIMEO recv()超时
   //     UDT_REUSEADDR 复用一个已存在的端口或者创建一个新的端口
   //     UDT_MAXBW 当前连接可以使用的最大带宽(bytes per second)
   // 第四个参数是参数值
   // 第五个参数为参数值长度,在底层也会被忽略,没有意义
   UDT::setsockopt(serv, 0, UDT_MSS, &mss, sizeof(int));
#endif


   // 绑定端口,第一个参数为Socket,第二个为绑定地址,第三个为地址长度(底层用来判断是否符合IPv4、IPv6标准的长度)
   if (UDT::ERROR == UDT::bind(serv, res->ai_addr, res->ai_addrlen))
   {
      cout << "bind: " << UDT::getlasterror().getErrorMessage() << endl;
      return 0;
   }


   // 释放通过getaddrinfo分配的地址信息
   freeaddrinfo(res);


   cout << "server is ready at port: " << service << endl;


   // 开始监听,第一个参数为Socket,第二个参数为队列中允许的最大连接数
   UDT::listen(serv, 10);


   // 客户端地址信息
   sockaddr_storage clientaddr;
   // 地址信息数据长度
   int addrlen = sizeof(clientaddr);


   // 捕获连接的Socket
   UDTSOCKET fhandle;


   while (true)
   {
  // 接受连接请求,第一个参数为Socket,第二个参数为请求方地址信息,第三个参数为地址信息长度,
  // 第二个和第三个参数必须分配好地址空间,才能返回信息
      if (UDT::INVALID_SOCK == (fhandle = UDT::accept(serv, (sockaddr*)&clientaddr, &addrlen)))
      {
         cout << "accept: " << UDT::getlasterror().getErrorMessage() << endl;
         return 0;
      }


 // 通过getnameinfo()将地址信息转换为节点地址、端口信息
      char clienthost[NI_MAXHOST];
      char clientservice[NI_MAXSERV];
      getnameinfo((sockaddr *)&clientaddr, addrlen, clienthost, sizeof(clienthost), clientservice, sizeof(clientservice), NI_NUMERICHOST|NI_NUMERICSERV);
      cout << "new connection: " << clienthost << ":" << clientservice << endl;


 // 连接成功后启动sendfile线程,传入捕获到的Socket,这里使用了拷贝构造创建一个新的对象,使得fhandle本身的使用不会受到线程的影响
      #ifndef WIN32
         pthread_t filethread;
         pthread_create(&filethread, NULL, sendfile, new UDTSOCKET(fhandle));
         pthread_detach(filethread);
      #else
         CreateThread(NULL, 0, sendfile, new UDTSOCKET(fhandle), 0, NULL);
      #endif
   }


   // 关闭监听Socket
   UDT::close(serv);


   // use this function to release the UDT library
   // 释放UDT库
   UDT::cleanup();


   return 0;
}


#ifndef WIN32
void* sendfile(void* usocket)
#else
DWORD WINAPI sendfile(LPVOID usocket)
#endif
{
// 获取Socket,并清除原传入的Socket
   UDTSOCKET fhandle = *(UDTSOCKET*)usocket;
   delete (UDTSOCKET*)usocket;


   // aquiring file name information from client
   // 从请求端获得文件名
   char file[1024];
   int len;


   // 接收一个数据包,内容为一个int值,长度为int的长度,数值保存在len中
   // 第一个参数为Socket,第二个参数为数据保存地址,第三个参数为数据长度,第四个参数底层会忽略,没有意义
   if (UDT::ERROR == UDT::recv(fhandle, (char*)&len, sizeof(int), 0))
   {
      cout << "recv: " << UDT::getlasterror().getErrorMessage() << endl;
      return 0;
   }


   // 开始接收信息,通过上面获得的长度来判断信息的大小
   if (UDT::ERROR == UDT::recv(fhandle, file, len, 0))
   {
      cout << "recv: " << UDT::getlasterror().getErrorMessage() << endl;
      return 0;
   }


   // 给接收到的字符串加结束符
   file[len] = '\0';


   // open the file
   // 打开请求方指定的文件,以二进制方式读取打开
   fstream ifs(file, ios::in | ios::binary);


   // 让文件指针定位到文件末尾
   ifs.seekg(0, ios::end);
   // 获得文件大小
   int64_t size = ifs.tellg();
   // 让文件指针定位到文件开头
   ifs.seekg(0, ios::beg);


   // send file size information
   // 向请求方发送文件长度
   // 第一个参数为Socket,第二个参数为数据地址,第三个参数为数据长度,第四个参数会被底层忽略,没有意义
   if (UDT::ERROR == UDT::send(fhandle, (char*)&size, sizeof(int64_t), 0))
   {
      cout << "send: " << UDT::getlasterror().getErrorMessage() << endl;
      return 0;
   }


   // 初始采样,内容见CPerfMon
   UDT::TRACEINFO trace;
   UDT::perfmon(fhandle, &trace);


   // send the file
   // 偏移量为0,开始发送文件
   // 第一个参数为Socket,第二个参数为文件流,第三个参数为偏移量,第四个参数为文件大小
   int64_t offset = 0;
   if (UDT::ERROR == UDT::sendfile(fhandle, ifs, offset, size))
   {
      cout << "sendfile: " << UDT::getlasterror().getErrorMessage() << endl;
      return 0;
   }


   // 获取采样信息,内容见CPerfMon
   // 得出传输效率
   UDT::perfmon(fhandle, &trace);
   cout << "speed = " << trace.mbpsSendRate << "Mbits/sec" << endl;


   // 关闭连接Socket
   UDT::close(fhandle);


   // 关闭文件流
   ifs.close();


   #ifndef WIN32
      return NULL;
   #else
      return 0;
   #endif
}
友情链接
版权所有 Copyright(c)2004-2024 锐英源软件
统一社会信用代码:91410105098562502G 豫ICP备08007559号 最佳分辨率 1440*900
地址:郑州市金水区文化路97号郑州大学北区院内南门附近