您的位置:首页 > 其它

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

2017-07-02 18:16 357 查看
#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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: