////////////////////////////////////////////////////////////////////////////// // 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 // 1: Control Packet // bit ff: // 11: solo message packet // 10: first packet of a message // 01: last packet of a message // bit o: // 0: in order delivery not required // 1: in order delivery required // // 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 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 // available receiver buffer size (in bytes) // 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/Delay 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 // 8: Error Signal from the Peer Side // Add. Info: Error code // Control Info: None // 0x7FFF: Explained by bits 16 - 31 // // bit 16 - 31: // This space is used for future expansion or user defined control packets. // // 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| Sequence Number a (first) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |0| Sequence Number b (last) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |0| Sequence Number (single) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Loss List Field Coding: // For any consectutive lost seqeunce numbers that the differnece between // the last and first is more than 1, only record the first (a) and the // the last (b) sequence numbers in the loss list field, and modify the // the first bit of a to 1. // For any single loss or consectutive loss less than 2 packets, use // the original sequence numbers in the field. using System; using System.Net; using System.Net.Sockets; using System.Text; namespace UdtSharp { public struct iovec { public uint[] iov_base; public int iov_len; } public unsafe class Packet { public enum ControlType { Handshake = 0, KeepAlive = 1, Ack = 2, Nak = 3, CongestionWarning = 4, Shutdown = 5, Ack2 = 6, DropMessage = 7, Error = 8, UserType = 32767, } const int m_iSeqNoIndex = 0; // alias: sequence number const int m_iMsgNoIndex = 1; // alias: message number const int m_iTimeStampIndex = 2; // alias: timestamp const int m_iIdIndex = 3; // alias: socket ID public const int m_iPktHdrSize = 16; // packet header size iovec[] m_PacketVector = new iovec[2]; // The 2-demension vector of UDT packet [header, data] public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); if (getFlag() == 0) { byte[] data = GetDataBytes(); stringBuilder.AppendFormat("Data length {0} bytes", data != null ? data.Length : 0); stringBuilder.AppendLine(); stringBuilder.AppendLine(" SeqNo " + GetSequenceNumber()); stringBuilder.AppendLine(" MsgNo " + GetMessageNumber()); stringBuilder.AppendLine(" Timestamp " + GetTimestamp()); stringBuilder.AppendLine(" SocketID " + GetId()); stringBuilder.Append(" Data: {"); if (data != null) { for (int i = 0; i < Math.Min(data.Length, 10); ++i) { stringBuilder.Append(data[i] + ","); } stringBuilder.Length = stringBuilder.Length - 1; } stringBuilder.AppendLine("}"); } else if (getFlag() == 1) { int type = getType(); stringBuilder.AppendFormat("CTRL {0} ({1})", (Packet.ControlType)type, type); stringBuilder.AppendLine(); switch (type) { case 2: //0010 - Acknowledgement (ACK) stringBuilder.AppendFormat(" Ack sequence {0}", getAckSeqNo()); stringBuilder.AppendLine(); break; case 6: //0110 - Acknowledgement of Acknowledgement (ACK-2) stringBuilder.AppendFormat(" Ack2 sequence {0}", getAckSeqNo()); stringBuilder.AppendLine(); break; case 3: //0011 - Loss Report (NAK) break; case 4: //0100 - Congestion Warning break; case 1: //0001 - Keep-alive break; case 0: //0000 - Handshake // control info filed is handshake info Handshake handshake = new Handshake(); handshake.deserialize(GetDataBytes(), Handshake.m_iContentSize); stringBuilder.AppendFormat(handshake.ToString()); stringBuilder.AppendLine(); break; case 5: //0101 - Shutdown break; case 7: //0111 - Message Drop Request break; case 8: //1000 - Error Signal from the Peer Side // Error type stringBuilder.AppendLine("Error: " + m_PacketVector[0].iov_base[m_iMsgNoIndex].ToString()); break; case 32767: //0x7FFF - Reserved for user defined control packets break; default: break; } } return stringBuilder.ToString(); } public Packet() { m_PacketVector[0].iov_base = new uint[4]; m_PacketVector[0].iov_len = m_iPktHdrSize; m_PacketVector[1].iov_base = null; m_PacketVector[1].iov_len = 0; } ~Packet() { } public void Clone(Packet source) { Buffer.BlockCopy(source.m_PacketVector[0].iov_base, 0, m_PacketVector[0].iov_base, 0, m_iPktHdrSize); if (source.m_PacketVector[1].iov_base == null) { m_PacketVector[1].iov_base = null; m_PacketVector[1].iov_len = source.m_PacketVector[1].iov_len; return; } m_PacketVector[1].iov_base = new uint[source.m_PacketVector[1].iov_base.Length]; Buffer.BlockCopy(source.m_PacketVector[1].iov_base, 0, m_PacketVector[1].iov_base, 0, source.m_PacketVector[1].iov_len); m_PacketVector[1].iov_len = source.m_PacketVector[1].iov_len; } public int GetSequenceNumber() { return (int)m_PacketVector[0].iov_base[m_iSeqNoIndex]; } public void SetSequenceNumber(int sequenceNumber) { m_PacketVector[0].iov_base[m_iSeqNoIndex] = (uint)sequenceNumber; } public uint GetMessageNumber() { return m_PacketVector[0].iov_base[m_iMsgNoIndex]; } public void SetMessageNumber(uint messageNumber) { m_PacketVector[0].iov_base[m_iMsgNoIndex] = messageNumber; } public int GetTimestamp() { return (int)m_PacketVector[0].iov_base[m_iTimeStampIndex]; } public void SetTimestamp(int timestamp) { m_PacketVector[0].iov_base[m_iTimeStampIndex] = (uint)timestamp; } public int GetId() { return (int)m_PacketVector[0].iov_base[m_iIdIndex]; } public void SetId(int id) { m_PacketVector[0].iov_base[m_iIdIndex] = (uint)id; } public byte[] GetBytes() { int dataLength = m_PacketVector[1].iov_len; byte[] bytes = new byte[m_iPktHdrSize + dataLength]; Buffer.BlockCopy(m_PacketVector[0].iov_base, 0, bytes, 0, m_iPktHdrSize); if (dataLength == 0 || m_PacketVector[1].iov_base == null) return bytes; Buffer.BlockCopy(m_PacketVector[1].iov_base, 0, bytes, m_iPktHdrSize, dataLength); return bytes; } public byte[] GetHeaderBytes() { byte[] bytes = new byte[m_iPktHdrSize]; Buffer.BlockCopy(m_PacketVector[0].iov_base, 0, bytes, 0, m_iPktHdrSize); return bytes; } public int GetDataBytes(int packetOffset, byte[] data, int dataOffset, int length) { if (m_PacketVector[1].iov_base == null) return 0; int bufferAvailable = data.Length - dataOffset; if (bufferAvailable < length) length = bufferAvailable; Buffer.BlockCopy(m_PacketVector[1].iov_base, packetOffset, data, dataOffset, length); return length; } public int GetIntFromData(int offset) { return (int)m_PacketVector[1].iov_base[offset]; } public byte[] GetDataBytes() { if (m_PacketVector[1].iov_base == null) return null; int dataLength = m_PacketVector[1].iov_len; if (dataLength <= 0) return null; byte[] bytes = new byte[dataLength]; Buffer.BlockCopy(m_PacketVector[1].iov_base, 0, bytes, 0, bytes.Length); return bytes; } public bool SetHeaderAndDataFromBytes(byte[] bytes, int length) { if (length < m_iPktHdrSize) return false; Buffer.BlockCopy(bytes, 0, m_PacketVector[0].iov_base, 0, m_iPktHdrSize); int dataLength = length - m_iPktHdrSize; if (dataLength == 0) { m_PacketVector[1].iov_base = null; m_PacketVector[1].iov_len = 0; return true; } SetDataFromBytes(bytes, m_iPktHdrSize, dataLength); return true; } public void SetDataFromBytes(byte[] bytes) { SetDataFromBytes(bytes, 0, bytes.Length); } public void SetDataFromBytes(byte[] bytes, int offset, int byteCount) { int intCount = byteCount / 4; if (byteCount % 4 != 0) ++intCount; m_PacketVector[1].iov_base = new uint[intCount]; m_PacketVector[1].iov_len = byteCount; Buffer.BlockCopy(bytes, offset, m_PacketVector[1].iov_base, 0, byteCount); } public void ConvertControlInfoToNetworkOrder() { if (getFlag() == 0) return; if (m_PacketVector[1].iov_base == null || m_PacketVector[1].iov_len == 0) return; for (int i = 0; i < m_PacketVector[1].iov_base.Length; ++i) { m_PacketVector[1].iov_base[i] = (uint)IPAddress.HostToNetworkOrder((int)m_PacketVector[1].iov_base[i]); } } public void ConvertControlInfoToHostOrder() { if (getFlag() == 0) return; if (m_PacketVector[1].iov_base == null || m_PacketVector[1].iov_len == 0) return; for (int i = 0; i < m_PacketVector[1].iov_base.Length; ++i) { m_PacketVector[1].iov_base[i] = (uint)IPAddress.NetworkToHostOrder((int)m_PacketVector[1].iov_base[i]); } } public void ConvertHeaderToNetworkOrder() { for (int i = 0; i < m_PacketVector[0].iov_base.Length; ++i) { m_PacketVector[0].iov_base[i] = (uint)IPAddress.HostToNetworkOrder((int)m_PacketVector[0].iov_base[i]); } } public void ConvertHeaderToHostOrder() { for (int i = 0; i < m_PacketVector[0].iov_base.Length; ++i) { m_PacketVector[0].iov_base[i] = (uint)IPAddress.NetworkToHostOrder((int)m_PacketVector[0].iov_base[i]); } } public int getLength() { return m_PacketVector[1].iov_len; } public void setLength(int len) { m_PacketVector[1].iov_len = len; } static iovec MakeIovec(void* rparam, int size) { iovec result = new iovec(); result.iov_len = size; if (rparam == null) return result; result.iov_base = new uint[size >> 2]; uint* pIn = (uint*)rparam; for (int i = 0; i < size >> 2; ++i) { result.iov_base[i] = *pIn++; } return result; } public void pack(Handshake hs) { // TODO avoid this inefficient buffer creation // we copy this buffer into another buffer later byte[] bytes = new byte[Handshake.m_iContentSize]; hs.serialize(bytes); pack(0, bytes); } public void pack(int pkttype, void* lparam) { if (pkttype != 6 && pkttype != 8) throw new Exception("pkttype must be 6 or 8"); pack(pkttype, lparam, (void*)null, 0); } public void pack(int pkttype, byte[] rparam) { if (pkttype != 0) throw new Exception("pkttype must be 0"); fixed (byte* prparam = rparam) { pack(pkttype, (void*)null, (void*)prparam, rparam.Length); } } public void pack(int pkttype, int lparam, int[] rparam) { if (pkttype != 2) throw new Exception("pkttype must be 2"); fixed (int* prparam = rparam) { pack(pkttype, &lparam, (void*)prparam, rparam.Length * 4); } } public void pack(int pkttype, int lparam, int[] rparam, int length) { if (pkttype != 2) throw new Exception("pkttype must be 2"); fixed (int* prparam = rparam) { pack(pkttype, &lparam, (void*)prparam, length * 4); } } public void pack(int pkttype, int[] rparam, int length) { if (pkttype != 3) throw new Exception("pkttype must be 3"); fixed (int* prparam = rparam) { pack(pkttype, (void*)null, (void*)prparam, length * 4); } } public void pack(int pkttype) { if (pkttype != 1 && pkttype != 4 && pkttype != 5) throw new Exception("pkttype must be 1, 4 or 5"); pack(pkttype, (void*)null, (void*)null, 0); } public void pack(int pkttype, void* lparam, void* rparam, int size) { // Set (bit-0 = 1) and (bit-1~15 = type) m_PacketVector[0].iov_base[m_iSeqNoIndex] = (uint)0x80000000 | (uint)(pkttype << 16); // Set additional information and control information field switch (pkttype) { case 2: //0010 - Acknowledgement (ACK) // ACK packet seq. no. if (null != lparam) m_PacketVector[0].iov_base[m_iMsgNoIndex] = *(uint*)lparam; // data ACK seq. no. // optional: RTT (microsends), RTT variance (microseconds) advertised flow window size (packets), and estimated link capacity (packets per second) m_PacketVector[1] = MakeIovec(rparam, size); break; case 6: //0110 - Acknowledgement of Acknowledgement (ACK-2) // ACK packet seq. no. m_PacketVector[0].iov_base[m_iMsgNoIndex] = *(uint*)lparam; // control info field should be none // but "writev" does not allow this m_PacketVector[1] = MakeIovec(null, 4); break; case 3: //0011 - Loss Report (NAK) // loss list m_PacketVector[1] = MakeIovec(rparam, size); break; case 4: //0100 - Congestion Warning // control info field should be none // but "writev" does not allow this m_PacketVector[1] = MakeIovec(null, 4); break; case 1: //0001 - Keep-alive // control info field should be none // but "writev" does not allow this m_PacketVector[1] = MakeIovec(null, 4); break; case 0: //0000 - Handshake // control info filed is handshake info m_PacketVector[1] = MakeIovec(rparam, size); break; case 5: //0101 - Shutdown // control info field should be none // but "writev" does not allow this m_PacketVector[1] = MakeIovec(null, 4); break; case 7: //0111 - Message Drop Request // msg id m_PacketVector[0].iov_base[m_iMsgNoIndex] = *(uint*)lparam; //first seq no, last seq no m_PacketVector[1] = MakeIovec(rparam, size); break; case 8: //1000 - Error Signal from the Peer Side // Error type m_PacketVector[0].iov_base[m_iMsgNoIndex] = *(uint*)lparam; // control info field should be none // but "writev" does not allow this m_PacketVector[1] = MakeIovec(null, 4); break; case 32767: //0x7FFF - Reserved for user defined control packets // for extended control packet // "lparam" contains the extended type information for bit 16 - 31 // "rparam" is the control information m_PacketVector[0].iov_base[m_iSeqNoIndex] |= *(uint*)lparam; if (null != rparam) { m_PacketVector[1] = MakeIovec(rparam, size); } else { m_PacketVector[1] = MakeIovec(null, 4); } break; default: break; } } public iovec[] getPacketVector() { return m_PacketVector; } public int getFlag() { // read bit 0 return (int)(m_PacketVector[0].iov_base[m_iSeqNoIndex] >> 31); } public int getType() { // read bit 1~15 return (int)((m_PacketVector[0].iov_base[m_iSeqNoIndex] >> 16) & 0x00007FFF); } int getExtendedType() { // read bit 16~31 return (int)(m_PacketVector[0].iov_base[m_iSeqNoIndex] & 0x0000FFFF); } public int getAckSeqNo() { // read additional information field return (int)m_PacketVector[0].iov_base[m_iMsgNoIndex]; } public int getMsgBoundary() { // read [1] bit 0~1 return (int)(m_PacketVector[0].iov_base[m_iMsgNoIndex] >> 30); } public bool getMsgOrderFlag() { // read [1] bit 2 return (1 == ((m_PacketVector[0].iov_base[m_iMsgNoIndex] >> 29) & 1)); } public int getMsgSeq() { // read [1] bit 3~31 return (int)(m_PacketVector[0].iov_base[m_iMsgNoIndex] & 0x1FFFFFFF); } } public class Handshake { public const int m_iContentSize = 48; // Size of hand shake data public int m_iVersion; // UDT version public SocketType m_iType; // UDT socket type public int m_iISN; // random initial sequence number public int m_iMSS; // maximum segment size public int m_iFlightFlagSize; // flow control window size public int m_iReqType; // connection request type: 1: regular connection request, 0: rendezvous connection request, -1/-2: response public int m_iID; // socket ID public int m_iCookie; // cookie public uint[] m_piPeerIP = new uint[4]; // The IP address that the peer's UDP port is bound to public Handshake() { for (int i = 0; i < 4; ++i) m_piPeerIP[i] = 0; } public override string ToString() { string type = "connection request"; if (m_iReqType == 0) type = "rendezvouz"; if (m_iReqType < 0) type = "reponse"; if (m_iReqType == 1002) type = "rejected request"; StringBuilder sb = new StringBuilder(); sb.AppendLine(" Version " + m_iVersion); sb.AppendLine(" Type " + type); sb.AppendLine(" Cookie " + m_iCookie); //sb.AppendLine(" Socket type " + m_iType.ToString()); //sb.AppendLine(" Socket id " + m_iID); sb.AppendLine(" Initial seq# " + m_iISN); //sb.AppendLine(" MSS " + m_iMSS); //sb.AppendLine(" Flight size " + m_iFlightFlagSize); return sb.ToString(); } public unsafe void serialize(byte[] buf) { fixed (byte* pb = buf) { int* p = (int*)(pb); *p++ = m_iVersion; *p++ = (int)m_iType; *p++ = m_iISN; *p++ = m_iMSS; *p++ = m_iFlightFlagSize; *p++ = m_iReqType; *p++ = m_iID; *p++ = m_iCookie; for (int i = 0; i < 4; ++i) *p++ = (int)m_piPeerIP[i]; } } public unsafe bool deserialize(byte[] buf, int size) { if (size < m_iContentSize) return false; fixed (byte* pb = buf) { int* p = (int*)(pb); m_iVersion = *p++; m_iType = (SocketType)(*p++); m_iISN = *p++; m_iMSS = *p++; m_iFlightFlagSize = *p++; m_iReqType = *p++; m_iID = *p++; m_iCookie = *p++; for (int i = 0; i < 4; ++i) m_piPeerIP[i] = (uint)*p++; } return true; } } }