961 lines
33 KiB
C#
961 lines
33 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
|
|
using UDTSOCKET = System.Int32;
|
|
|
|
namespace UdtSharp
|
|
{
|
|
class Multiplexer
|
|
{
|
|
internal SndQueue m_pSndQueue; // The sending queue
|
|
internal RcvQueue m_pRcvQueue; // The receiving queue
|
|
internal Channel m_pChannel; // The UDP channel for sending and receiving
|
|
internal Timer m_pTimer; // The timer
|
|
|
|
internal int m_iPort; // The UDP port number of this multiplexer
|
|
internal AddressFamily m_iIPversion; // IP version
|
|
internal int m_iMSS; // Maximum Segment Size
|
|
internal int m_iRefCount; // number of UDT instances that are associated with this multiplexer
|
|
internal bool m_bReusable; // if this one can be shared with others
|
|
|
|
internal int m_iID; // multiplexer ID
|
|
}
|
|
|
|
internal class UdtSocketInternal
|
|
{
|
|
public UDTSTATUS m_Status; // current socket state
|
|
|
|
public ulong m_TimeStamp; // time when the socket is closed
|
|
|
|
public AddressFamily m_iIPversion; // IP version
|
|
public IPEndPoint m_pSelfAddr; // pointer to the local address of the socket
|
|
public IPEndPoint m_pPeerAddr; // pointer to the peer address of the socket
|
|
|
|
public UDTSOCKET m_SocketID; // socket ID
|
|
public UDTSOCKET m_ListenSocket; // ID of the listener socket; 0 means this is an independent socket
|
|
|
|
public UDTSOCKET m_PeerID; // peer socket ID
|
|
public int m_iISN; // initial sequence number, used to tell different connection from same IP:port
|
|
|
|
public UDT m_pUDT; // pointer to the UDT entity
|
|
|
|
public HashSet<UDTSOCKET> m_pQueuedSockets; // set of connections waiting for accept()
|
|
public HashSet<UDTSOCKET> m_pAcceptSockets; // set of accept()ed connections
|
|
|
|
public EventWaitHandle m_AcceptCond = new EventWaitHandle(false, EventResetMode.AutoReset);// used to block "accept" call
|
|
public object m_AcceptLock = new object(); // mutex associated to m_AcceptCond
|
|
|
|
public uint m_uiBackLog; // maximum number of connections in queue
|
|
|
|
public int m_iMuxID; // multiplexer ID
|
|
|
|
public object m_ControlLock = new object(); // lock this socket exclusively for control APIs: bind/listen/connect
|
|
|
|
public UdtSocketInternal()
|
|
{
|
|
m_Status = UDTSTATUS.INIT;
|
|
m_iMuxID = -1;
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
m_AcceptCond.Close();
|
|
}
|
|
}
|
|
|
|
public class UdtUnited
|
|
{
|
|
Dictionary<UDTSOCKET, UdtSocketInternal> m_Sockets = new Dictionary<UDTSOCKET, UdtSocketInternal>(); // stores all the socket structures
|
|
|
|
object m_ControlLock = new object(); // used to synchronize UDT API
|
|
|
|
object m_IDLock = new object(); // used to synchronize ID generation
|
|
UDTSOCKET m_SocketID; // seed to generate a new unique socket ID
|
|
|
|
Dictionary<long, HashSet<UDTSOCKET>> m_PeerRec = new Dictionary<long, HashSet<UDTSOCKET>>();// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn
|
|
|
|
//pthread_key_t m_TLSError; // thread local error record (last error)
|
|
Dictionary<uint, UdtException> m_mTLSRecord;
|
|
object m_TLSLock = new object();
|
|
|
|
Dictionary<int, Multiplexer> m_mMultiplexer = new Dictionary<UDTSOCKET, Multiplexer>(); // UDP multiplexer
|
|
object m_MultiplexerLock = new object();
|
|
|
|
HashSet<InfoBlock> m_pCache = new HashSet<InfoBlock>(); // UDT network information cache
|
|
|
|
volatile bool m_bClosing;
|
|
object m_GCStopLock = new object();
|
|
EventWaitHandle m_GCStopCond = new EventWaitHandle(false, EventResetMode.AutoReset);
|
|
|
|
object m_InitLock = new object();
|
|
int m_iInstanceCount; // number of startup() called by application
|
|
|
|
Dictionary<UDTSOCKET, UdtSocketInternal> m_ClosedSockets = new Dictionary<UDTSOCKET, UdtSocketInternal>(); // temporarily store closed sockets
|
|
|
|
static Random m_random = new Random();
|
|
|
|
public UdtUnited()
|
|
{
|
|
// Socket ID MUST start from a random value
|
|
m_SocketID = 1 + (int)((1 << 30) * m_random.NextDouble());
|
|
|
|
//m_TLSError = TlsAlloc();
|
|
}
|
|
|
|
~UdtUnited()
|
|
{
|
|
//TlsFree(m_TLSError);
|
|
}
|
|
|
|
public int startup()
|
|
{
|
|
lock (m_InitLock)
|
|
{
|
|
++m_iInstanceCount;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public int cleanup()
|
|
{
|
|
lock (m_InitLock)
|
|
{
|
|
if (--m_iInstanceCount > 0)
|
|
return 0;
|
|
}
|
|
|
|
m_bClosing = true;
|
|
return 0;
|
|
}
|
|
|
|
public UDTSOCKET newSocket(AddressFamily af, SocketType type)
|
|
{
|
|
if ((type != SocketType.Stream) && (type != SocketType.Dgram))
|
|
throw new UdtException(5, 3, 0);
|
|
|
|
UdtSocketInternal ns = new UdtSocketInternal();
|
|
ns.m_pUDT = new UDT();
|
|
ns.m_pSelfAddr = new IPEndPoint(IPAddress.Any, 0);
|
|
|
|
lock (m_IDLock)
|
|
{
|
|
ns.m_SocketID = --m_SocketID;
|
|
}
|
|
|
|
ns.m_Status = UDTSTATUS.INIT;
|
|
ns.m_ListenSocket = 0;
|
|
ns.m_pUDT.m_SocketID = ns.m_SocketID;
|
|
ns.m_pUDT.m_iSockType = type;
|
|
ns.m_pUDT.m_iIPversion = af;
|
|
ns.m_pUDT.m_pCache = m_pCache;
|
|
|
|
// protect the m_Sockets structure.
|
|
lock (m_ControlLock)
|
|
{
|
|
m_Sockets[ns.m_SocketID] = ns;
|
|
}
|
|
|
|
return ns.m_SocketID;
|
|
}
|
|
|
|
public int newConnection(UDTSOCKET listen, IPEndPoint peer, Handshake hs)
|
|
{
|
|
UdtSocketInternal ns = null;
|
|
UdtSocketInternal ls = locate(listen);
|
|
|
|
if (null == ls)
|
|
return -1;
|
|
|
|
// if this connection has already been processed
|
|
if (null != (ns = locate(peer, hs.m_iID, hs.m_iISN)))
|
|
{
|
|
if (ns.m_pUDT.m_bBroken)
|
|
{
|
|
// last connection from the "peer" address has been broken
|
|
ns.m_Status = UDTSTATUS.CLOSED;
|
|
ns.m_TimeStamp = Timer.getTime();
|
|
|
|
lock (ls.m_AcceptLock)
|
|
{
|
|
ls.m_pQueuedSockets.Remove(ns.m_SocketID);
|
|
ls.m_pAcceptSockets.Remove(ns.m_SocketID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// connection already exist, this is a repeated connection request
|
|
// respond with existing HS information
|
|
|
|
hs.m_iISN = ns.m_pUDT.m_iISN;
|
|
hs.m_iMSS = ns.m_pUDT.m_iMSS;
|
|
hs.m_iFlightFlagSize = ns.m_pUDT.m_iFlightFlagSize;
|
|
hs.m_iReqType = -1;
|
|
hs.m_iID = ns.m_SocketID;
|
|
|
|
return 0;
|
|
|
|
//except for this situation a new connection should be started
|
|
}
|
|
}
|
|
|
|
// exceeding backlog, refuse the connection request
|
|
if (ls.m_pQueuedSockets.Count >= ls.m_uiBackLog)
|
|
return -1;
|
|
|
|
ns = new UdtSocketInternal();
|
|
ns.m_pUDT = new UDT(ls.m_pUDT);
|
|
ns.m_pSelfAddr = new IPEndPoint(IPAddress.Any, 0);
|
|
ns.m_pPeerAddr = peer;
|
|
|
|
lock (m_IDLock)
|
|
{
|
|
ns.m_SocketID = --m_SocketID;
|
|
}
|
|
|
|
ns.m_ListenSocket = listen;
|
|
ns.m_iIPversion = ls.m_iIPversion;
|
|
ns.m_pUDT.m_SocketID = ns.m_SocketID;
|
|
ns.m_PeerID = hs.m_iID;
|
|
ns.m_iISN = hs.m_iISN;
|
|
|
|
int error = 0;
|
|
|
|
try
|
|
{
|
|
// bind to the same addr of listening socket
|
|
ns.m_pUDT.open();
|
|
updateMux(ns, ls);
|
|
ns.m_pUDT.connect(peer, hs);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
error = 1;
|
|
goto ERR_ROLLBACK;
|
|
}
|
|
|
|
ns.m_Status = UDTSTATUS.CONNECTED;
|
|
|
|
// copy address information of local node
|
|
ns.m_pUDT.m_pSndQueue.m_pChannel.getSockAddr(ref ns.m_pSelfAddr);
|
|
ConvertIPAddress.ToUintArray(ns.m_pSelfAddr.Address, ref ns.m_pUDT.m_piSelfIP);
|
|
|
|
// protect the m_Sockets structure.
|
|
lock (m_ControlLock)
|
|
{
|
|
m_Sockets[ns.m_SocketID] = ns;
|
|
HashSet<int> sockets;
|
|
if (!m_PeerRec.TryGetValue((ns.m_PeerID << 30) + ns.m_iISN, out sockets))
|
|
{
|
|
sockets = new HashSet<int>();
|
|
m_PeerRec.Add((ns.m_PeerID << 30) + ns.m_iISN, sockets);
|
|
}
|
|
|
|
sockets.Add(ns.m_SocketID);
|
|
}
|
|
|
|
lock (ls.m_AcceptLock)
|
|
{
|
|
ls.m_pQueuedSockets.Add(ns.m_SocketID);
|
|
}
|
|
|
|
// acknowledge users waiting for new connections on the listening socket
|
|
//m_EPoll.update_events(listen, ls.m_pUDT.m_sPollID, UDT_EPOLL_IN, true);
|
|
|
|
Timer.triggerEvent();
|
|
|
|
ERR_ROLLBACK:
|
|
if (error > 0)
|
|
{
|
|
ns.m_pUDT.close();
|
|
ns.m_Status = UDTSTATUS.CLOSED;
|
|
ns.m_TimeStamp = Timer.getTime();
|
|
|
|
return -1;
|
|
}
|
|
|
|
// wake up a waiting accept() call
|
|
ls.m_AcceptCond.Set();
|
|
|
|
return 1;
|
|
}
|
|
|
|
public UDT lookup(UDTSOCKET u)
|
|
{
|
|
// protects the m_Sockets structure
|
|
lock (m_ControlLock)
|
|
{
|
|
UdtSocketInternal socket;
|
|
if (!m_Sockets.TryGetValue(u, out socket) || socket.m_Status == UDTSTATUS.CLOSED)
|
|
throw new UdtException(5, 4, 0);
|
|
|
|
return socket.m_pUDT;
|
|
}
|
|
}
|
|
|
|
public UDTSTATUS getStatus(UDTSOCKET u)
|
|
{
|
|
// protects the m_Sockets structure
|
|
lock (m_ControlLock)
|
|
{
|
|
UdtSocketInternal socket;
|
|
if (m_Sockets.TryGetValue(u, out socket))
|
|
{
|
|
if (socket.m_pUDT.m_bBroken)
|
|
return UDTSTATUS.BROKEN;
|
|
|
|
return socket.m_Status;
|
|
}
|
|
|
|
if (m_ClosedSockets.ContainsKey(u))
|
|
return UDTSTATUS.CLOSED;
|
|
|
|
return UDTSTATUS.NONEXIST;
|
|
}
|
|
}
|
|
|
|
public int bind(UDTSOCKET u, IPEndPoint name)
|
|
{
|
|
UdtSocketInternal s = locate(u);
|
|
if (null == s)
|
|
throw new UdtException(5, 4, 0);
|
|
|
|
lock (s.m_ControlLock)
|
|
{
|
|
// cannot bind a socket more than once
|
|
if (UDTSTATUS.INIT != s.m_Status)
|
|
throw new UdtException(5, 0, 0);
|
|
|
|
s.m_pUDT.open();
|
|
updateMux(s, name);
|
|
s.m_Status = UDTSTATUS.OPENED;
|
|
|
|
// copy address information of local node
|
|
s.m_pUDT.m_pSndQueue.m_pChannel.getSockAddr(ref s.m_pSelfAddr);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
public int bind(UDTSOCKET u, Socket udpsock)
|
|
{
|
|
UdtSocketInternal s = locate(u);
|
|
if (null == s)
|
|
throw new UdtException(5, 4, 0);
|
|
|
|
lock (s.m_ControlLock)
|
|
{
|
|
|
|
// cannot bind a socket more than once
|
|
if (UDTSTATUS.INIT != s.m_Status)
|
|
throw new UdtException(5, 0, 0);
|
|
|
|
IPEndPoint name = null;
|
|
s.m_pUDT.m_pSndQueue.m_pChannel.getSockAddr(ref name); //TODO CHECK THIS
|
|
|
|
s.m_pUDT.open();
|
|
updateMux(s, name, udpsock);
|
|
s.m_Status = UDTSTATUS.OPENED;
|
|
|
|
// copy address information of local node
|
|
s.m_pUDT.m_pSndQueue.m_pChannel.getSockAddr(ref s.m_pSelfAddr);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public int listen(UDTSOCKET u, int backlog)
|
|
{
|
|
UdtSocketInternal s = locate(u);
|
|
if (null == s)
|
|
throw new UdtException(5, 4, 0);
|
|
|
|
lock (s.m_ControlLock)
|
|
{
|
|
|
|
// do nothing if the socket is already listening
|
|
if (UDTSTATUS.LISTENING == s.m_Status)
|
|
return 0;
|
|
|
|
// a socket can listen only if is in UDTSTATUS.OPENED status
|
|
if (UDTSTATUS.OPENED != s.m_Status)
|
|
throw new UdtException(5, 5, 0);
|
|
|
|
// listen is not supported in rendezvous connection setup
|
|
if (s.m_pUDT.m_bRendezvous)
|
|
throw new UdtException(5, 7, 0);
|
|
|
|
if (backlog <= 0)
|
|
throw new UdtException(5, 3, 0);
|
|
|
|
s.m_uiBackLog = (uint)backlog;
|
|
|
|
s.m_pQueuedSockets = new HashSet<UDTSOCKET>();
|
|
s.m_pAcceptSockets = new HashSet<UDTSOCKET>();
|
|
|
|
s.m_pUDT.listen();
|
|
|
|
s.m_Status = UDTSTATUS.LISTENING;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public UDTSOCKET accept(UDTSOCKET listen, ref IPEndPoint addr)
|
|
{
|
|
if (null != addr)
|
|
throw new UdtException(5, 3, 0);
|
|
|
|
UdtSocketInternal ls = locate(listen);
|
|
|
|
if (ls == null)
|
|
throw new UdtException(5, 4, 0);
|
|
|
|
// the "listen" socket must be in UDTSTATUS.LISTENING status
|
|
if (UDTSTATUS.LISTENING != ls.m_Status)
|
|
throw new UdtException(5, 6, 0);
|
|
|
|
// no "accept" in rendezvous connection setup
|
|
if (ls.m_pUDT.m_bRendezvous)
|
|
throw new UdtException(5, 7, 0);
|
|
|
|
UDTSOCKET u = UDT.INVALID_SOCK;
|
|
bool accepted = false;
|
|
|
|
// !!only one conection can be set up each time!!
|
|
while (!accepted)
|
|
{
|
|
lock (ls.m_AcceptLock)
|
|
{
|
|
if (ls.m_pQueuedSockets.Count > 0)
|
|
{
|
|
HashSet<UDTSOCKET>.Enumerator e = ls.m_pQueuedSockets.GetEnumerator();
|
|
e.MoveNext();
|
|
u = e.Current;
|
|
ls.m_pAcceptSockets.Add(u);
|
|
ls.m_pQueuedSockets.Remove(u);
|
|
|
|
accepted = true;
|
|
}
|
|
else if (!ls.m_pUDT.m_bSynRecving)
|
|
accepted = true;
|
|
}
|
|
|
|
if (!accepted & (UDTSTATUS.LISTENING == ls.m_Status))
|
|
ls.m_AcceptCond.WaitOne(Timeout.Infinite);
|
|
|
|
if ((UDTSTATUS.LISTENING != ls.m_Status) || ls.m_pUDT.m_bBroken)
|
|
{
|
|
// Send signal to other threads that are waiting to accept.
|
|
ls.m_AcceptCond.Set();
|
|
accepted = true;
|
|
}
|
|
|
|
//if (ls.m_pQueuedSockets.Count == 0)
|
|
// m_EPoll.update_events(listen, ls.m_pUDT.m_sPollID, UDT_EPOLL_IN, false);
|
|
}
|
|
|
|
if (u == UDT.INVALID_SOCK)
|
|
{
|
|
// non-blocking receiving, no connection available
|
|
if (!ls.m_pUDT.m_bSynRecving)
|
|
throw new UdtException(6, 2, 0);
|
|
|
|
// listening socket is closed
|
|
throw new UdtException(5, 6, 0);
|
|
}
|
|
|
|
addr = locate(u).m_pPeerAddr;
|
|
|
|
return u;
|
|
}
|
|
|
|
public int connect(UDTSOCKET u, IPEndPoint name)
|
|
{
|
|
UdtSocketInternal s = locate(u);
|
|
if (null == s)
|
|
throw new UdtException(5, 4, 0);
|
|
|
|
lock (s.m_ControlLock)
|
|
{
|
|
// a socket can "connect" only if it is in INIT or UDTSTATUS.OPENED status
|
|
if (UDTSTATUS.INIT == s.m_Status)
|
|
{
|
|
if (!s.m_pUDT.m_bRendezvous)
|
|
{
|
|
s.m_pUDT.open();
|
|
updateMux(s);
|
|
s.m_Status = UDTSTATUS.OPENED;
|
|
}
|
|
else
|
|
throw new UdtException(5, 8, 0);
|
|
}
|
|
else if (UDTSTATUS.OPENED != s.m_Status)
|
|
throw new UdtException(5, 2, 0);
|
|
|
|
// connect_complete() may be called before connect() returns.
|
|
// So we need to update the status before connect() is called,
|
|
// otherwise the status may be overwritten with wrong value (CONNECTED vs. CONNECTING).
|
|
s.m_Status = UDTSTATUS.CONNECTING;
|
|
try
|
|
{
|
|
s.m_pUDT.connect(name);
|
|
}
|
|
catch (UdtException e)
|
|
{
|
|
s.m_Status = UDTSTATUS.OPENED;
|
|
throw e;
|
|
}
|
|
|
|
// record peer address
|
|
s.m_pPeerAddr = name;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public void connect_complete(UDTSOCKET u)
|
|
{
|
|
UdtSocketInternal s = locate(u);
|
|
if (null == s)
|
|
throw new UdtException(5, 4, 0);
|
|
|
|
// copy address information of local node
|
|
// the local port must be correctly assigned BEFORE CUDT.connect(),
|
|
// otherwise if connect() fails, the multiplexer cannot be located by garbage collection and will cause leak
|
|
s.m_pUDT.m_pSndQueue.m_pChannel.getSockAddr(ref s.m_pSelfAddr);
|
|
ConvertIPAddress.ToUintArray(s.m_pSelfAddr.Address, ref s.m_pUDT.m_piSelfIP);
|
|
|
|
s.m_Status = UDTSTATUS.CONNECTED;
|
|
}
|
|
|
|
public int close(UDTSOCKET u)
|
|
{
|
|
UdtSocketInternal s = locate(u);
|
|
if (null == s)
|
|
throw new UdtException(5, 4, 0);
|
|
|
|
lock (s.m_ControlLock)
|
|
{
|
|
|
|
if (s.m_Status == UDTSTATUS.LISTENING)
|
|
{
|
|
if (s.m_pUDT.m_bBroken)
|
|
return 0;
|
|
|
|
s.m_TimeStamp = Timer.getTime();
|
|
s.m_pUDT.m_bBroken = true;
|
|
|
|
// broadcast all "accept" waiting
|
|
s.m_AcceptCond.Set();
|
|
|
|
return 0;
|
|
}
|
|
|
|
s.m_pUDT.close();
|
|
|
|
// synchronize with garbage collection.
|
|
lock (m_ControlLock)
|
|
{
|
|
|
|
// since "s" is located before m_ControlLock, locate it again in case it became invalid
|
|
if (!m_Sockets.TryGetValue(u, out s) || s.m_Status == UDTSTATUS.CLOSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
s.m_Status = UDTSTATUS.CLOSED;
|
|
|
|
// a socket will not be immediated removed when it is closed
|
|
// in order to prevent other methods from accessing invalid address
|
|
// a timer is started and the socket will be removed after approximately 1 second
|
|
s.m_TimeStamp = Timer.getTime();
|
|
|
|
m_Sockets.Remove(s.m_SocketID);
|
|
m_ClosedSockets.Add(s.m_SocketID, s);
|
|
|
|
Timer.triggerEvent();
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// int CUDTUnited.getpeername(const UDTSOCKET u, sockaddr*name, int* namelen)
|
|
//{
|
|
// if (CONNECTED != getStatus(u))
|
|
// throw new UdtException(2, 2, 0);
|
|
|
|
// UdtSocket* s = locate(u);
|
|
|
|
// if (null == s)
|
|
// throw new UdtException(5, 4, 0);
|
|
|
|
// if (!s.m_pUDT.m_bConnected || s.m_pUDT.m_bBroken)
|
|
// throw new UdtException(2, 2, 0);
|
|
|
|
// if (AF_INET == s.m_iIPversion)
|
|
// *namelen = sizeof(sockaddr_in);
|
|
// else
|
|
// *namelen = sizeof(sockaddr_in6);
|
|
|
|
// // copy address information of peer node
|
|
// memcpy(name, s.m_pPeerAddr, *namelen);
|
|
|
|
// return 0;
|
|
// }
|
|
|
|
// int CUDTUnited.getsockname(const UDTSOCKET u, sockaddr*name, int* namelen)
|
|
//{
|
|
// UdtSocket* s = locate(u);
|
|
|
|
// if (null == s)
|
|
// throw new UdtException(5, 4, 0);
|
|
|
|
// if (s.m_pUDT.m_bBroken)
|
|
// throw new UdtException(5, 4, 0);
|
|
|
|
// if (INIT == s.m_Status)
|
|
// throw new UdtException(2, 2, 0);
|
|
|
|
// if (AF_INET == s.m_iIPversion)
|
|
// *namelen = sizeof(sockaddr_in);
|
|
// else
|
|
// *namelen = sizeof(sockaddr_in6);
|
|
|
|
// // copy address information of local node
|
|
// memcpy(name, s.m_pSelfAddr, *namelen);
|
|
|
|
// return 0;
|
|
// }
|
|
|
|
|
|
|
|
|
|
internal UdtSocketInternal locate(UDTSOCKET u)
|
|
{
|
|
lock (m_ControlLock)
|
|
{
|
|
UdtSocketInternal s;
|
|
if (!m_Sockets.TryGetValue(u, out s) || s.m_Status == UDTSTATUS.CLOSED)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
}
|
|
|
|
UdtSocketInternal locate(IPEndPoint peer, UDTSOCKET id, int isn)
|
|
{
|
|
lock (m_ControlLock)
|
|
{
|
|
HashSet<int> sockets;
|
|
if (!m_PeerRec.TryGetValue((id << 30) + isn, out sockets))
|
|
return null;
|
|
|
|
foreach (int iSocket in sockets)
|
|
{
|
|
UdtSocketInternal socket;
|
|
if (!m_Sockets.TryGetValue(iSocket, out socket))
|
|
continue;
|
|
|
|
if (socket.m_pPeerAddr.Equals(peer))
|
|
return socket;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public void checkBrokenSockets()
|
|
{
|
|
lock (m_ControlLock)
|
|
{
|
|
checkBrokenSockets_unsafe();
|
|
}
|
|
}
|
|
|
|
void checkBrokenSockets_unsafe()
|
|
{
|
|
// set of sockets To Be Closed and To Be Removed
|
|
List<UDTSOCKET> tbc = new List<UDTSOCKET>();
|
|
List<UDTSOCKET> tbr = new List<UDTSOCKET>();
|
|
|
|
foreach (KeyValuePair<UDTSOCKET, UdtSocketInternal> item in m_Sockets)
|
|
{
|
|
// check broken connection
|
|
if (item.Value.m_pUDT.m_bBroken)
|
|
{
|
|
if (item.Value.m_Status == UDTSTATUS.LISTENING)
|
|
{
|
|
// for a listening socket, it should wait an extra 3 seconds in case a client is connecting
|
|
if (Timer.getTime() - item.Value.m_TimeStamp < 3000000)
|
|
continue;
|
|
}
|
|
else if ((item.Value.m_pUDT.m_pRcvBuffer != null) && (item.Value.m_pUDT.m_pRcvBuffer.getRcvDataSize() > 0) && (item.Value.m_pUDT.m_iBrokenCounter-- > 0))
|
|
{
|
|
// if there is still data in the receiver buffer, wait longer
|
|
continue;
|
|
}
|
|
|
|
//close broken connections and start removal timer
|
|
item.Value.m_Status = UDTSTATUS.CLOSED;
|
|
item.Value.m_TimeStamp = Timer.getTime();
|
|
tbc.Add(item.Key);
|
|
m_ClosedSockets[item.Key] = item.Value;
|
|
|
|
// remove from listener's queue
|
|
UdtSocketInternal listenSocket;
|
|
if (!m_Sockets.TryGetValue(item.Value.m_ListenSocket, out listenSocket))
|
|
{
|
|
if (!m_ClosedSockets.TryGetValue(item.Value.m_ListenSocket, out listenSocket))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Monitor.Enter(listenSocket.m_AcceptLock);
|
|
listenSocket.m_pQueuedSockets.Remove(item.Value.m_SocketID);
|
|
listenSocket.m_pAcceptSockets.Remove(item.Value.m_SocketID);
|
|
Monitor.Exit(listenSocket.m_AcceptLock);
|
|
}
|
|
}
|
|
|
|
foreach (KeyValuePair<UDTSOCKET, UdtSocketInternal> j in m_ClosedSockets)
|
|
{
|
|
if (j.Value.m_pUDT.m_ullLingerExpiration > 0)
|
|
{
|
|
// asynchronous close:
|
|
if ((null == j.Value.m_pUDT.m_pSndBuffer) || (0 == j.Value.m_pUDT.m_pSndBuffer.getCurrBufSize()) || (j.Value.m_pUDT.m_ullLingerExpiration <= Timer.getTime()))
|
|
{
|
|
j.Value.m_pUDT.m_ullLingerExpiration = 0;
|
|
j.Value.m_pUDT.m_bClosing = true;
|
|
j.Value.m_TimeStamp = Timer.getTime();
|
|
}
|
|
}
|
|
|
|
// timeout 1 second to destroy a socket AND it has been removed from RcvUList
|
|
if ((Timer.getTime() - j.Value.m_TimeStamp > 1000000) && ((null == j.Value.m_pUDT.m_pRNode) || !j.Value.m_pUDT.m_pRNode.m_bOnList))
|
|
{
|
|
tbr.Add(j.Key);
|
|
}
|
|
}
|
|
|
|
// move closed sockets to the ClosedSockets structure
|
|
foreach (UDTSOCKET k in tbc)
|
|
m_Sockets.Remove(k);
|
|
|
|
// remove those timeout sockets
|
|
foreach (UDTSOCKET l in tbr)
|
|
removeSocket(l);
|
|
}
|
|
|
|
void removeSocket(UDTSOCKET u)
|
|
{
|
|
UdtSocketInternal closedSocket;
|
|
if (!m_ClosedSockets.TryGetValue(u, out closedSocket))
|
|
return;
|
|
|
|
// decrease multiplexer reference count, and remove it if necessary
|
|
int mid = closedSocket.m_iMuxID;
|
|
|
|
if (null != closedSocket.m_pQueuedSockets)
|
|
{
|
|
Monitor.Enter(closedSocket.m_AcceptLock);
|
|
|
|
// if it is a listener, close all un-accepted sockets in its queue and remove them later
|
|
foreach (UDTSOCKET q in closedSocket.m_pQueuedSockets)
|
|
{
|
|
m_Sockets[q].m_pUDT.m_bBroken = true;
|
|
m_Sockets[q].m_pUDT.close();
|
|
m_Sockets[q].m_TimeStamp = Timer.getTime();
|
|
m_Sockets[q].m_Status = UDTSTATUS.CLOSED;
|
|
m_ClosedSockets[q] = m_Sockets[q];
|
|
m_Sockets.Remove(q);
|
|
}
|
|
|
|
Monitor.Exit(closedSocket.m_AcceptLock);
|
|
}
|
|
|
|
// remove from peer rec
|
|
HashSet<int> sockets;
|
|
if (m_PeerRec.TryGetValue((closedSocket.m_PeerID << 30) + closedSocket.m_iISN, out sockets))
|
|
{
|
|
sockets.Remove(u);
|
|
if (sockets.Count == 0)
|
|
m_PeerRec.Remove(closedSocket.m_PeerID);
|
|
}
|
|
|
|
// delete this one
|
|
closedSocket.m_pUDT.close();
|
|
closedSocket.Close();
|
|
m_ClosedSockets.Remove(u);
|
|
|
|
Multiplexer m;
|
|
if (!m_mMultiplexer.TryGetValue(mid, out m))
|
|
{
|
|
//something is wrong!!!
|
|
return;
|
|
}
|
|
|
|
m.m_iRefCount--;
|
|
if (0 == m.m_iRefCount)
|
|
{
|
|
m.m_pChannel.close();
|
|
m.m_pSndQueue.Close();
|
|
m.m_pRcvQueue.Close();
|
|
m.m_pTimer.Stop();
|
|
|
|
m_mMultiplexer.Remove(mid);
|
|
}
|
|
}
|
|
|
|
// void setError(UdtException e)
|
|
//{
|
|
// CGuard tg(m_TLSLock);
|
|
// delete(UdtException *)TlsGetValue(m_TLSError);
|
|
// TlsSetValue(m_TLSError, e);
|
|
// m_mTLSRecord[GetCurrentThreadId()] = e;
|
|
// }
|
|
|
|
// UdtException getError()
|
|
//{
|
|
// CGuard tg(m_TLSLock);
|
|
// if (null == TlsGetValue(m_TLSError))
|
|
// {
|
|
// UdtException* e = new UdtException;
|
|
// TlsSetValue(m_TLSError, e);
|
|
// m_mTLSRecord[GetCurrentThreadId()] = e;
|
|
// }
|
|
// return (UdtException*)TlsGetValue(m_TLSError);
|
|
// }
|
|
|
|
// void checkTLSValue()
|
|
//{
|
|
// CGuard tg(m_TLSLock);
|
|
|
|
// vector<DWORD> tbr;
|
|
// for (map<DWORD, UdtException*>.iterator i = m_mTLSRecord.begin(); i != m_mTLSRecord.end(); ++i)
|
|
// {
|
|
// HANDLE h = OpenThread(THREAD_QUERY_INFORMATION, FALSE, i.first);
|
|
// if (null == h)
|
|
// {
|
|
// tbr.push_back(i.first);
|
|
// break;
|
|
// }
|
|
// if (WAIT_OBJECT_0 == WaitForSingleObject(h, 0))
|
|
// {
|
|
// delete i.second;
|
|
// tbr.push_back(i.first);
|
|
// }
|
|
// CloseHandle(h);
|
|
// }
|
|
// for (vector<DWORD>.iterator j = tbr.begin(); j != tbr.end(); ++j)
|
|
// m_mTLSRecord.erase(*j);
|
|
// }
|
|
|
|
void updateMux(UdtSocketInternal s, IPEndPoint addr = null, Socket udpsock = null)
|
|
{
|
|
lock (m_ControlLock)
|
|
{
|
|
Multiplexer m;
|
|
if ((s.m_pUDT.m_bReuseAddr) && (null != addr))
|
|
{
|
|
int port = addr.Port;
|
|
|
|
// find a reusable address
|
|
foreach (KeyValuePair<int, Multiplexer> item in m_mMultiplexer)
|
|
{
|
|
// reuse the existing multiplexer
|
|
m = item.Value;
|
|
if ((m.m_iIPversion == s.m_pUDT.m_iIPversion) && (m.m_iMSS == s.m_pUDT.m_iMSS) && m.m_bReusable)
|
|
{
|
|
if (m.m_iPort == port)
|
|
{
|
|
// reuse the existing multiplexer
|
|
++m.m_iRefCount;
|
|
s.m_pUDT.m_pSndQueue = m.m_pSndQueue;
|
|
s.m_pUDT.m_pRcvQueue = m.m_pRcvQueue;
|
|
s.m_iMuxID = m.m_iID;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// a new multiplexer is needed
|
|
m = new Multiplexer();
|
|
m.m_iMSS = s.m_pUDT.m_iMSS;
|
|
m.m_iIPversion = s.m_pUDT.m_iIPversion;
|
|
m.m_iRefCount = 1;
|
|
m.m_bReusable = s.m_pUDT.m_bReuseAddr;
|
|
m.m_iID = s.m_SocketID;
|
|
|
|
m.m_pChannel = new Channel(s.m_pUDT.m_iIPversion);
|
|
m.m_pChannel.setSndBufSize(s.m_pUDT.m_iUDPSndBufSize);
|
|
m.m_pChannel.setRcvBufSize(s.m_pUDT.m_iUDPRcvBufSize);
|
|
|
|
try
|
|
{
|
|
if (null != udpsock)
|
|
m.m_pChannel.open(udpsock);
|
|
else
|
|
m.m_pChannel.open(addr);
|
|
}
|
|
catch (UdtException e)
|
|
{
|
|
m.m_pChannel.close();
|
|
throw e;
|
|
}
|
|
|
|
IPEndPoint sa = new IPEndPoint(IPAddress.Any, 0);
|
|
m.m_pChannel.getSockAddr(ref sa);
|
|
m.m_iPort = sa.Port;
|
|
|
|
m.m_pTimer = new Timer();
|
|
|
|
m.m_pSndQueue = new SndQueue();
|
|
m.m_pSndQueue.init(m.m_pChannel, m.m_pTimer);
|
|
m.m_pRcvQueue = new RcvQueue();
|
|
m.m_pRcvQueue.init(32, s.m_pUDT.m_iPayloadSize, m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer);
|
|
|
|
m_mMultiplexer[m.m_iID] = m;
|
|
|
|
s.m_pUDT.m_pSndQueue = m.m_pSndQueue;
|
|
s.m_pUDT.m_pRcvQueue = m.m_pRcvQueue;
|
|
s.m_iMuxID = m.m_iID;
|
|
}
|
|
}
|
|
|
|
void updateMux(UdtSocketInternal s, UdtSocketInternal ls)
|
|
{
|
|
lock (m_ControlLock)
|
|
{
|
|
|
|
int port = ls.m_pSelfAddr.Port;
|
|
|
|
// find the listener's address
|
|
foreach (KeyValuePair<int, Multiplexer> item in m_mMultiplexer)
|
|
{
|
|
if (item.Value.m_iPort == port)
|
|
{
|
|
// reuse the existing multiplexer
|
|
Multiplexer multiplexer = item.Value;
|
|
++multiplexer.m_iRefCount;
|
|
s.m_pUDT.m_pSndQueue = multiplexer.m_pSndQueue;
|
|
s.m_pUDT.m_pRcvQueue = multiplexer.m_pRcvQueue;
|
|
s.m_iMuxID = multiplexer.m_iID;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace UdtSharp
|