aurain
技术文摘
posts - 137,  comments - 268,  trackbacks - 0

UDT是建立在UDP协议基础之上的应用层协议,其最终是通过UDP协议来接发数据。

UDT的实现中,是通过类CChannel来处理的,顾名思义,可以理解为通过UDP管道来接发数据。

       来看看CChannel提供的主要方法:

void setSndBufSize(const int& size);

void setRcvBufSize(const int& size);

void open(const sockaddr* addr = NULL);

int sendto(const sockaddr* addr, CPacket& packet) const;

int recvfrom(sockaddr* addr, CPacket& packet) const;

注:CPacketUDP包结构(数据包与控制包)

 

#ifndef WIN32

  int m_iSocket;                    // socket descriptor

#else

  SOCKET m_iSocket;

#endif

int m_iSndBufSize;                   // UDP sending buffer size

int m_iRcvBufSize;                   // UDP receiving buffer size

 

setSndBufSizesetRcvBufSize分别设置发送和接收缓冲区大小,即对m_iSndBufSizem_iRcvBufSize赋值,主要看看open函数:

void CChannel::open(const sockaddr* addr)

{

   // construct an socket

   m_iSocket = socket(m_iIPversion, SOCK_DGRAM, 0);

   if (NULL != addr)

   {

      socklen_t namelen = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) :sizeof(sockaddr_in6);

      if (0 != bind(m_iSocket, addr, namelen))

         //error

   }

   else

   {

      //sendto or WSASendTo will also automatically bind the socket

      addrinfo hints;

      addrinfo* res;

      memset(&hints, 0, sizeof(struct addrinfo));

      hints.ai_flags = AI_PASSIVE;

      hints.ai_family = m_iIPversion;

      hints.ai_socktype = SOCK_DGRAM;

      if (0 != getaddrinfo(NULL, "0", &hints, &res))

        //error

      if (0 != bind(m_iSocket, res->ai_addr, res->ai_addrlen))

        //error

      freeaddrinfo(res);

   }

   setUDPSockOpt();    //调用TCP/IP协议栈提供的方法设置UDP选项(缓冲区大小)

}

void CChannel::setUDPSockOpt()

{

   if ((0 != setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char *)&m_iRcvBufSize, sizeof(int))) ||(0 != setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char *)&m_iSndBufSize,sizeof(int))))

      //error

 

   #ifdef WIN32

      DWORD ot = 1; //milliseconds

      if (setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&ot, sizeof(DWORD)) < 0)

         throw CUDTException(1, 3, NET_ERROR);

   #else

      // Set receiving time-out value

      if (setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(timeval)) < 0)

         throw CUDTException(1, 3, NET_ERROR);

   #endif

}

 

下面看看sendto函数,该函数是把CPacket内容通过udp发送出去,分linux系与win32两种情况。

int CChannel::sendto(const sockaddr* addr, CPacket& packet) const

{

   // 主机序到网络序

   if (packet.getFlag())//如果是控制包

      for (int i = 0, n = packet.getLength() / 4; i < n; ++ i)

         *((uint32_t *)packet.m_pcData + i) = htonl(*((uint32_t *)packet.m_pcData + i));

 

   // convert packet header into network order(包头序也需要转换)

   for (int j = 0; j < 4; ++ j)

      packet.m_nHeader[j] = htonl(packet.m_nHeader[j]);

 

   #ifndef WIN32

      msghdr mh;

      mh.msg_name = (sockaddr*)addr;

      mh.msg_namelen = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);

      mh.msg_iov = (iovec*)packet.m_PacketVector;

      mh.msg_iovlen = 2;

      mh.msg_control = NULL;

      mh.msg_controllen = 0;

      mh.msg_flags = 0;

 

      int res = sendmsg(m_iSocket, &mh, 0);    //调用协议栈函数发送出去

   #else

      DWORD size = CPacket::m_iPktHdrSize + packet.getLength();

      int addrsize = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);

      int res = WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr, addrsize, NULL, NULL);

      res = (0 == res) ? size : -1;

   #endif

 

   // 恢复序

   for (int k = 0; k < 4; ++ k)

      packet.m_nHeader[k] = ntohl(packet.m_nHeader[k]);

 

   if (packet.getFlag())

      for (int l = 0, n = packet.getLength() / 4; l < n; ++ l)

         *((uint32_t *)packet.m_pcData + l) = ntohl(*((uint32_t *)packet.m_pcData + l));

 

   return res;

}

 

recvfrom的原理也类似与sendto

int CChannel::recvfrom(sockaddr* addr, CPacket& packet) const

{

   #ifndef WIN32

      msghdr mh;  

      mh.msg_name = addr;

      mh.msg_namelen = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);

      mh.msg_iov = packet.m_PacketVector;

      mh.msg_iovlen = 2;

      mh.msg_control = NULL;

      mh.msg_controllen = 0;

      mh.msg_flags = 0;

 

      #ifdef UNIX

         fd_set set;

         timeval tv;

         FD_ZERO(&set);

         FD_SET(m_iSocket, &set);

         tv.tv_sec = 0;

         tv.tv_usec = 10000;

         select(m_iSocket+1, &set, NULL, &set, &tv);

      #endif

 

      int res = recvmsg(m_iSocket, &mh, 0);

   #else

      DWORD size = CPacket::m_iPktHdrSize + packet.getLength();

      DWORD flag = 0;

      int addrsize = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);

 

      int res = WSARecvFrom(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, &flag, addr, &addrsize, NULL, NULL);

      res = (0 == res) ? size : -1;

   #endif

 

   if (res <= 0)

   {

      packet.setLength(-1);

      return -1;

   }

 

   packet.setLength(res - CPacket::m_iPktHdrSize);

 

   // convert back into local host order

   for (int i = 0; i < 4; ++ i)

      packet.m_nHeader[i] = ntohl(packet.m_nHeader[i]);

 

   if (packet.getFlag())

      for (int j = 0, n = packet.getLength() / 4; j < n; ++ j)

         *((uint32_t *)packet.m_pcData + j) = ntohl(*((uint32_t *)packet.m_pcData + j));

 

   return packet.getLength();

}

posted on 2008-11-26 11:42 阅读(4877) 评论(3)  编辑 收藏 引用 所属分类: udt分析

FeedBack:
# re: UDT接发数据
2009-08-13 21:17 | azure
问一下哈.不知道udt能否改装成想udp那样可以使用同一个端口进行多个客户端连接,也就是假若实现p2p的话该如何操作  回复  更多评论
  
# re: UDT接发数据
2009-08-15 20:08 | oldseven
@azure
我也正想问这个问题,我觉的还是到 UDT的SourceForget网站上去问吧。  回复  更多评论
  
# re: UDT接发数据
2009-08-17 09:44 |
@azure
@oldseven
~~,这个UDT我也好长一段时间没弄过了,官网应该有清楚的解答  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理



<2008年8月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456

常用链接

留言簿(17)

随笔分类(138)

随笔档案(137)

网络开发

最新随笔

搜索

  •  

积分与排名

  • 积分 - 488926
  • 排名 - 36

最新随笔

最新评论

阅读排行榜

评论排行榜