using System; using System.Collections.Generic; namespace UdtSharp { public class SndBuffer { object m_BufLock = new object(); // used to synchronize buffer operation class Block { internal byte[] m_pcData; // pointer to the data block internal int m_iLength; // length of the block internal uint m_iMsgNo; // message number internal ulong m_OriginTime; // original request time internal int m_iTTL; // time to live (milliseconds) } List mBlockList = new List(); int m_iLastBlock = 0; int m_iCurrentBlock = 0; int m_iFirstBlock = 0; uint m_iNextMsgNo; // next message number int m_iSize; // buffer size (number of packets) int m_iMSS; // maximum seqment/packet size int m_iCount; // number of used blocks public SndBuffer(int size, int mss) { m_iSize = size; m_iMSS = mss; // circular linked list for out bound packets for (int i = 0; i < m_iSize; ++i) { Block block = new Block(); block.m_iMsgNo = 0; block.m_pcData = new byte[m_iMSS]; mBlockList.Add(block); } } // Functionality: // Insert a user buffer into the sending list. // Parameters: // 0) [in] data: pointer to the user data block. // 1) [in] len: size of the block. // 2) [in] ttl: time to live in milliseconds // 3) [in] order: if the block should be delivered in order, for DGRAM only // Returned value: // None. public void addBuffer(byte[] data, int offset, int len, int ttl = -1, bool order = false) { int size = len / m_iMSS; if ((len % m_iMSS) != 0) size++; // dynamically increase sender buffer while (size + m_iCount >= m_iSize) increase(); ulong time = Timer.getTime(); uint inorder = Convert.ToUInt32(order); inorder <<= 29; for (int i = 0; i < size; ++i) { Block s = mBlockList[m_iLastBlock]; IncrementBlockIndex(ref m_iLastBlock); int pktlen = len - i * m_iMSS; if (pktlen > m_iMSS) pktlen = m_iMSS; Array.Copy(data, i * m_iMSS + offset, s.m_pcData, 0, pktlen); s.m_iLength = pktlen; s.m_iMsgNo = m_iNextMsgNo | inorder; if (i == 0) s.m_iMsgNo |= 0x80000000; if (i == size - 1) s.m_iMsgNo |= 0x40000000; s.m_OriginTime = time; s.m_iTTL = ttl; } lock (m_BufLock) { m_iCount += size; } m_iNextMsgNo++; if (m_iNextMsgNo == MessageNumber.m_iMaxMsgNo) m_iNextMsgNo = 1; } public int readData(ref byte[] data, ref uint msgno) { // No data to read if (m_iCurrentBlock == m_iLastBlock) return 0; data = mBlockList[m_iCurrentBlock].m_pcData; int readlen = mBlockList[m_iCurrentBlock].m_iLength; msgno = mBlockList[m_iCurrentBlock].m_iMsgNo; IncrementBlockIndex(ref m_iCurrentBlock); return readlen; } public int readData(ref byte[] data, int offset, ref uint msgno, out int msglen) { msglen = 0; lock (m_BufLock) { int blockIndex = m_iFirstBlock; IncrementBlockIndex(ref blockIndex, offset); Block p = mBlockList[blockIndex]; if ((p.m_iTTL >= 0) && ((Timer.getTime() - p.m_OriginTime) / 1000 > (ulong)p.m_iTTL)) { msgno = p.m_iMsgNo & 0x1FFFFFFF; msglen = 1; IncrementBlockIndex(ref blockIndex); p = mBlockList[blockIndex]; bool move = false; while (msgno == (p.m_iMsgNo & 0x1FFFFFFF)) { if (blockIndex == m_iCurrentBlock) move = true; IncrementBlockIndex(ref blockIndex); p = mBlockList[blockIndex]; if (move) m_iCurrentBlock = blockIndex; msglen++; } return -1; } data = p.m_pcData; int readlen = p.m_iLength; msgno = p.m_iMsgNo; return readlen; } } void IncrementBlockIndex(ref int blockIndex, int offset = 1) { blockIndex = (blockIndex + offset) % mBlockList.Count; } public void ackData(int offset) { lock (m_BufLock) { IncrementBlockIndex(ref m_iFirstBlock, offset); m_iCount -= offset; Timer.triggerEvent(); } } public int getCurrBufSize() { return m_iCount; } void increase() { int unitsize = m_iSize; for (int i = 0; i < unitsize; ++i) { Block block = new Block(); block.m_iMsgNo = 0; block.m_pcData = new byte[m_iMSS]; mBlockList.Add(block); } m_iSize += unitsize; } } public class RcvBuffer { Unit[] m_pUnit; // pointer to the protocol buffer int m_iSize; // size of the protocol buffer int m_iStartPos; // the head position for I/O (inclusive) int m_iLastAckPos; // the last ACKed position (exclusive) // EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1 int m_iMaxPos; // the furthest data position int m_iNotch; // the starting read point of the first unit public RcvBuffer(int bufsize) { m_iSize = bufsize; m_iStartPos = 0; m_iLastAckPos = 0; m_iMaxPos = 0; m_iNotch = 0; m_pUnit = new Unit[m_iSize]; for (int i = 0; i < m_iSize; ++i) m_pUnit[i] = null; } ~RcvBuffer() { for (int i = 0; i < m_iSize; ++i) { if (null != m_pUnit[i]) { m_pUnit[i].m_iFlag = 0; } } } public int addData(Unit unit, int offset) { int pos = (m_iLastAckPos + offset) % m_iSize; if (offset > m_iMaxPos) m_iMaxPos = offset; if (null != m_pUnit[pos]) return -1; m_pUnit[pos] = unit; unit.m_iFlag = 1; return 0; } public int readBuffer(byte[] data, int offset, int len) { int p = m_iStartPos; int lastack = m_iLastAckPos; int rs = len; while ((p != lastack) && (rs > 0)) { int unitsize = m_pUnit[p].m_Packet.getLength() - m_iNotch; if (unitsize > rs) unitsize = rs; unitsize = m_pUnit[p].m_Packet.GetDataBytes(m_iNotch, data, offset, unitsize); offset += unitsize; if ((rs > unitsize) || (rs == m_pUnit[p].m_Packet.getLength() - m_iNotch)) { Unit tmp = m_pUnit[p]; m_pUnit[p] = null; tmp.m_iFlag = 0; if (++p == m_iSize) p = 0; m_iNotch = 0; } else m_iNotch += rs; rs -= unitsize; } m_iStartPos = p; return len - rs; } public void ackData(int len) { m_iLastAckPos = (m_iLastAckPos + len) % m_iSize; m_iMaxPos -= len; if (m_iMaxPos < 0) m_iMaxPos = 0; Timer.triggerEvent(); } public int getAvailBufSize() { // One slot must be empty in order to tell the difference between "empty buffer" and "full buffer" return m_iSize - getRcvDataSize() - 1; } public int getRcvDataSize() { if (m_iLastAckPos >= m_iStartPos) return m_iLastAckPos - m_iStartPos; return m_iSize + m_iLastAckPos - m_iStartPos; } public void dropMsg(int msgno) { for (int i = m_iStartPos, n = (m_iLastAckPos + m_iMaxPos) % m_iSize; i != n; i = (i + 1) % m_iSize) if ((null != m_pUnit[i]) && (msgno == m_pUnit[i].m_Packet.GetMessageNumber())) m_pUnit[i].m_iFlag = 3; } public int readMsg(byte[] data, int len) { int p = 0; int q = 0; bool passack = false; if (!scanMsg(ref p, ref q, ref passack)) return 0; int rs = len; int dataOffset = 0; while (p != (q + 1) % m_iSize) { byte[] allData = m_pUnit[p].m_Packet.GetDataBytes(); int unitsize = allData.Length; if ((rs >= 0) && (unitsize > rs)) unitsize = rs; if (unitsize > 0) { Array.Copy(allData, 0, data, dataOffset, unitsize); dataOffset += unitsize; rs -= unitsize; } if (!passack) { Unit tmp = m_pUnit[p]; m_pUnit[p] = null; tmp.m_iFlag = 0; } else m_pUnit[p].m_iFlag = 2; if (++p == m_iSize) p = 0; } if (!passack) m_iStartPos = (q + 1) % m_iSize; return len - rs; } int getRcvMsgNum() { int p = 0; int q = 0; bool passack = false; return scanMsg(ref p, ref q, ref passack) ? 1 : 0; } bool scanMsg(ref int p, ref int q, ref bool passack) { // empty buffer if ((m_iStartPos == m_iLastAckPos) && (m_iMaxPos <= 0)) return false; //skip all bad msgs at the beginning while (m_iStartPos != m_iLastAckPos) { if (null == m_pUnit[m_iStartPos]) { if (++m_iStartPos == m_iSize) m_iStartPos = 0; continue; } if ((1 == m_pUnit[m_iStartPos].m_iFlag) && (m_pUnit[m_iStartPos].m_Packet.getMsgBoundary() > 1)) { bool good = true; // look ahead for the whole message for (int i = m_iStartPos; i != m_iLastAckPos;) { if ((null == m_pUnit[i]) || (1 != m_pUnit[i].m_iFlag)) { good = false; break; } if ((m_pUnit[i].m_Packet.getMsgBoundary() == 1) || (m_pUnit[i].m_Packet.getMsgBoundary() == 3)) break; if (++i == m_iSize) i = 0; } if (good) break; } Unit tmp = m_pUnit[m_iStartPos]; m_pUnit[m_iStartPos] = null; tmp.m_iFlag = 0; if (++m_iStartPos == m_iSize) m_iStartPos = 0; } p = -1; // message head q = m_iStartPos; // message tail passack = m_iStartPos == m_iLastAckPos; bool found = false; // looking for the first message for (int i = 0, n = m_iMaxPos + getRcvDataSize(); i <= n; ++i) { if ((null != m_pUnit[q]) && (1 == m_pUnit[q].m_iFlag)) { switch (m_pUnit[q].m_Packet.getMsgBoundary()) { case 3: // 11 p = q; found = true; break; case 2: // 10 p = q; break; case 1: // 01 if (p != -1) found = true; break; } } else { // a hole in this message, not valid, restart search p = -1; } if (found) { // the msg has to be ack'ed or it is allowed to read out of order, and was not read before if (!passack || !m_pUnit[q].m_Packet.getMsgOrderFlag()) break; found = false; } if (++q == m_iSize) q = 0; if (q == m_iLastAckPos) passack = true; } // no msg found if (!found) { // if the message is larger than the receiver buffer, return part of the message if ((p != -1) && ((q + 1) % m_iSize == p)) found = true; } return found; } } }