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;
注:CPacket即UDP包结构(数据包与控制包)
 
#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
 
setSndBufSize与setRcvBufSize分别设置发送和接收缓冲区大小,即对m_iSndBufSize和m_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 
水 阅读(4951) 
评论(3)  编辑 收藏 引用  所属分类: 
udt分析