using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading; namespace UdtSharp { public class SNode { public UDT m_pUDT; // Pointer to the instance of CUDT socket public ulong m_llTimeStamp; // Time Stamp public int m_iHeapLoc; // location on the heap, -1 means not on the heap }; public class RNode { public UDT m_pUDT; // Pointer to the instance of CUDT socket public ulong m_llTimeStamp; // Time Stamp public bool m_bOnList; // if the node is already on the list }; class UnitQueue { struct QEntry { internal Unit[] m_pUnit; // unit queue internal byte[][] m_pBuffer; // data buffer internal int m_iSize; // size of each queue } List mEntries = new List(); int m_iCurrEntry = 0; int m_iLastEntry = 0; int m_iAvailUnit; // recent available unit int m_iAvailableQueue; int m_iSize; // total size of the unit queue, in number of packets public int m_iCount; // total number of valid packets in the queue int m_iMSS; // unit buffer size AddressFamily m_iIPversion; // IP version UnitQueue() { m_iSize = 0; m_iCount = 0; m_iMSS = 0; m_iIPversion = 0; } ~UnitQueue() { } int init(int size, int mss, AddressFamily version) { QEntry tempq = new QEntry(); Unit[] tempu = new Unit[size]; byte[][] tempb = new byte[size][]; for (int i = 0; i < size; ++i) { tempb[i] = new byte[mss]; tempu[i] = new Unit(); tempu[i].m_iFlag = 0; tempu[i].m_Packet.SetDataFromBytes(tempb[i]); } tempq.m_pUnit = tempu; tempq.m_pBuffer = tempb; tempq.m_iSize = size; m_iSize = size; m_iMSS = mss; m_iIPversion = version; mEntries.Add(tempq); return 0; } int increase() { // adjust/correct m_iCount int real_count = 0; for (int q = 0; q < mEntries.Count; ++q) { Unit[] units = mEntries[q].m_pUnit; for (int u = mEntries[q].m_iSize; u < units.Length; ++u) if (units[u].m_iFlag != 0) ++real_count; } m_iCount = real_count; if ((double)m_iCount / m_iSize < 0.9) return -1; // all queues have the same size int size = mEntries[0].m_iSize; QEntry tempq = new QEntry(); Unit[] tempu = new Unit[size]; byte[][] tempb = new byte[size][]; for (int i = 0; i < size; ++i) { tempb[i] = new byte[m_iMSS]; tempu[i].m_iFlag = 0; tempu[i].m_Packet.SetDataFromBytes(tempb[i]); } tempq.m_pUnit = tempu; tempq.m_pBuffer = tempb; tempq.m_iSize = size; mEntries.Add(tempq); m_iSize += size; return 0; } int shrink() { // currently queue cannot be shrunk. return -1; } Unit getNextAvailUnit() { if (m_iCount * 10 > m_iSize * 9) increase(); if (m_iCount >= m_iSize) return null; QEntry entrance = mEntries[m_iCurrEntry]; //do //{ // QEntry currentEntry = mEntries[m_iCurrEntry]; // Unit sentinel = currentEntry.m_pUnit[currentEntry.m_iSize - 1]; // for (CUnit* sentinel = m_pCurrQueue.m_pUnit + m_pCurrQueue.m_iSize - 1; m_pAvailUnit != sentinel; ++m_pAvailUnit) // if (m_pAvailUnit.m_iFlag == 0) // return m_pAvailUnit; // if (m_pCurrQueue.m_pUnit.m_iFlag == 0) // { // m_pAvailUnit = m_pCurrQueue.m_pUnit; // return m_pAvailUnit; // } // m_pCurrQueue = m_pCurrQueue.m_pNext; // m_pAvailUnit = m_pCurrQueue.m_pUnit; //} while (m_pCurrQueue != entrance); increase(); return null; } } public class SndUList { object m_ListLock = new object(); public object m_pWindowLock; public EventWaitHandle m_pWindowCond; SNode[] m_pHeap; // The heap array int m_iArrayLength; // physical length of the array int m_iLastEntry; // position of last entry on the heap array public Timer m_pTimer; public SndUList() { m_iArrayLength = 4096; m_iLastEntry = -1; m_pHeap = new SNode[m_iArrayLength]; } public void insert(ulong ts, UDT u) { lock (m_ListLock) { // increase the heap array size if necessary if (m_iLastEntry == m_iArrayLength - 1) { Array.Resize(ref m_pHeap, m_iArrayLength * 2); m_iArrayLength *= 2; } insert_(ts, u); } } public void update(UDT u, bool reschedule = true) { lock (m_ListLock) { SNode n = u.m_pSNode; if (n.m_iHeapLoc >= 0) { if (!reschedule) return; if (n.m_iHeapLoc == 0) { n.m_llTimeStamp = 1; m_pTimer.interrupt(); return; } remove_(u); } insert_(1, u); } } public int pop(ref IPEndPoint addr, ref Packet pkt) { lock (m_ListLock) { if (-1 == m_iLastEntry) return -1; // no pop until the next schedulled time ulong ts = Timer.rdtsc(); if (ts < m_pHeap[0].m_llTimeStamp) return -1; UDT u = m_pHeap[0].m_pUDT; remove_(u); if (!u.m_bConnected || u.m_bBroken) return -1; // pack a packet from the socket if (u.packData(pkt, ref ts) <= 0) return -1; addr = u.m_pPeerAddr; // insert a new entry, ts is the next processing time if (ts > 0) insert_(ts, u); return 1; } } public void remove(UDT u) { lock (m_ListLock) { remove_(u); } } public ulong getNextProcTime() { lock (m_ListLock) { if (-1 == m_iLastEntry) return 0; return m_pHeap[0].m_llTimeStamp; } } void insert_(ulong ts, UDT u) { SNode n = u.m_pSNode; // do not insert repeated node if (n.m_iHeapLoc >= 0) return; m_iLastEntry++; m_pHeap[m_iLastEntry] = n; n.m_llTimeStamp = ts; int q = m_iLastEntry; int p = q; while (p != 0) { p = (q - 1) >> 1; if (m_pHeap[p].m_llTimeStamp > m_pHeap[q].m_llTimeStamp) { SNode t = m_pHeap[p]; m_pHeap[p] = m_pHeap[q]; m_pHeap[q] = t; t.m_iHeapLoc = q; q = p; } else break; } n.m_iHeapLoc = q; // an earlier event has been inserted, wake up sending worker if (n.m_iHeapLoc == 0) m_pTimer.interrupt(); // first entry, activate the sending queue if (0 == m_iLastEntry) { m_pWindowCond.Set(); } } void remove_(UDT u) { SNode n = u.m_pSNode; if (n.m_iHeapLoc >= 0) { // remove the node from heap m_pHeap[n.m_iHeapLoc] = m_pHeap[m_iLastEntry]; m_iLastEntry--; m_pHeap[n.m_iHeapLoc].m_iHeapLoc = n.m_iHeapLoc; int q = n.m_iHeapLoc; int p = q * 2 + 1; while (p <= m_iLastEntry) { if ((p + 1 <= m_iLastEntry) && (m_pHeap[p].m_llTimeStamp > m_pHeap[p + 1].m_llTimeStamp)) p++; if (m_pHeap[q].m_llTimeStamp > m_pHeap[p].m_llTimeStamp) { SNode t = m_pHeap[p]; m_pHeap[p] = m_pHeap[q]; m_pHeap[p].m_iHeapLoc = p; m_pHeap[q] = t; m_pHeap[q].m_iHeapLoc = q; q = p; p = q * 2 + 1; } else break; } n.m_iHeapLoc = -1; } // the only event has been deleted, wake up immediately if (0 == m_iLastEntry) m_pTimer.interrupt(); } } public class RendezvousQueue { struct CRL { internal int m_iID; // UDT socket ID (self) internal UDT m_pUDT; // UDT instance internal AddressFamily m_iIPversion; // IP version internal IPEndPoint m_pPeerAddr; // UDT sonnection peer address internal ulong m_ullTTL; // the time that this request expires }; List m_lRendezvousID = new List(); // The sockets currently in rendezvous mode object m_RIDVectorLock = new object(); public void insert(int id, UDT u, AddressFamily ipv, IPEndPoint addr, ulong ttl) { CRL r; r.m_iID = id; r.m_pUDT = u; r.m_iIPversion = ipv; r.m_pPeerAddr = addr; r.m_ullTTL = ttl; lock (m_RIDVectorLock) { m_lRendezvousID.Add(r); } } public void remove(int id) { lock (m_RIDVectorLock) { for (int i = 0; i < m_lRendezvousID.Count; ++i) { if (m_lRendezvousID[i].m_iID == id) { m_lRendezvousID.RemoveAt(i); return; } } } } public UDT retrieve(IPEndPoint addr, ref int id) { lock (m_RIDVectorLock) { foreach (CRL crl in m_lRendezvousID) { if (crl.m_pPeerAddr.Equals(addr) && (id == 0) || (id == crl.m_iID)) { id = crl.m_iID; return crl.m_pUDT; } } return null; } } public void updateConnStatus() { if (m_lRendezvousID.Count == 0) return; lock (m_RIDVectorLock) { foreach (CRL crl in m_lRendezvousID) { // avoid sending too many requests, at most 1 request per 250ms if (Timer.getTime() - (ulong)crl.m_pUDT.m_llLastReqTime > 250000) { //if (Timer.getTime() >= crl.m_ullTTL) //{ // // connection timer expired, acknowledge app via epoll // i->m_pUDT->m_bConnecting = false; // CUDT::s_UDTUnited.m_EPoll.update_events(i->m_iID, i->m_pUDT->m_sPollID, UDT_EPOLL_ERR, true); // continue; //} Packet request = new Packet(); request.pack(crl.m_pUDT.m_ConnReq); // ID = 0, connection request request.SetId(!crl.m_pUDT.m_bRendezvous ? 0 : crl.m_pUDT.m_ConnRes.m_iID); crl.m_pUDT.m_pSndQueue.sendto(crl.m_pPeerAddr, request); crl.m_pUDT.m_llLastReqTime = (long)Timer.getTime(); } } } } } public class SndQueue { public SndUList m_pSndUList; // List of UDT instances for data sending public Channel m_pChannel; // The UDP channel for data sending Timer m_pTimer; // Timing facility object m_WindowLock; EventWaitHandle m_WindowCond; volatile bool m_bClosing; // closing the worker EventWaitHandle m_ExitCond; Thread m_WorkerThread; public SndQueue() { m_WindowLock = new object(); m_WindowCond = new EventWaitHandle(false, EventResetMode.AutoReset); m_ExitCond = new EventWaitHandle(false, EventResetMode.AutoReset); } public void Close() { m_bClosing = true; m_WindowCond.Set(); if (null != m_WorkerThread) m_ExitCond.WaitOne(Timeout.Infinite); m_WindowCond.Close(); m_ExitCond.Close(); } public void init(Channel c, Timer t) { m_pChannel = c; m_pTimer = t; m_pSndUList = new SndUList(); m_pSndUList.m_pWindowLock = m_WindowLock; m_pSndUList.m_pWindowCond = m_WindowCond; m_pSndUList.m_pTimer = m_pTimer; m_WorkerThread = new Thread(worker); m_WorkerThread.IsBackground = true; m_WorkerThread.Start(this); } static void worker(object param) { SndQueue self = param as SndQueue; if (self == null) return; while (!self.m_bClosing) { ulong ts = self.m_pSndUList.getNextProcTime(); if (ts > 0) { // wait until next processing time of the first socket on the list ulong currtime = Timer.rdtsc(); if (currtime < ts) self.m_pTimer.sleepto(ts); // it is time to send the next pkt IPEndPoint addr = null; Packet pkt = new Packet(); if (self.m_pSndUList.pop(ref addr, ref pkt) < 0) continue; self.m_pChannel.sendto(addr, pkt); } else { // wait here if there is no sockets with data to be sent self.m_WindowCond.WaitOne(Timeout.Infinite); } } self.m_ExitCond.Set(); } public int sendto(IPEndPoint addr, Packet packet) { // send out the packet immediately (high priority), this is a control packet m_pChannel.sendto(addr, packet); return packet.getLength(); } } public class RcvUList { public List m_nodeList = new List(); public void insert(UDT u) { RNode n = u.m_pRNode; n.m_llTimeStamp = Timer.rdtsc(); // always insert at the end for RcvUList m_nodeList.Add(n); } public void remove(UDT u) { RNode n = u.m_pRNode; if (!n.m_bOnList) return; m_nodeList.Remove(n); } public void update(UDT u) { RNode n = u.m_pRNode; if (!n.m_bOnList) return; RNode match = m_nodeList.Find(x => x.Equals(n)); if (match.Equals(default(RNode))) return; match.m_llTimeStamp = Timer.rdtsc(); } } public class RcvQueue { RcvUList m_pRcvUList = new RcvUList(); // List of UDT instances that will read packets from the queue Channel m_pChannel; // UDP channel for receving packets Timer m_pTimer; // shared timer with the snd queue int m_iPayloadSize; // packet payload size volatile bool m_bClosing; // closing the workder EventWaitHandle m_ExitCond; object m_LSLock; UDT m_pListener; // pointer to the (unique, if any) listening UDT entity RendezvousQueue m_pRendezvousQueue = new RendezvousQueue(); // The list of sockets in rendezvous mode List m_vNewEntry = new List(); // newly added entries, to be inserted object m_IDLock; Dictionary> m_mBuffer = new Dictionary>(); // temporary buffer for rendezvous connection request object m_PassLock; EventWaitHandle m_PassCond; Thread m_WorkerThread; Dictionary m_hash = new Dictionary(); public RcvQueue() { m_PassLock = new object(); m_PassCond = new EventWaitHandle(false, EventResetMode.AutoReset); m_LSLock = new object(); m_IDLock = new object(); m_ExitCond = new EventWaitHandle(false, EventResetMode.AutoReset); } public void Close() { m_bClosing = true; if (null != m_WorkerThread) m_ExitCond.WaitOne(Timeout.Infinite); m_PassCond.Close(); m_ExitCond.Close(); } public void init(int qsize, int payload, AddressFamily version, int hsize, Channel cc, Timer t) { m_iPayloadSize = payload; m_pChannel = cc; m_pTimer = t; m_WorkerThread = new Thread(worker); m_WorkerThread.IsBackground = true; m_WorkerThread.Start(this); } static void worker(object param) { RcvQueue self = param as RcvQueue; if (self == null) return; IPEndPoint addr = new IPEndPoint(IPAddress.Any, 0); UDT u = null; int id; while (!self.m_bClosing) { self.m_pTimer.tick(); // check waiting list, if new socket, insert it to the list while (self.ifNewEntry()) { UDT ne = self.getNewEntry(); if (null != ne) { self.m_pRcvUList.insert(ne); self.m_hash.Add(ne.m_SocketID, ne); } } // find next available slot for incoming packet Unit unit = new Unit(); unit.m_Packet.setLength(self.m_iPayloadSize); // reading next incoming packet, recvfrom returns -1 is nothing has been received if (self.m_pChannel.recvfrom(ref addr, unit.m_Packet) < 0) goto TIMER_CHECK; id = unit.m_Packet.GetId(); // ID 0 is for connection request, which should be passed to the listening socket or rendezvous sockets if (0 == id) { if (null != self.m_pListener) self.m_pListener.listen(addr, unit.m_Packet); else if (null != (u = self.m_pRendezvousQueue.retrieve(addr, ref id))) { // asynchronous connect: call connect here // otherwise wait for the UDT socket to retrieve this packet if (!u.m_bSynRecving) u.connect(unit.m_Packet); else { Packet newPacket = new Packet(); newPacket.Clone(unit.m_Packet); self.storePkt(id, newPacket); } } } else if (id > 0) { if (self.m_hash.TryGetValue(id, out u)) { if (addr.Equals(u.m_pPeerAddr)) { if (u.m_bConnected && !u.m_bBroken && !u.m_bClosing) { if (0 == unit.m_Packet.getFlag()) u.processData(unit); else u.processCtrl(unit.m_Packet); u.checkTimers(); self.m_pRcvUList.update(u); } } } else if (null != (u = self.m_pRendezvousQueue.retrieve(addr, ref id))) { if (!u.m_bSynRecving) u.connect(unit.m_Packet); else { Packet newPacket = new Packet(); newPacket.Clone(unit.m_Packet); self.storePkt(id, newPacket); } } } TIMER_CHECK: // take care of the timing event for all UDT sockets ulong currtime = Timer.rdtsc(); ulong ctime = currtime - 100000 * Timer.getCPUFrequency(); for (int i = 0; i < self.m_pRcvUList.m_nodeList.Count; ++i) { RNode ul = self.m_pRcvUList.m_nodeList[0]; if (ul.m_llTimeStamp >= ctime) break; u = ul.m_pUDT; if (u.m_bConnected && !u.m_bBroken && !u.m_bClosing) { u.checkTimers(); self.m_pRcvUList.update(u); } else { // the socket must be removed from Hash table first, then RcvUList self.m_hash.Remove(u.m_SocketID); self.m_pRcvUList.remove(u); u.m_pRNode.m_bOnList = false; } } // Check connection requests status for all sockets in the RendezvousQueue. self.m_pRendezvousQueue.updateConnStatus(); } self.m_ExitCond.Set(); } public int recvfrom(int id, Packet packet) { bool gotLock = false; Monitor.Enter(m_PassLock, ref gotLock); Queue packetQueue; if (!m_mBuffer.TryGetValue(id, out packetQueue)) { if (gotLock) Monitor.Exit(m_PassLock); m_PassCond.WaitOne(1000); lock (m_PassLock) { if (!m_mBuffer.TryGetValue(id, out packetQueue)) { packet.setLength(-1); return -1; } } } if (gotLock && Monitor.IsEntered(m_PassLock)) Monitor.Exit(m_PassLock); // retrieve the earliest packet Packet newpkt = packetQueue.Peek(); if (packet.getLength() < newpkt.getLength()) { packet.setLength(-1); return -1; } // copy packet content packet.Clone(newpkt); packetQueue.Dequeue(); if (packetQueue.Count == 0) { lock (m_PassLock) { m_mBuffer.Remove(id); } } return packet.getLength(); } public int setListener(UDT u) { lock (m_LSLock) { if (null != m_pListener) return -1; m_pListener = u; return 0; } } public void removeListener(UDT u) { lock (m_LSLock) { if (u == m_pListener) m_pListener = null; } } public void registerConnector(int id, UDT u, AddressFamily ipv, IPEndPoint addr, ulong ttl) { m_pRendezvousQueue.insert(id, u, ipv, addr, ttl); } public void removeConnector(int id) { m_pRendezvousQueue.remove(id); lock (m_PassLock) { m_mBuffer.Remove(id); } } public void setNewEntry(UDT u) { lock (m_IDLock) { m_vNewEntry.Add(u); } } bool ifNewEntry() { return !(m_vNewEntry.Count == 0); } UDT getNewEntry() { lock (m_IDLock) { if (m_vNewEntry.Count == 0) return null; UDT u = m_vNewEntry[0]; m_vNewEntry.RemoveAt(0); return u; } } void storePkt(int id, Packet pkt) { lock (m_PassLock) { Queue packetQueue; if (!m_mBuffer.TryGetValue(id, out packetQueue)) { packetQueue = new Queue(); packetQueue.Enqueue(pkt); m_mBuffer.Add(id, packetQueue); m_PassCond.Set(); } else { //avoid storing too many packets, in case of malfunction or attack if (packetQueue.Count > 16) return; packetQueue.Enqueue(pkt); } } } } }