Files
2025-08-19 12:45:50 +02:00

904 lines
26 KiB
C#

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<QEntry> mEntries = new List<QEntry>();
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<CRL> m_lRendezvousID = new List<CRL>(); // 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<RNode> m_nodeList = new List<RNode>();
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<UDT> m_vNewEntry = new List<UDT>(); // newly added entries, to be inserted
object m_IDLock;
Dictionary<int, Queue<Packet>> m_mBuffer = new Dictionary<int, Queue<Packet>>(); // temporary buffer for rendezvous connection request
object m_PassLock;
EventWaitHandle m_PassCond;
Thread m_WorkerThread;
Dictionary<int, UDT> m_hash = new Dictionary<int, UDT>();
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<Packet> 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<Packet> packetQueue;
if (!m_mBuffer.TryGetValue(id, out packetQueue))
{
packetQueue = new Queue<Packet>();
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);
}
}
}
}
}