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 m_pQueuedSockets; // set of connections waiting for accept() public HashSet 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 m_Sockets = new Dictionary(); // 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> m_PeerRec = new Dictionary>();// 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 m_mTLSRecord; object m_TLSLock = new object(); Dictionary m_mMultiplexer = new Dictionary(); // UDP multiplexer object m_MultiplexerLock = new object(); HashSet m_pCache = new HashSet(); // 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 m_ClosedSockets = new Dictionary(); // 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 sockets; if (!m_PeerRec.TryGetValue((ns.m_PeerID << 30) + ns.m_iISN, out sockets)) { sockets = new HashSet(); 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(); s.m_pAcceptSockets = new HashSet(); 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.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 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 tbc = new List(); List tbr = new List(); foreach (KeyValuePair 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 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 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 tbr; // for (map.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.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 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 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