udp
UDT是建立在UDP协议基础之上的应用层协议,其最终是通过UDP协议来接发数据。
最近在看udt协议的一些知识,先把包结构放上来
UDT包结构
总体结构:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
Packet Header(包头) |
Data/Control Information Field(数据包/控制包 信息) |
数据包包头结构:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
0 | Sequence Number |
ff |o | Message Number |
Time Stamp |
Destination Socket ID |
bit 0:
0: Data Packet(0:数据包)
1: Control Packet(1:控制包)
bit ff:
11: solo message packet(11:单一的消息包)
10: first packet of a message(10:一份消息的第一个包)
01: last packet of a message(00:一份消息的最后一个包)
bit o:
0: in order delivery not required(0:没有要求按正常序号传递)
1: in order delivery required(1:要求按正常序号传递)
控制包包头结构:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1 | Type | Reserved |
Additional Info |
Time Stamp |
Destination Socket ID |
bit 0:
0: Data Packet(0:数据包)
1: Control Packet(1:控制包)
bit 1~15:
0: Protocol Connection Handshake
Add. Info: Undefined
Control Info: Handshake information (see CHandShake)
1: Keep-alive
Add. Info: Undefined
Control Info: None
2: Acknowledgement (ACK)
Add. Info: The ACK sequence number
Control Info: The sequence number to which (but not include) all the previous packets have beed received
Optional: RTT
RTT Variance
advertised flow window size (number of packets)
estimated bandwidth (number of packets per second)
3: Negative Acknowledgement (NAK)
Add. Info: Undefined
Control Info: Loss list (see loss list coding below)
4: Congestion Warning
Add. Info: Undefined
Control Info: None
5: Shutdown
Add. Info: Undefined
Control Info: None
6: Acknowledgement of Acknowledement (ACK-square)
Add. Info: The ACK sequence number
Control Info: None
7: Message Drop Request
Add. Info: Message ID
Control Info: first sequence number of the message
last seqeunce number of the message
65535: Explained by bits 16 - 31
bit 16 - 31:
This space is used for future expansion or user defined control packets.
在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(); }