mirror of
https://github.com/ossrs/srs.git
synced 2025-02-13 11:51:57 +00:00
3245 lines
87 KiB
C++
3245 lines
87 KiB
C++
/*
|
|
* SRT - Secure, Reliable, Transport
|
|
* Copyright (c) 2018 Haivision Systems Inc.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met:
|
|
|
|
* Redistributions of source code must retain the above
|
|
copyright notice, this list of conditions and the
|
|
following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the
|
|
above copyright notice, this list of conditions
|
|
and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the University of Illinois
|
|
nor the names of its contributors may be used to
|
|
endorse or promote products derived from this
|
|
software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
written by
|
|
Yunhong Gu, last updated 07/09/2011
|
|
modified by
|
|
Haivision Systems Inc.
|
|
*****************************************************************************/
|
|
|
|
#include <exception>
|
|
#include <stdexcept>
|
|
#include <typeinfo>
|
|
#include <iterator>
|
|
|
|
#include <cstring>
|
|
#include "platform_sys.h"
|
|
#include "api.h"
|
|
#include "core.h"
|
|
#include "logging.h"
|
|
#include "threadname.h"
|
|
#include "srt.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <win/wintime.h>
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(error: 4530)
|
|
#endif
|
|
|
|
using namespace std;
|
|
using namespace srt_logging;
|
|
extern LogConfig srt_logger_config;
|
|
|
|
|
|
CUDTSocket::CUDTSocket():
|
|
m_Status(SRTS_INIT),
|
|
m_ClosureTimeStamp(0),
|
|
m_iIPversion(0),
|
|
m_pSelfAddr(NULL),
|
|
m_pPeerAddr(NULL),
|
|
m_SocketID(0),
|
|
m_ListenSocket(0),
|
|
m_PeerID(0),
|
|
m_iISN(0),
|
|
m_pUDT(NULL),
|
|
m_pQueuedSockets(NULL),
|
|
m_pAcceptSockets(NULL),
|
|
m_AcceptCond(),
|
|
m_AcceptLock(),
|
|
m_uiBackLog(0),
|
|
m_iMuxID(-1)
|
|
{
|
|
pthread_mutex_init(&m_AcceptLock, NULL);
|
|
pthread_cond_init(&m_AcceptCond, NULL);
|
|
pthread_mutex_init(&m_ControlLock, NULL);
|
|
}
|
|
|
|
CUDTSocket::~CUDTSocket()
|
|
{
|
|
if (m_iIPversion == AF_INET)
|
|
{
|
|
delete (sockaddr_in*)m_pSelfAddr;
|
|
delete (sockaddr_in*)m_pPeerAddr;
|
|
}
|
|
else
|
|
{
|
|
delete (sockaddr_in6*)m_pSelfAddr;
|
|
delete (sockaddr_in6*)m_pPeerAddr;
|
|
}
|
|
|
|
delete m_pUDT;
|
|
m_pUDT = NULL;
|
|
|
|
delete m_pQueuedSockets;
|
|
delete m_pAcceptSockets;
|
|
|
|
pthread_mutex_destroy(&m_AcceptLock);
|
|
pthread_cond_destroy(&m_AcceptCond);
|
|
pthread_mutex_destroy(&m_ControlLock);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
CUDTUnited::CUDTUnited():
|
|
m_Sockets(),
|
|
m_ControlLock(),
|
|
m_IDLock(),
|
|
m_SocketIDGenerator(0),
|
|
m_TLSError(),
|
|
m_mMultiplexer(),
|
|
m_MultiplexerLock(),
|
|
m_pCache(NULL),
|
|
m_bClosing(false),
|
|
m_GCStopLock(),
|
|
m_GCStopCond(),
|
|
m_InitLock(),
|
|
m_iInstanceCount(0),
|
|
m_bGCStatus(false),
|
|
m_GCThread(),
|
|
m_ClosedSockets()
|
|
{
|
|
// Socket ID MUST start from a random value
|
|
// Note. Don't use CTimer here, because s_UDTUnited is a static instance of CUDTUnited
|
|
// with dynamic initialization (calling this constructor), while CTimer has
|
|
// a static member s_ullCPUFrequency with dynamic initialization.
|
|
// The order of initialization is not guaranteed.
|
|
timeval t;
|
|
gettimeofday(&t, 0);
|
|
srand((unsigned int)t.tv_usec);
|
|
m_SocketIDGenerator = 1 + (int)((1 << 30) * (double(rand()) / RAND_MAX));
|
|
|
|
pthread_mutex_init(&m_ControlLock, NULL);
|
|
pthread_mutex_init(&m_IDLock, NULL);
|
|
pthread_mutex_init(&m_InitLock, NULL);
|
|
|
|
pthread_key_create(&m_TLSError, TLSDestroy);
|
|
|
|
m_pCache = new CCache<CInfoBlock>;
|
|
}
|
|
|
|
CUDTUnited::~CUDTUnited()
|
|
{
|
|
// Call it if it wasn't called already.
|
|
// This will happen at the end of main() of the application,
|
|
// when the user didn't call srt_cleanup().
|
|
if (m_bGCStatus)
|
|
{
|
|
cleanup();
|
|
}
|
|
|
|
pthread_mutex_destroy(&m_ControlLock);
|
|
pthread_mutex_destroy(&m_IDLock);
|
|
pthread_mutex_destroy(&m_InitLock);
|
|
|
|
delete (CUDTException*)pthread_getspecific(m_TLSError);
|
|
pthread_key_delete(m_TLSError);
|
|
|
|
delete m_pCache;
|
|
}
|
|
|
|
std::string CUDTUnited::CONID(SRTSOCKET sock)
|
|
{
|
|
if ( sock == 0 )
|
|
return "";
|
|
|
|
std::ostringstream os;
|
|
os << "@" << sock << ":";
|
|
return os.str();
|
|
}
|
|
|
|
int CUDTUnited::startup()
|
|
{
|
|
CGuard gcinit(m_InitLock);
|
|
|
|
if (m_iInstanceCount++ > 0)
|
|
return 0;
|
|
|
|
// Global initialization code
|
|
#ifdef _WIN32
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
wVersionRequested = MAKEWORD(2, 2);
|
|
|
|
if (0 != WSAStartup(wVersionRequested, &wsaData))
|
|
throw CUDTException(MJ_SETUP, MN_NONE, WSAGetLastError());
|
|
#endif
|
|
|
|
PacketFilter::globalInit();
|
|
|
|
//init CTimer::EventLock
|
|
|
|
if (m_bGCStatus)
|
|
return true;
|
|
|
|
m_bClosing = false;
|
|
pthread_mutex_init(&m_GCStopLock, NULL);
|
|
#if ENABLE_MONOTONIC_CLOCK
|
|
pthread_condattr_t CondAttribs;
|
|
pthread_condattr_init(&CondAttribs);
|
|
pthread_condattr_setclock(&CondAttribs, CLOCK_MONOTONIC);
|
|
pthread_cond_init(&m_GCStopCond, &CondAttribs);
|
|
#else
|
|
pthread_cond_init(&m_GCStopCond, NULL);
|
|
#endif
|
|
{
|
|
ThreadName tn("SRT:GC");
|
|
pthread_create(&m_GCThread, NULL, garbageCollect, this);
|
|
}
|
|
|
|
m_bGCStatus = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CUDTUnited::cleanup()
|
|
{
|
|
CGuard gcinit(m_InitLock);
|
|
|
|
if (--m_iInstanceCount > 0)
|
|
return 0;
|
|
|
|
//destroy CTimer::EventLock
|
|
|
|
if (!m_bGCStatus)
|
|
return 0;
|
|
|
|
m_bClosing = true;
|
|
pthread_cond_signal(&m_GCStopCond);
|
|
pthread_join(m_GCThread, NULL);
|
|
|
|
// XXX There's some weird bug here causing this
|
|
// to hangup on Windows. This might be either something
|
|
// bigger, or some problem in pthread-win32. As this is
|
|
// the application cleanup section, this can be temporarily
|
|
// tolerated with simply exit the application without cleanup,
|
|
// counting on that the system will take care of it anyway.
|
|
#ifndef _WIN32
|
|
pthread_mutex_destroy(&m_GCStopLock);
|
|
pthread_cond_destroy(&m_GCStopCond);
|
|
#endif
|
|
|
|
m_bGCStatus = false;
|
|
|
|
// Global destruction code
|
|
#ifdef _WIN32
|
|
WSACleanup();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
SRTSOCKET CUDTUnited::newSocket(int af, int)
|
|
{
|
|
|
|
CUDTSocket* ns = NULL;
|
|
|
|
try
|
|
{
|
|
// XXX REFACTOR:
|
|
// Use sockaddr_any for m_pSelfAddr and just initialize it
|
|
// with 'af'.
|
|
ns = new CUDTSocket;
|
|
ns->m_pUDT = new CUDT;
|
|
if (af == AF_INET)
|
|
{
|
|
ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in);
|
|
((sockaddr_in*)(ns->m_pSelfAddr))->sin_port = 0;
|
|
}
|
|
else
|
|
{
|
|
ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in6);
|
|
((sockaddr_in6*)(ns->m_pSelfAddr))->sin6_port = 0;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
delete ns;
|
|
throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
|
|
}
|
|
|
|
CGuard::enterCS(m_IDLock);
|
|
ns->m_SocketID = -- m_SocketIDGenerator;
|
|
CGuard::leaveCS(m_IDLock);
|
|
|
|
ns->m_Status = SRTS_INIT;
|
|
ns->m_ListenSocket = 0;
|
|
ns->m_pUDT->m_SocketID = ns->m_SocketID;
|
|
// The "Socket type" is deprecated. For the sake of
|
|
// HSv4 there will be only a "socket type" field set
|
|
// in the handshake, always to UDT_DGRAM.
|
|
//ns->m_pUDT->m_iSockType = (type == SOCK_STREAM) ? UDT_STREAM : UDT_DGRAM;
|
|
ns->m_pUDT->m_iSockType = UDT_DGRAM;
|
|
ns->m_pUDT->m_iIPversion = ns->m_iIPversion = af;
|
|
ns->m_pUDT->m_pCache = m_pCache;
|
|
|
|
// protect the m_Sockets structure.
|
|
CGuard::enterCS(m_ControlLock);
|
|
try
|
|
{
|
|
HLOGC(mglog.Debug, log << CONID(ns->m_SocketID)
|
|
<< "newSocket: mapping socket "
|
|
<< ns->m_SocketID);
|
|
m_Sockets[ns->m_SocketID] = ns;
|
|
}
|
|
catch (...)
|
|
{
|
|
//failure and rollback
|
|
CGuard::leaveCS(m_ControlLock);
|
|
delete ns;
|
|
ns = NULL;
|
|
}
|
|
CGuard::leaveCS(m_ControlLock);
|
|
|
|
if (!ns)
|
|
throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
|
|
|
|
return ns->m_SocketID;
|
|
}
|
|
|
|
int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr* peer, CHandShake* hs, const CPacket& hspkt,
|
|
ref_t<SRT_REJECT_REASON> r_error)
|
|
{
|
|
CUDTSocket* ns = NULL;
|
|
|
|
*r_error = SRT_REJ_IPE;
|
|
|
|
// Can't manage this error through an exception because this is
|
|
// running in the listener loop.
|
|
CUDTSocket* ls = locate(listen);
|
|
if (!ls)
|
|
{
|
|
LOGC(mglog.Error, log << "IPE: newConnection by listener socket id=" << listen << " which DOES NOT EXIST.");
|
|
return -1;
|
|
}
|
|
|
|
// if this connection has already been processed
|
|
if ((ns = locate(peer, hs->m_iID, hs->m_iISN)) != NULL)
|
|
{
|
|
if (ns->m_pUDT->m_bBroken)
|
|
{
|
|
// last connection from the "peer" address has been broken
|
|
ns->m_Status = SRTS_CLOSED;
|
|
ns->m_ClosureTimeStamp = CTimer::getTime();
|
|
|
|
CGuard::enterCS(ls->m_AcceptLock);
|
|
ls->m_pQueuedSockets->erase(ns->m_SocketID);
|
|
ls->m_pAcceptSockets->erase(ns->m_SocketID);
|
|
CGuard::leaveCS(ls->m_AcceptLock);
|
|
}
|
|
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 = URQ_CONCLUSION;
|
|
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->size() >= ls->m_uiBackLog)
|
|
{
|
|
*r_error = SRT_REJ_BACKLOG;
|
|
LOGC(mglog.Error, log << "newConnection: listen backlog=" << ls->m_uiBackLog << " EXCEEDED");
|
|
return -1;
|
|
}
|
|
|
|
try
|
|
{
|
|
ns = new CUDTSocket;
|
|
ns->m_pUDT = new CUDT(*(ls->m_pUDT));
|
|
if (ls->m_iIPversion == AF_INET)
|
|
{
|
|
ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in);
|
|
((sockaddr_in*)(ns->m_pSelfAddr))->sin_port = 0;
|
|
ns->m_pPeerAddr = (sockaddr*)(new sockaddr_in);
|
|
memcpy(ns->m_pPeerAddr, peer, sizeof(sockaddr_in));
|
|
}
|
|
else
|
|
{
|
|
ns->m_pSelfAddr = (sockaddr*)(new sockaddr_in6);
|
|
((sockaddr_in6*)(ns->m_pSelfAddr))->sin6_port = 0;
|
|
ns->m_pPeerAddr = (sockaddr*)(new sockaddr_in6);
|
|
memcpy(ns->m_pPeerAddr, peer, sizeof(sockaddr_in6));
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
*r_error = SRT_REJ_RESOURCE;
|
|
delete ns;
|
|
LOGC(mglog.Error, log << "IPE: newConnection: unexpected exception (probably std::bad_alloc)");
|
|
return -1;
|
|
}
|
|
|
|
CGuard::enterCS(m_IDLock);
|
|
ns->m_SocketID = -- m_SocketIDGenerator;
|
|
HLOGF(mglog.Debug, "newConnection: generated socket id %d", ns->m_SocketID);
|
|
CGuard::leaveCS(m_IDLock);
|
|
|
|
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;
|
|
|
|
// Set the error code for all prospective problems below.
|
|
// It won't be interpreted when result was successful.
|
|
*r_error = SRT_REJ_RESOURCE;
|
|
|
|
// These can throw exception only when the memory allocation failed.
|
|
// CUDT::connect() translates exception into CUDTException.
|
|
// CUDT::open() may only throw original std::bad_alloc from new.
|
|
// This is only to make the library extra safe (when your machine lacks
|
|
// memory, it will continue to work, but fail to accept connection).
|
|
try
|
|
{
|
|
// This assignment must happen b4 the call to CUDT::connect() because
|
|
// this call causes sending the SRT Handshake through this socket.
|
|
// Without this mapping the socket cannot be found and therefore
|
|
// the SRT Handshake message would fail.
|
|
HLOGF(mglog.Debug,
|
|
"newConnection: incoming %s, mapping socket %d",
|
|
SockaddrToString(peer).c_str(), ns->m_SocketID);
|
|
{
|
|
CGuard cg(m_ControlLock);
|
|
m_Sockets[ns->m_SocketID] = ns;
|
|
}
|
|
|
|
// bind to the same addr of listening socket
|
|
ns->m_pUDT->open();
|
|
updateListenerMux(ns, ls);
|
|
if (ls->m_pUDT->m_cbAcceptHook)
|
|
{
|
|
if (!ls->m_pUDT->runAcceptHook(ns->m_pUDT, peer, hs, hspkt))
|
|
{
|
|
error = 1;
|
|
goto ERR_ROLLBACK;
|
|
}
|
|
}
|
|
ns->m_pUDT->acceptAndRespond(peer, hs, hspkt);
|
|
}
|
|
catch (...)
|
|
{
|
|
// Extract the error that was set in this new failed entity.
|
|
*r_error = ns->m_pUDT->m_RejectReason;
|
|
error = 1;
|
|
goto ERR_ROLLBACK;
|
|
}
|
|
|
|
ns->m_Status = SRTS_CONNECTED;
|
|
|
|
// copy address information of local node
|
|
ns->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(ns->m_pSelfAddr);
|
|
CIPAddress::pton(ns->m_pSelfAddr, ns->m_pUDT->m_piSelfIP, ns->m_iIPversion);
|
|
|
|
// protect the m_Sockets structure.
|
|
CGuard::enterCS(m_ControlLock);
|
|
try
|
|
{
|
|
HLOGF(mglog.Debug,
|
|
"newConnection: mapping peer %d to that socket (%d)\n",
|
|
ns->m_PeerID, ns->m_SocketID);
|
|
m_PeerRec[ns->getPeerSpec()].insert(ns->m_SocketID);
|
|
}
|
|
catch (...)
|
|
{
|
|
error = 2;
|
|
}
|
|
CGuard::leaveCS(m_ControlLock);
|
|
|
|
CGuard::enterCS(ls->m_AcceptLock);
|
|
try
|
|
{
|
|
ls->m_pQueuedSockets->insert(ns->m_SocketID);
|
|
}
|
|
catch (...)
|
|
{
|
|
error = 3;
|
|
}
|
|
CGuard::leaveCS(ls->m_AcceptLock);
|
|
|
|
// acknowledge users waiting for new connections on the listening socket
|
|
m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, true);
|
|
|
|
CTimer::triggerEvent();
|
|
|
|
ERR_ROLLBACK:
|
|
// XXX the exact value of 'error' is ignored
|
|
if (error > 0)
|
|
{
|
|
#if ENABLE_LOGGING
|
|
static const char* why [] = {
|
|
"UNKNOWN ERROR",
|
|
"CONNECTION REJECTED",
|
|
"IPE when mapping a socket",
|
|
"IPE when inserting a socket"
|
|
};
|
|
LOGC(mglog.Error, log << CONID(ns->m_SocketID) << "newConnection: connection rejected due to: " << why[error]);
|
|
#endif
|
|
SRTSOCKET id = ns->m_SocketID;
|
|
ns->m_pUDT->close();
|
|
ns->m_Status = SRTS_CLOSED;
|
|
ns->m_ClosureTimeStamp = CTimer::getTime();
|
|
// The mapped socket should be now unmapped to preserve the situation that
|
|
// was in the original UDT code.
|
|
// In SRT additionally the acceptAndRespond() function (it was called probably
|
|
// connect() in UDT code) may fail, in which case this socket should not be
|
|
// further processed and should be removed.
|
|
{
|
|
CGuard cg(m_ControlLock);
|
|
m_Sockets.erase(id);
|
|
m_ClosedSockets[id] = ns;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// wake up a waiting accept() call
|
|
pthread_mutex_lock(&(ls->m_AcceptLock));
|
|
pthread_cond_signal(&(ls->m_AcceptCond));
|
|
pthread_mutex_unlock(&(ls->m_AcceptLock));
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq)
|
|
{
|
|
try
|
|
{
|
|
CUDT* lc = lookup(lsn);
|
|
lc->installAcceptHook(hook, opaq);
|
|
|
|
}
|
|
catch (CUDTException& e)
|
|
{
|
|
setError(new CUDTException(e));
|
|
return SRT_ERROR;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
CUDT* CUDTUnited::lookup(const SRTSOCKET u)
|
|
{
|
|
// protects the m_Sockets structure
|
|
CGuard cg(m_ControlLock);
|
|
|
|
map<SRTSOCKET, CUDTSocket*>::iterator i = m_Sockets.find(u);
|
|
|
|
if ((i == m_Sockets.end()) || (i->second->m_Status == SRTS_CLOSED))
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
return i->second->m_pUDT;
|
|
}
|
|
|
|
SRT_SOCKSTATUS CUDTUnited::getStatus(const SRTSOCKET u)
|
|
{
|
|
// protects the m_Sockets structure
|
|
CGuard cg(m_ControlLock);
|
|
|
|
map<SRTSOCKET, CUDTSocket*>::const_iterator i = m_Sockets.find(u);
|
|
|
|
if (i == m_Sockets.end())
|
|
{
|
|
if (m_ClosedSockets.find(u) != m_ClosedSockets.end())
|
|
return SRTS_CLOSED;
|
|
|
|
return SRTS_NONEXIST;
|
|
}
|
|
const CUDTSocket* s = i->second;
|
|
|
|
if (s->m_pUDT->m_bBroken)
|
|
return SRTS_BROKEN;
|
|
|
|
// TTL in CRendezvousQueue::updateConnStatus() will set m_bConnecting to false.
|
|
// Although m_Status is still SRTS_CONNECTING, the connection is in fact to be closed due to TTL expiry.
|
|
// In this case m_bConnected is also false. Both checks are required to avoid hitting
|
|
// a regular state transition from CONNECTING to CONNECTED.
|
|
if ((s->m_Status == SRTS_CONNECTING) && !s->m_pUDT->m_bConnecting && !s->m_pUDT->m_bConnected)
|
|
return SRTS_BROKEN;
|
|
|
|
return s->m_Status;
|
|
}
|
|
|
|
int CUDTUnited::bind(const SRTSOCKET u, const sockaddr* name, int namelen)
|
|
{
|
|
CUDTSocket* s = locate(u);
|
|
if (!s)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
CGuard cg(s->m_ControlLock);
|
|
|
|
// cannot bind a socket more than once
|
|
if (s->m_Status != SRTS_INIT)
|
|
throw CUDTException(MJ_NOTSUP, MN_NONE, 0);
|
|
|
|
// check the size of SOCKADDR structure
|
|
if (s->m_iIPversion == AF_INET)
|
|
{
|
|
if (namelen != sizeof(sockaddr_in))
|
|
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
|
|
}
|
|
else
|
|
{
|
|
if (namelen != sizeof(sockaddr_in6))
|
|
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
|
|
}
|
|
|
|
s->m_pUDT->open();
|
|
updateMux(s, name);
|
|
s->m_Status = SRTS_OPENED;
|
|
|
|
// copy address information of local node
|
|
s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CUDTUnited::bind(SRTSOCKET u, UDPSOCKET udpsock)
|
|
{
|
|
CUDTSocket* s = locate(u);
|
|
if (!s)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
CGuard cg(s->m_ControlLock);
|
|
|
|
// cannot bind a socket more than once
|
|
if (s->m_Status != SRTS_INIT)
|
|
throw CUDTException(MJ_NOTSUP, MN_NONE, 0);
|
|
|
|
sockaddr_in name4;
|
|
sockaddr_in6 name6;
|
|
sockaddr* name;
|
|
socklen_t namelen;
|
|
|
|
if (s->m_iIPversion == AF_INET)
|
|
{
|
|
namelen = sizeof(sockaddr_in);
|
|
name = (sockaddr*)&name4;
|
|
}
|
|
else
|
|
{
|
|
namelen = sizeof(sockaddr_in6);
|
|
name = (sockaddr*)&name6;
|
|
}
|
|
|
|
if (::getsockname(udpsock, name, &namelen) == -1)
|
|
throw CUDTException(MJ_NOTSUP, MN_INVAL);
|
|
|
|
s->m_pUDT->open();
|
|
updateMux(s, name, &udpsock);
|
|
s->m_Status = SRTS_OPENED;
|
|
|
|
// copy address information of local node
|
|
s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CUDTUnited::listen(const SRTSOCKET u, int backlog)
|
|
{
|
|
if (backlog <= 0)
|
|
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
|
|
|
|
// Don't search for the socket if it's already -1;
|
|
// this never is a valid socket.
|
|
if (u == UDT::INVALID_SOCK)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
CUDTSocket* s = locate(u);
|
|
if (!s)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
CGuard cg(s->m_ControlLock);
|
|
|
|
// NOTE: since now the socket is protected against simultaneous access.
|
|
// In the meantime the socket might have been closed, which means that
|
|
// it could have changed the state. It could be also set listen in another
|
|
// thread, so check it out.
|
|
|
|
// do nothing if the socket is already listening
|
|
if (s->m_Status == SRTS_LISTENING)
|
|
return 0;
|
|
|
|
// a socket can listen only if is in OPENED status
|
|
if (s->m_Status != SRTS_OPENED)
|
|
throw CUDTException(MJ_NOTSUP, MN_ISUNBOUND, 0);
|
|
|
|
// [[using assert(s->m_Status == OPENED)]];
|
|
|
|
// listen is not supported in rendezvous connection setup
|
|
if (s->m_pUDT->m_bRendezvous)
|
|
throw CUDTException(MJ_NOTSUP, MN_ISRENDEZVOUS, 0);
|
|
|
|
s->m_uiBackLog = backlog;
|
|
|
|
try
|
|
{
|
|
s->m_pQueuedSockets = new set<SRTSOCKET>;
|
|
s->m_pAcceptSockets = new set<SRTSOCKET>;
|
|
}
|
|
catch (...)
|
|
{
|
|
delete s->m_pQueuedSockets;
|
|
delete s->m_pAcceptSockets;
|
|
|
|
// XXX Translated std::bad_alloc into CUDTException specifying
|
|
// memory allocation failure...
|
|
throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
|
|
}
|
|
|
|
// [[using assert(s->m_Status == OPENED)]]; // (still, unchanged)
|
|
|
|
s->m_pUDT->setListenState(); // propagates CUDTException,
|
|
// if thrown, remains in OPENED state if so.
|
|
s->m_Status = SRTS_LISTENING;
|
|
|
|
return 0;
|
|
}
|
|
|
|
SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen)
|
|
{
|
|
if ((addr) && (!addrlen))
|
|
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
|
|
|
|
CUDTSocket* ls = locate(listen);
|
|
|
|
if (ls == NULL)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
// the "listen" socket must be in LISTENING status
|
|
if (ls->m_Status != SRTS_LISTENING)
|
|
throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0);
|
|
|
|
// no "accept" in rendezvous connection setup
|
|
if (ls->m_pUDT->m_bRendezvous)
|
|
throw CUDTException(MJ_NOTSUP, MN_ISRENDEZVOUS, 0);
|
|
|
|
SRTSOCKET u = CUDT::INVALID_SOCK;
|
|
bool accepted = false;
|
|
|
|
// !!only one conection can be set up each time!!
|
|
while (!accepted)
|
|
{
|
|
CGuard cg(ls->m_AcceptLock);
|
|
|
|
if ((ls->m_Status != SRTS_LISTENING) || ls->m_pUDT->m_bBroken)
|
|
{
|
|
// This socket has been closed.
|
|
accepted = true;
|
|
}
|
|
else if (ls->m_pQueuedSockets->size() > 0)
|
|
{
|
|
// XXX REFACTORING REQUIRED HERE!
|
|
// Actually this should at best be something like that:
|
|
// set<SRTSOCKET>::iterator b = ls->m_pQueuedSockets->begin();
|
|
// u = *b;
|
|
// ls->m_pQueuedSockets->erase(b);
|
|
// ls->m_pAcceptSockets->insert(u);
|
|
//
|
|
// It is also questionable why m_pQueuedSockets should be of type 'set'.
|
|
// There's no quick-searching capabilities of that container used anywhere except
|
|
// checkBrokenSockets and garbageCollect, which aren't performance-critical,
|
|
// whereas it's mainly used for getting the first element and iterating
|
|
// over elements, which is slow in case of std::set. It's also doubtful
|
|
// as to whether the sorting capability of std::set is properly used;
|
|
// the first is taken here, which is actually the socket with lowest
|
|
// possible descriptor value (as default operator< and ascending sorting
|
|
// used for std::set<SRTSOCKET> where SRTSOCKET=int).
|
|
//
|
|
// Consider using std::list or std::vector here.
|
|
|
|
u = *(ls->m_pQueuedSockets->begin());
|
|
ls->m_pAcceptSockets->insert(ls->m_pAcceptSockets->end(), u);
|
|
ls->m_pQueuedSockets->erase(ls->m_pQueuedSockets->begin());
|
|
accepted = true;
|
|
}
|
|
else if (!ls->m_pUDT->m_bSynRecving)
|
|
{
|
|
accepted = true;
|
|
}
|
|
|
|
if (!accepted && (ls->m_Status == SRTS_LISTENING))
|
|
pthread_cond_wait(&(ls->m_AcceptCond), &(ls->m_AcceptLock));
|
|
|
|
if (ls->m_pQueuedSockets->empty())
|
|
m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, UDT_EPOLL_IN, false);
|
|
}
|
|
|
|
if (u == CUDT::INVALID_SOCK)
|
|
{
|
|
// non-blocking receiving, no connection available
|
|
if (!ls->m_pUDT->m_bSynRecving)
|
|
throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0);
|
|
|
|
// listening socket is closed
|
|
throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0);
|
|
}
|
|
|
|
if ((addr != NULL) && (addrlen != NULL))
|
|
{
|
|
CUDTSocket* s = locate(u);
|
|
if (s == NULL)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
CGuard cg(s->m_ControlLock);
|
|
|
|
if (AF_INET == s->m_iIPversion)
|
|
*addrlen = sizeof(sockaddr_in);
|
|
else
|
|
*addrlen = sizeof(sockaddr_in6);
|
|
|
|
// copy address information of peer node
|
|
memcpy(addr, s->m_pPeerAddr, *addrlen);
|
|
}
|
|
|
|
return u;
|
|
}
|
|
|
|
int CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn)
|
|
{
|
|
CUDTSocket* s = locate(u);
|
|
if (!s)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
CGuard cg(s->m_ControlLock);
|
|
|
|
// XXX Consider translating this to using sockaddr_any,
|
|
// this should take out all the "IP version check" things.
|
|
if (AF_INET == s->m_iIPversion)
|
|
{
|
|
if (namelen != sizeof(sockaddr_in))
|
|
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
|
|
}
|
|
else
|
|
{
|
|
if (namelen != sizeof(sockaddr_in6))
|
|
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
|
|
}
|
|
|
|
// a socket can "connect" only if it is in INIT or OPENED status
|
|
if (s->m_Status == SRTS_INIT)
|
|
{
|
|
if (!s->m_pUDT->m_bRendezvous)
|
|
{
|
|
s->m_pUDT->open(); // XXX here use the AF_* family value from 'name'
|
|
updateMux(s); // <<---- updateMux
|
|
// -> C(Snd|Rcv)Queue::init
|
|
// -> pthread_create(...C(Snd|Rcv)Queue::worker...)
|
|
s->m_Status = SRTS_OPENED;
|
|
}
|
|
else
|
|
throw CUDTException(MJ_NOTSUP, MN_ISRENDUNBOUND, 0);
|
|
}
|
|
else if (s->m_Status != SRTS_OPENED)
|
|
throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 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 = SRTS_CONNECTING;
|
|
|
|
/*
|
|
* In blocking mode, connect can block for up to 30 seconds for
|
|
* rendez-vous mode. Holding the s->m_ControlLock prevent close
|
|
* from cancelling the connect
|
|
*/
|
|
try
|
|
{
|
|
// InvertedGuard unlocks in the constructor, then locks in the
|
|
// destructor, no matter if an exception has fired.
|
|
InvertedGuard l_unlocker( s->m_pUDT->m_bSynRecving ? &s->m_ControlLock : 0 );
|
|
s->m_pUDT->startConnect(name, forced_isn);
|
|
}
|
|
catch (CUDTException& e) // Interceptor, just to change the state.
|
|
{
|
|
s->m_Status = SRTS_OPENED;
|
|
throw e;
|
|
}
|
|
|
|
// record peer address
|
|
delete s->m_pPeerAddr;
|
|
if (AF_INET == s->m_iIPversion)
|
|
{
|
|
s->m_pPeerAddr = (sockaddr*)(new sockaddr_in);
|
|
memcpy(s->m_pPeerAddr, name, sizeof(sockaddr_in));
|
|
}
|
|
else
|
|
{
|
|
s->m_pPeerAddr = (sockaddr*)(new sockaddr_in6);
|
|
memcpy(s->m_pPeerAddr, name, sizeof(sockaddr_in6));
|
|
}
|
|
|
|
// CGuard destructor will delete cg and unlock s->m_ControlLock
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int CUDTUnited::close(const SRTSOCKET u)
|
|
{
|
|
CUDTSocket* s = locate(u);
|
|
if (!s)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
HLOGC(mglog.Debug, log << s->m_pUDT->CONID() << " CLOSE. Acquiring control lock");
|
|
|
|
CGuard socket_cg(s->m_ControlLock);
|
|
|
|
HLOGC(mglog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing from listening, closing CUDT)");
|
|
|
|
bool synch_close_snd = s->m_pUDT->m_bSynSending;
|
|
//bool synch_close_rcv = s->m_pUDT->m_bSynRecving;
|
|
|
|
if (s->m_Status == SRTS_LISTENING)
|
|
{
|
|
if (s->m_pUDT->m_bBroken)
|
|
return 0;
|
|
|
|
s->m_ClosureTimeStamp = CTimer::getTime();
|
|
s->m_pUDT->m_bBroken = true;
|
|
|
|
// Change towards original UDT:
|
|
// Leave all the closing activities for garbageCollect to happen,
|
|
// however remove the listener from the RcvQueue IMMEDIATELY.
|
|
// Even though garbageCollect would eventually remove the listener
|
|
// as well, there would be some time interval between now and the
|
|
// moment when it's done, and during this time the application will
|
|
// be unable to bind to this port that the about-to-delete listener
|
|
// is currently occupying (due to blocked slot in the RcvQueue).
|
|
|
|
HLOGC(mglog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing listener immediately)");
|
|
{
|
|
CGuard cg(s->m_pUDT->m_ConnectionLock);
|
|
s->m_pUDT->m_bListening = false;
|
|
s->m_pUDT->m_pRcvQueue->removeListener(s->m_pUDT);
|
|
}
|
|
|
|
// broadcast all "accept" waiting
|
|
pthread_mutex_lock(&(s->m_AcceptLock));
|
|
pthread_cond_broadcast(&(s->m_AcceptCond));
|
|
pthread_mutex_unlock(&(s->m_AcceptLock));
|
|
|
|
}
|
|
else
|
|
{
|
|
s->m_pUDT->close();
|
|
|
|
// synchronize with garbage collection.
|
|
HLOGC(mglog.Debug, log << "@" << u << "U::close done. GLOBAL CLOSE: " << s->m_pUDT->CONID() << ". Acquiring GLOBAL control lock");
|
|
CGuard manager_cg(m_ControlLock);
|
|
|
|
// since "s" is located before m_ControlLock, locate it again in case
|
|
// it became invalid
|
|
map<SRTSOCKET, CUDTSocket*>::iterator i = m_Sockets.find(u);
|
|
if ((i == m_Sockets.end()) || (i->second->m_Status == SRTS_CLOSED))
|
|
{
|
|
HLOGC(mglog.Debug, log << "@" << u << "U::close: NOT AN ACTIVE SOCKET, returning.");
|
|
return 0;
|
|
}
|
|
s = i->second;
|
|
|
|
s->m_Status = SRTS_CLOSED;
|
|
|
|
// a socket will not be immediately 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_ClosureTimeStamp = CTimer::getTime();
|
|
|
|
m_Sockets.erase(s->m_SocketID);
|
|
m_ClosedSockets[s->m_SocketID] = s;
|
|
HLOGC(mglog.Debug, log << "@" << u << "U::close: Socket MOVED TO CLOSED for collecting later.");
|
|
|
|
CTimer::triggerEvent();
|
|
}
|
|
|
|
HLOGC(mglog.Debug, log << "%" << u << ": GLOBAL: CLOSING DONE");
|
|
|
|
// Check if the ID is still in closed sockets before you access it
|
|
// (the last triggerEvent could have deleted it).
|
|
if ( synch_close_snd )
|
|
{
|
|
#if SRT_ENABLE_CLOSE_SYNCH
|
|
|
|
HLOGC(mglog.Debug, log << "@" << u << " GLOBAL CLOSING: sync-waiting for releasing sender resources...");
|
|
for (;;)
|
|
{
|
|
CSndBuffer* sb = s->m_pUDT->m_pSndBuffer;
|
|
|
|
// Disconnected from buffer - nothing more to check.
|
|
if (!sb)
|
|
{
|
|
HLOGC(mglog.Debug, log << "@" << u << " GLOBAL CLOSING: sending buffer disconnected. Allowed to close.");
|
|
break;
|
|
}
|
|
|
|
// Sender buffer empty
|
|
if (sb->getCurrBufSize() == 0)
|
|
{
|
|
HLOGC(mglog.Debug, log << "@" << u << " GLOBAL CLOSING: sending buffer depleted. Allowed to close.");
|
|
break;
|
|
}
|
|
|
|
// Ok, now you are keeping GC thread hands off the internal data.
|
|
// You can check then if it has already deleted the socket or not.
|
|
// The socket is either in m_ClosedSockets or is already gone.
|
|
|
|
// Done the other way, but still done. You can stop waiting.
|
|
bool isgone = false;
|
|
{
|
|
CGuard manager_cg(m_ControlLock);
|
|
isgone = m_ClosedSockets.count(u) == 0;
|
|
}
|
|
if (!isgone)
|
|
{
|
|
isgone = !s->m_pUDT->m_bOpened;
|
|
}
|
|
if (isgone)
|
|
{
|
|
HLOGC(mglog.Debug, log << "@" << u << " GLOBAL CLOSING: ... gone in the meantime, whatever. Exiting close().");
|
|
break;
|
|
}
|
|
|
|
HLOGC(mglog.Debug, log << "@" << u << " GLOBAL CLOSING: ... still waiting for any update.");
|
|
CTimer::EWait wt = CTimer::waitForEvent();
|
|
|
|
if ( wt == CTimer::WT_ERROR )
|
|
{
|
|
HLOGC(mglog.Debug, log << "GLOBAL CLOSING: ... ERROR WHEN WAITING FOR EVENT. Exiting close() to prevent hangup.");
|
|
break;
|
|
}
|
|
|
|
// Continue waiting in case when an event happened or 1s waiting time passed for checkpoint.
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
This code is PUT ASIDE for now.
|
|
Most likely this will be never required.
|
|
It had to hold the closing activity until the time when the receiver buffer is depleted.
|
|
However the closing of the socket should only happen when the receiver has received
|
|
an information about that the reading is no longer possible (error report from recv/recvfile).
|
|
When this happens, the receiver buffer is definitely depleted already and there's no need to check
|
|
anything.
|
|
|
|
Should there appear any other conditions in future under which the closing process should be
|
|
delayed until the receiver buffer is empty, this code can be filled here.
|
|
|
|
if ( synch_close_rcv )
|
|
{
|
|
...
|
|
}
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* name, int* namelen)
|
|
{
|
|
if (getStatus(u) != SRTS_CONNECTED)
|
|
throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
|
|
|
|
CUDTSocket* s = locate(u);
|
|
|
|
if (!s)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
if (!s->m_pUDT->m_bConnected || s->m_pUDT->m_bBroken)
|
|
throw CUDTException(MJ_CONNECTION, MN_NOCONN, 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 SRTSOCKET u, sockaddr* name, int* namelen)
|
|
{
|
|
CUDTSocket* s = locate(u);
|
|
|
|
if (!s)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
if (s->m_pUDT->m_bBroken)
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
|
|
if (s->m_Status == SRTS_INIT)
|
|
throw CUDTException(MJ_CONNECTION, MN_NOCONN, 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;
|
|
}
|
|
|
|
int CUDTUnited::select(
|
|
ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout)
|
|
{
|
|
uint64_t entertime = CTimer::getTime();
|
|
|
|
uint64_t to;
|
|
if (!timeout)
|
|
to = 0xFFFFFFFFFFFFFFFFULL;
|
|
else
|
|
to = timeout->tv_sec * 1000000 + timeout->tv_usec;
|
|
|
|
// initialize results
|
|
int count = 0;
|
|
set<SRTSOCKET> rs, ws, es;
|
|
|
|
// retrieve related UDT sockets
|
|
vector<CUDTSocket*> ru, wu, eu;
|
|
CUDTSocket* s;
|
|
if (readfds)
|
|
for (set<SRTSOCKET>::iterator i1 = readfds->begin();
|
|
i1 != readfds->end(); ++ i1)
|
|
{
|
|
if (getStatus(*i1) == SRTS_BROKEN)
|
|
{
|
|
rs.insert(*i1);
|
|
++ count;
|
|
}
|
|
else if (!(s = locate(*i1)))
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
else
|
|
ru.push_back(s);
|
|
}
|
|
if (writefds)
|
|
for (set<SRTSOCKET>::iterator i2 = writefds->begin();
|
|
i2 != writefds->end(); ++ i2)
|
|
{
|
|
if (getStatus(*i2) == SRTS_BROKEN)
|
|
{
|
|
ws.insert(*i2);
|
|
++ count;
|
|
}
|
|
else if (!(s = locate(*i2)))
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
else
|
|
wu.push_back(s);
|
|
}
|
|
if (exceptfds)
|
|
for (set<SRTSOCKET>::iterator i3 = exceptfds->begin();
|
|
i3 != exceptfds->end(); ++ i3)
|
|
{
|
|
if (getStatus(*i3) == SRTS_BROKEN)
|
|
{
|
|
es.insert(*i3);
|
|
++ count;
|
|
}
|
|
else if (!(s = locate(*i3)))
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0);
|
|
else
|
|
eu.push_back(s);
|
|
}
|
|
|
|
do
|
|
{
|
|
// query read sockets
|
|
for (vector<CUDTSocket*>::iterator j1 = ru.begin(); j1 != ru.end(); ++ j1)
|
|
{
|
|
s = *j1;
|
|
|
|
if ((s->m_pUDT->m_bConnected
|
|
&& s->m_pUDT->m_pRcvBuffer->isRcvDataReady()
|
|
)
|
|
|| (!s->m_pUDT->m_bListening
|
|
&& (s->m_pUDT->m_bBroken || !s->m_pUDT->m_bConnected))
|
|
|| (s->m_pUDT->m_bListening && (s->m_pQueuedSockets->size() > 0))
|
|
|| (s->m_Status == SRTS_CLOSED))
|
|
{
|
|
rs.insert(s->m_SocketID);
|
|
++ count;
|
|
}
|
|
}
|
|
|
|
// query write sockets
|
|
for (vector<CUDTSocket*>::iterator j2 = wu.begin(); j2 != wu.end(); ++ j2)
|
|
{
|
|
s = *j2;
|
|
|
|
if ((s->m_pUDT->m_bConnected
|
|
&& (s->m_pUDT->m_pSndBuffer->getCurrBufSize()
|
|
< s->m_pUDT->m_iSndBufSize))
|
|
|| s->m_pUDT->m_bBroken
|
|
|| !s->m_pUDT->m_bConnected
|
|
|| (s->m_Status == SRTS_CLOSED))
|
|
{
|
|
ws.insert(s->m_SocketID);
|
|
++ count;
|
|
}
|
|
}
|
|
|
|
// query exceptions on sockets
|
|
for (vector<CUDTSocket*>::iterator j3 = eu.begin(); j3 != eu.end(); ++ j3)
|
|
{
|
|
// check connection request status, not supported now
|
|
}
|
|
|
|
if (0 < count)
|
|
break;
|
|
|
|
CTimer::waitForEvent();
|
|
} while (to > CTimer::getTime() - entertime);
|
|
|
|
if (readfds)
|
|
*readfds = rs;
|
|
|
|
if (writefds)
|
|
*writefds = ws;
|
|
|
|
if (exceptfds)
|
|
*exceptfds = es;
|
|
|
|
return count;
|
|
}
|
|
|
|
int CUDTUnited::selectEx(
|
|
const vector<SRTSOCKET>& fds,
|
|
vector<SRTSOCKET>* readfds,
|
|
vector<SRTSOCKET>* writefds,
|
|
vector<SRTSOCKET>* exceptfds,
|
|
int64_t msTimeOut)
|
|
{
|
|
uint64_t entertime = CTimer::getTime();
|
|
|
|
uint64_t to;
|
|
if (msTimeOut >= 0)
|
|
to = msTimeOut * 1000;
|
|
else
|
|
to = 0xFFFFFFFFFFFFFFFFULL;
|
|
|
|
// initialize results
|
|
int count = 0;
|
|
if (readfds)
|
|
readfds->clear();
|
|
if (writefds)
|
|
writefds->clear();
|
|
if (exceptfds)
|
|
exceptfds->clear();
|
|
|
|
do
|
|
{
|
|
for (vector<SRTSOCKET>::const_iterator i = fds.begin();
|
|
i != fds.end(); ++ i)
|
|
{
|
|
CUDTSocket* s = locate(*i);
|
|
|
|
if ((!s) || s->m_pUDT->m_bBroken || (s->m_Status == SRTS_CLOSED))
|
|
{
|
|
if (exceptfds)
|
|
{
|
|
exceptfds->push_back(*i);
|
|
++ count;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (readfds)
|
|
{
|
|
if ((s->m_pUDT->m_bConnected
|
|
&& s->m_pUDT->m_pRcvBuffer->isRcvDataReady()
|
|
)
|
|
|| (s->m_pUDT->m_bListening
|
|
&& (s->m_pQueuedSockets->size() > 0)))
|
|
{
|
|
readfds->push_back(s->m_SocketID);
|
|
++ count;
|
|
}
|
|
}
|
|
|
|
if (writefds)
|
|
{
|
|
if (s->m_pUDT->m_bConnected
|
|
&& (s->m_pUDT->m_pSndBuffer->getCurrBufSize()
|
|
< s->m_pUDT->m_iSndBufSize))
|
|
{
|
|
writefds->push_back(s->m_SocketID);
|
|
++ count;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count > 0)
|
|
break;
|
|
|
|
CTimer::waitForEvent();
|
|
} while (to > CTimer::getTime() - entertime);
|
|
|
|
return count;
|
|
}
|
|
|
|
int CUDTUnited::epoll_create()
|
|
{
|
|
return m_EPoll.create();
|
|
}
|
|
|
|
int CUDTUnited::epoll_add_usock(
|
|
const int eid, const SRTSOCKET u, const int* events)
|
|
{
|
|
CUDTSocket* s = locate(u);
|
|
int ret = -1;
|
|
if (s)
|
|
{
|
|
ret = m_EPoll.add_usock(eid, u, events);
|
|
s->m_pUDT->addEPoll(eid);
|
|
}
|
|
else
|
|
{
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int CUDTUnited::epoll_add_ssock(
|
|
const int eid, const SYSSOCKET s, const int* events)
|
|
{
|
|
return m_EPoll.add_ssock(eid, s, events);
|
|
}
|
|
|
|
int CUDTUnited::epoll_update_usock(
|
|
const int eid, const SRTSOCKET u, const int* events)
|
|
{
|
|
CUDTSocket* s = locate(u);
|
|
int ret = -1;
|
|
if (s)
|
|
{
|
|
ret = m_EPoll.update_usock(eid, u, events);
|
|
s->m_pUDT->addEPoll(eid);
|
|
}
|
|
else
|
|
{
|
|
throw CUDTException(MJ_NOTSUP, MN_SIDINVAL);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int CUDTUnited::epoll_update_ssock(
|
|
const int eid, const SYSSOCKET s, const int* events)
|
|
{
|
|
return m_EPoll.update_ssock(eid, s, events);
|
|
}
|
|
|
|
int CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u)
|
|
{
|
|
int ret = m_EPoll.remove_usock(eid, u);
|
|
|
|
CUDTSocket* s = locate(u);
|
|
if (s)
|
|
{
|
|
s->m_pUDT->removeEPoll(eid);
|
|
}
|
|
//else
|
|
//{
|
|
// throw CUDTException(MJ_NOTSUP, MN_SIDINVAL);
|
|
//}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s)
|
|
{
|
|
return m_EPoll.remove_ssock(eid, s);
|
|
}
|
|
|
|
int CUDTUnited::epoll_wait(
|
|
const int eid,
|
|
set<SRTSOCKET>* readfds,
|
|
set<SRTSOCKET>* writefds,
|
|
int64_t msTimeOut,
|
|
set<SYSSOCKET>* lrfds,
|
|
set<SYSSOCKET>* lwfds)
|
|
{
|
|
return m_EPoll.wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds);
|
|
}
|
|
|
|
int CUDTUnited::epoll_uwait(
|
|
const int eid,
|
|
SRT_EPOLL_EVENT* fdsSet,
|
|
int fdsSize,
|
|
int64_t msTimeOut)
|
|
{
|
|
return m_EPoll.uwait(eid, fdsSet, fdsSize, msTimeOut);
|
|
}
|
|
|
|
int32_t CUDTUnited::epoll_set(int eid, int32_t flags)
|
|
{
|
|
return m_EPoll.setflags(eid, flags);
|
|
}
|
|
|
|
int CUDTUnited::epoll_release(const int eid)
|
|
{
|
|
return m_EPoll.release(eid);
|
|
}
|
|
|
|
CUDTSocket* CUDTUnited::locate(const SRTSOCKET u)
|
|
{
|
|
CGuard cg(m_ControlLock);
|
|
|
|
map<SRTSOCKET, CUDTSocket*>::iterator i = m_Sockets.find(u);
|
|
|
|
if ((i == m_Sockets.end()) || (i->second->m_Status == SRTS_CLOSED))
|
|
return NULL;
|
|
|
|
return i->second;
|
|
}
|
|
|
|
CUDTSocket* CUDTUnited::locate(
|
|
const sockaddr* peer,
|
|
const SRTSOCKET id,
|
|
int32_t isn)
|
|
{
|
|
CGuard cg(m_ControlLock);
|
|
|
|
map<int64_t, set<SRTSOCKET> >::iterator i = m_PeerRec.find(
|
|
CUDTSocket::getPeerSpec(id, isn));
|
|
if (i == m_PeerRec.end())
|
|
return NULL;
|
|
|
|
for (set<SRTSOCKET>::iterator j = i->second.begin();
|
|
j != i->second.end(); ++ j)
|
|
{
|
|
map<SRTSOCKET, CUDTSocket*>::iterator k = m_Sockets.find(*j);
|
|
// this socket might have been closed and moved m_ClosedSockets
|
|
if (k == m_Sockets.end())
|
|
continue;
|
|
|
|
if (CIPAddress::ipcmp(
|
|
peer, k->second->m_pPeerAddr, k->second->m_iIPversion))
|
|
{
|
|
return k->second;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CUDTUnited::checkBrokenSockets()
|
|
{
|
|
CGuard cg(m_ControlLock);
|
|
|
|
// set of sockets To Be Closed and To Be Removed
|
|
vector<SRTSOCKET> tbc;
|
|
vector<SRTSOCKET> tbr;
|
|
|
|
for (map<SRTSOCKET, CUDTSocket*>::iterator i = m_Sockets.begin();
|
|
i != m_Sockets.end(); ++ i)
|
|
{
|
|
CUDTSocket* s = i->second;
|
|
|
|
// HLOGF(mglog.Debug, "checking EXISTING socket: %d\n", i->first);
|
|
// check broken connection
|
|
if (s->m_pUDT->m_bBroken)
|
|
{
|
|
if (s->m_Status == SRTS_LISTENING)
|
|
{
|
|
uint64_t elapsed = CTimer::getTime() - s->m_ClosureTimeStamp;
|
|
// for a listening socket, it should wait an extra 3 seconds
|
|
// in case a client is connecting
|
|
if (elapsed < 3000000) // XXX MAKE A SYMBOLIC CONSTANT HERE!
|
|
{
|
|
// HLOGF(mglog.Debug, "STILL KEEPING socket %d
|
|
// (listener, too early, w8 %fs)\n", i->first,
|
|
// double(elapsed)/1000000);
|
|
continue;
|
|
}
|
|
}
|
|
else if ((s->m_pUDT->m_pRcvBuffer != NULL)
|
|
// FIXED: calling isRcvDataAvailable() just to get the information
|
|
// whether there are any data waiting in the buffer,
|
|
// NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when
|
|
// this function is called (isRcvDataReady also checks if the
|
|
// available data is "ready to play").
|
|
&& s->m_pUDT->m_pRcvBuffer->isRcvDataAvailable()
|
|
&& (s->m_pUDT->m_iBrokenCounter -- > 0))
|
|
{
|
|
// HLOGF(mglog.Debug, "STILL KEEPING socket (still have data):
|
|
// %d\n", i->first);
|
|
// if there is still data in the receiver buffer, wait longer
|
|
continue;
|
|
}
|
|
|
|
// HLOGF(mglog.Debug, "moving socket to CLOSED: %d\n", i->first);
|
|
|
|
//close broken connections and start removal timer
|
|
s->m_Status = SRTS_CLOSED;
|
|
s->m_ClosureTimeStamp = CTimer::getTime();
|
|
tbc.push_back(i->first);
|
|
m_ClosedSockets[i->first] = s;
|
|
|
|
// remove from listener's queue
|
|
map<SRTSOCKET, CUDTSocket*>::iterator ls = m_Sockets.find(
|
|
s->m_ListenSocket);
|
|
if (ls == m_Sockets.end())
|
|
{
|
|
ls = m_ClosedSockets.find(s->m_ListenSocket);
|
|
if (ls == m_ClosedSockets.end())
|
|
continue;
|
|
}
|
|
|
|
CGuard::enterCS(ls->second->m_AcceptLock);
|
|
ls->second->m_pQueuedSockets->erase(s->m_SocketID);
|
|
ls->second->m_pAcceptSockets->erase(s->m_SocketID);
|
|
CGuard::leaveCS(ls->second->m_AcceptLock);
|
|
}
|
|
}
|
|
|
|
for (map<SRTSOCKET, CUDTSocket*>::iterator j = m_ClosedSockets.begin();
|
|
j != m_ClosedSockets.end(); ++ j)
|
|
{
|
|
// HLOGF(mglog.Debug, "checking CLOSED socket: %d\n", j->first);
|
|
if (j->second->m_pUDT->m_ullLingerExpiration > 0)
|
|
{
|
|
// asynchronous close:
|
|
if ((!j->second->m_pUDT->m_pSndBuffer)
|
|
|| (0 == j->second->m_pUDT->m_pSndBuffer->getCurrBufSize())
|
|
|| (j->second->m_pUDT->m_ullLingerExpiration <= CTimer::getTime()))
|
|
{
|
|
j->second->m_pUDT->m_ullLingerExpiration = 0;
|
|
j->second->m_pUDT->m_bClosing = true;
|
|
j->second->m_ClosureTimeStamp = CTimer::getTime();
|
|
}
|
|
}
|
|
|
|
// timeout 1 second to destroy a socket AND it has been removed from
|
|
// RcvUList
|
|
if ((CTimer::getTime() - j->second->m_ClosureTimeStamp > 1000000)
|
|
&& ((!j->second->m_pUDT->m_pRNode)
|
|
|| !j->second->m_pUDT->m_pRNode->m_bOnList))
|
|
{
|
|
// HLOGF(mglog.Debug, "will unref socket: %d\n", j->first);
|
|
tbr.push_back(j->first);
|
|
}
|
|
}
|
|
|
|
// move closed sockets to the ClosedSockets structure
|
|
for (vector<SRTSOCKET>::iterator k = tbc.begin(); k != tbc.end(); ++ k)
|
|
m_Sockets.erase(*k);
|
|
|
|
// remove those timeout sockets
|
|
for (vector<SRTSOCKET>::iterator l = tbr.begin(); l != tbr.end(); ++ l)
|
|
removeSocket(*l);
|
|
}
|
|
|
|
void CUDTUnited::removeSocket(const SRTSOCKET u)
|
|
{
|
|
map<SRTSOCKET, CUDTSocket*>::iterator i = m_ClosedSockets.find(u);
|
|
|
|
// invalid socket ID
|
|
if (i == m_ClosedSockets.end())
|
|
return;
|
|
|
|
// decrease multiplexer reference count, and remove it if necessary
|
|
const int mid = i->second->m_iMuxID;
|
|
|
|
if (i->second->m_pQueuedSockets)
|
|
{
|
|
CGuard cg(i->second->m_AcceptLock);
|
|
|
|
// if it is a listener, close all un-accepted sockets in its queue
|
|
// and remove them later
|
|
for (set<SRTSOCKET>::iterator q = i->second->m_pQueuedSockets->begin();
|
|
q != i->second->m_pQueuedSockets->end(); ++ q)
|
|
{
|
|
m_Sockets[*q]->m_pUDT->m_bBroken = true;
|
|
m_Sockets[*q]->m_pUDT->close();
|
|
m_Sockets[*q]->m_ClosureTimeStamp = CTimer::getTime();
|
|
m_Sockets[*q]->m_Status = SRTS_CLOSED;
|
|
m_ClosedSockets[*q] = m_Sockets[*q];
|
|
m_Sockets.erase(*q);
|
|
}
|
|
|
|
}
|
|
|
|
// remove from peer rec
|
|
map<int64_t, set<SRTSOCKET> >::iterator j = m_PeerRec.find(
|
|
i->second->getPeerSpec());
|
|
if (j != m_PeerRec.end())
|
|
{
|
|
j->second.erase(u);
|
|
if (j->second.empty())
|
|
m_PeerRec.erase(j);
|
|
}
|
|
|
|
/*
|
|
* Socket may be deleted while still having ePoll events set that would
|
|
* remains forever causing epoll_wait to unblock continuously for inexistent
|
|
* sockets. Get rid of all events for this socket.
|
|
*/
|
|
m_EPoll.update_events(u, i->second->m_pUDT->m_sPollID,
|
|
UDT_EPOLL_IN|UDT_EPOLL_OUT|UDT_EPOLL_ERR, false);
|
|
|
|
// delete this one
|
|
HLOGC(mglog.Debug, log << "GC/removeSocket: closing associated UDT %" << u);
|
|
i->second->m_pUDT->close();
|
|
HLOGC(mglog.Debug, log << "GC/removeSocket: DELETING SOCKET %" << u);
|
|
delete i->second;
|
|
m_ClosedSockets.erase(i);
|
|
|
|
if (mid == -1)
|
|
return;
|
|
|
|
map<int, CMultiplexer>::iterator m;
|
|
m = m_mMultiplexer.find(mid);
|
|
if (m == m_mMultiplexer.end())
|
|
{
|
|
LOGC(mglog.Fatal, log << "IPE: For socket %" << u << " MUXER id=" << mid << " NOT FOUND!");
|
|
return;
|
|
}
|
|
|
|
CMultiplexer& mx = m->second;
|
|
|
|
mx.m_iRefCount --;
|
|
// HLOGF(mglog.Debug, "unrefing underlying socket for %u: %u\n",
|
|
// u, mx.m_iRefCount);
|
|
if (0 == mx.m_iRefCount)
|
|
{
|
|
HLOGC(mglog.Debug, log << "MUXER id=" << mid << " lost last socket %"
|
|
<< u << " - deleting muxer bound to port "
|
|
<< mx.m_pChannel->bindAddressAny().hport());
|
|
// The channel has no access to the queues and
|
|
// it looks like the multiplexer is the master of all of them.
|
|
// The queues must be silenced before closing the channel
|
|
// because this will cause error to be returned in any operation
|
|
// being currently done in the queues, if any.
|
|
mx.m_pSndQueue->setClosing();
|
|
mx.m_pRcvQueue->setClosing();
|
|
delete mx.m_pSndQueue;
|
|
delete mx.m_pRcvQueue;
|
|
mx.m_pChannel->close();
|
|
delete mx.m_pTimer;
|
|
delete mx.m_pChannel;
|
|
m_mMultiplexer.erase(m);
|
|
}
|
|
}
|
|
|
|
void CUDTUnited::setError(CUDTException* e)
|
|
{
|
|
delete (CUDTException*)pthread_getspecific(m_TLSError);
|
|
pthread_setspecific(m_TLSError, e);
|
|
}
|
|
|
|
CUDTException* CUDTUnited::getError()
|
|
{
|
|
if(!pthread_getspecific(m_TLSError))
|
|
pthread_setspecific(m_TLSError, new CUDTException);
|
|
return (CUDTException*)pthread_getspecific(m_TLSError);
|
|
}
|
|
|
|
|
|
void CUDTUnited::updateMux(
|
|
CUDTSocket* s, const sockaddr* addr, const UDPSOCKET* udpsock)
|
|
{
|
|
CGuard cg(m_ControlLock);
|
|
|
|
if ((s->m_pUDT->m_bReuseAddr) && (addr))
|
|
{
|
|
int port = (AF_INET == s->m_pUDT->m_iIPversion)
|
|
? ntohs(((sockaddr_in*)addr)->sin_port)
|
|
: ntohs(((sockaddr_in6*)addr)->sin6_port);
|
|
|
|
// find a reusable address
|
|
for (map<int, CMultiplexer>::iterator i = m_mMultiplexer.begin();
|
|
i != m_mMultiplexer.end(); ++ i)
|
|
{
|
|
if ((i->second.m_iIPversion == s->m_pUDT->m_iIPversion)
|
|
&& (i->second.m_iMSS == s->m_pUDT->m_iMSS)
|
|
#ifdef SRT_ENABLE_IPOPTS
|
|
&& (i->second.m_iIpTTL == s->m_pUDT->m_iIpTTL)
|
|
&& (i->second.m_iIpToS == s->m_pUDT->m_iIpToS)
|
|
#endif
|
|
&& (i->second.m_iIpV6Only == s->m_pUDT->m_iIpV6Only)
|
|
&& i->second.m_bReusable)
|
|
{
|
|
if (i->second.m_iPort == port)
|
|
{
|
|
// HLOGF(mglog.Debug, "reusing multiplexer for port
|
|
// %hd\n", port);
|
|
// reuse the existing multiplexer
|
|
++ i->second.m_iRefCount;
|
|
s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue;
|
|
s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue;
|
|
s->m_iMuxID = i->second.m_iID;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// a new multiplexer is needed
|
|
CMultiplexer m;
|
|
m.m_iMSS = s->m_pUDT->m_iMSS;
|
|
m.m_iIPversion = s->m_pUDT->m_iIPversion;
|
|
#ifdef SRT_ENABLE_IPOPTS
|
|
m.m_iIpTTL = s->m_pUDT->m_iIpTTL;
|
|
m.m_iIpToS = s->m_pUDT->m_iIpToS;
|
|
#endif
|
|
m.m_iRefCount = 1;
|
|
m.m_iIpV6Only = s->m_pUDT->m_iIpV6Only;
|
|
m.m_bReusable = s->m_pUDT->m_bReuseAddr;
|
|
m.m_iID = s->m_SocketID;
|
|
|
|
m.m_pChannel = new CChannel(s->m_pUDT->m_iIPversion);
|
|
#ifdef SRT_ENABLE_IPOPTS
|
|
m.m_pChannel->setIpTTL(s->m_pUDT->m_iIpTTL);
|
|
m.m_pChannel->setIpToS(s->m_pUDT->m_iIpToS);
|
|
#endif
|
|
m.m_pChannel->setSndBufSize(s->m_pUDT->m_iUDPSndBufSize);
|
|
m.m_pChannel->setRcvBufSize(s->m_pUDT->m_iUDPRcvBufSize);
|
|
if (s->m_pUDT->m_iIpV6Only != -1)
|
|
m.m_pChannel->setIpV6Only(s->m_pUDT->m_iIpV6Only);
|
|
|
|
try
|
|
{
|
|
if (udpsock)
|
|
m.m_pChannel->attach(*udpsock);
|
|
else
|
|
m.m_pChannel->open(addr);
|
|
}
|
|
catch (CUDTException& e)
|
|
{
|
|
m.m_pChannel->close();
|
|
delete m.m_pChannel;
|
|
throw;
|
|
}
|
|
|
|
// XXX Simplify this. Use sockaddr_any.
|
|
sockaddr* sa = (AF_INET == s->m_pUDT->m_iIPversion)
|
|
? (sockaddr*) new sockaddr_in
|
|
: (sockaddr*) new sockaddr_in6;
|
|
m.m_pChannel->getSockAddr(sa);
|
|
m.m_iPort = (AF_INET == s->m_pUDT->m_iIPversion)
|
|
? ntohs(((sockaddr_in*)sa)->sin_port)
|
|
: ntohs(((sockaddr_in6*)sa)->sin6_port);
|
|
|
|
if (AF_INET == s->m_pUDT->m_iIPversion)
|
|
delete (sockaddr_in*)sa;
|
|
else
|
|
delete (sockaddr_in6*)sa;
|
|
|
|
m.m_pTimer = new CTimer;
|
|
|
|
m.m_pSndQueue = new CSndQueue;
|
|
m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer);
|
|
m.m_pRcvQueue = new CRcvQueue;
|
|
m.m_pRcvQueue->init(
|
|
32, s->m_pUDT->maxPayloadSize(), 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;
|
|
|
|
HLOGF(mglog.Debug,
|
|
"creating new multiplexer for port %i\n", m.m_iPort);
|
|
}
|
|
|
|
// XXX This functionality needs strong refactoring.
|
|
//
|
|
// This function is going to find a multiplexer for the port contained
|
|
// in the 'ls' listening socket, by searching through the multiplexer
|
|
// container.
|
|
//
|
|
// Somehow, however, it's not even predicted a situation that the multiplexer
|
|
// for that port doesn't exist - that is, this function WILL find the
|
|
// multiplexer. How can it be so certain? It's because the listener has
|
|
// already created the multiplexer during the call to bind(), so if it
|
|
// didn't, this function wouldn't even have a chance to be called.
|
|
//
|
|
// Why can't then the multiplexer be recorded in the 'ls' listening socket data
|
|
// to be accessed immediately, especially when one listener can't bind to more
|
|
// than one multiplexer at a time (well, even if it could, there's still no
|
|
// reason why this should be extracted by "querying")?
|
|
//
|
|
// Maybe because the multiplexer container is a map, not a list.
|
|
// Why is this then a map? Because it's addressed by MuxID. Why do we need
|
|
// mux id? Because we don't have a list... ?
|
|
//
|
|
// But what's the multiplexer ID? It's a socket ID for which it was originally
|
|
// created.
|
|
//
|
|
// Is this then shared? Yes, only between the listener socket and the accepted
|
|
// sockets, or in case of "bound" connecting sockets (by binding you can
|
|
// enforce the port number, which can be the same for multiple SRT sockets).
|
|
// Not shared in case of unbound connecting socket or rendezvous socket.
|
|
//
|
|
// Ok, in which situation do we need dispatching by mux id? Only when the
|
|
// socket is being deleted. How does the deleting procedure know the muxer id?
|
|
// Because it is recorded here at the time when it's found, as... the socket ID
|
|
// of the actual listener socket being actually the first socket to create the
|
|
// multiplexer, so the multiplexer gets its id.
|
|
//
|
|
// Still, no reasons found why the socket can't contain a list iterator to a
|
|
// multiplexer INSTEAD of m_iMuxID. There's no danger in this solution because
|
|
// the multiplexer is never deleted until there's at least one socket using it.
|
|
//
|
|
// The multiplexer may even physically be contained in the CUDTUnited object,
|
|
// just track the multiple users of it (the listener and the accepted sockets).
|
|
// When deleting, you simply "unsubscribe" yourself from the multiplexer, which
|
|
// will unref it and remove the list element by the iterator kept by the
|
|
// socket.
|
|
void CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls)
|
|
{
|
|
CGuard cg(m_ControlLock);
|
|
|
|
int port = (AF_INET == ls->m_iIPversion)
|
|
? ntohs(((sockaddr_in*)ls->m_pSelfAddr)->sin_port)
|
|
: ntohs(((sockaddr_in6*)ls->m_pSelfAddr)->sin6_port);
|
|
|
|
// find the listener's address
|
|
for (map<int, CMultiplexer>::iterator i = m_mMultiplexer.begin();
|
|
i != m_mMultiplexer.end(); ++ i)
|
|
{
|
|
if (i->second.m_iPort == port)
|
|
{
|
|
HLOGF(mglog.Debug,
|
|
"updateMux: reusing multiplexer for port %i\n", port);
|
|
// reuse the existing multiplexer
|
|
++ i->second.m_iRefCount;
|
|
s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue;
|
|
s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue;
|
|
s->m_iMuxID = i->second.m_iID;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void* CUDTUnited::garbageCollect(void* p)
|
|
{
|
|
CUDTUnited* self = (CUDTUnited*)p;
|
|
|
|
THREAD_STATE_INIT("SRT:GC");
|
|
|
|
CGuard gcguard(self->m_GCStopLock);
|
|
|
|
while (!self->m_bClosing)
|
|
{
|
|
INCREMENT_THREAD_ITERATIONS();
|
|
self->checkBrokenSockets();
|
|
|
|
//#ifdef _WIN32
|
|
// self->checkTLSValue();
|
|
//#endif
|
|
|
|
timespec timeout;
|
|
#if ENABLE_MONOTONIC_CLOCK
|
|
clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
timeout.tv_sec++;
|
|
HLOGC(mglog.Debug, log << "GC: sleep until " << FormatTime(uint64_t(timeout.tv_nsec)/1000 + 1000000*(timeout.tv_sec)));
|
|
#else
|
|
timeval now;
|
|
gettimeofday(&now, 0);
|
|
timeout.tv_sec = now.tv_sec + 1;
|
|
timeout.tv_nsec = now.tv_usec * 1000;
|
|
|
|
HLOGC(mglog.Debug, log << "GC: sleep until " << FormatTime(uint64_t(now.tv_usec) + 1000000*(timeout.tv_sec)));
|
|
#endif
|
|
pthread_cond_timedwait(
|
|
&self->m_GCStopCond, &self->m_GCStopLock, &timeout);
|
|
}
|
|
|
|
// remove all sockets and multiplexers
|
|
HLOGC(mglog.Debug, log << "GC: GLOBAL EXIT - releasing all pending sockets. Acquring control lock...");
|
|
CGuard::enterCS(self->m_ControlLock);
|
|
for (map<SRTSOCKET, CUDTSocket*>::iterator i = self->m_Sockets.begin();
|
|
i != self->m_Sockets.end(); ++ i)
|
|
{
|
|
i->second->m_pUDT->m_bBroken = true;
|
|
i->second->m_pUDT->close();
|
|
i->second->m_Status = SRTS_CLOSED;
|
|
i->second->m_ClosureTimeStamp = CTimer::getTime();
|
|
self->m_ClosedSockets[i->first] = i->second;
|
|
|
|
// remove from listener's queue
|
|
map<SRTSOCKET, CUDTSocket*>::iterator ls = self->m_Sockets.find(
|
|
i->second->m_ListenSocket);
|
|
if (ls == self->m_Sockets.end())
|
|
{
|
|
ls = self->m_ClosedSockets.find(i->second->m_ListenSocket);
|
|
if (ls == self->m_ClosedSockets.end())
|
|
continue;
|
|
}
|
|
|
|
CGuard::enterCS(ls->second->m_AcceptLock);
|
|
ls->second->m_pQueuedSockets->erase(i->second->m_SocketID);
|
|
ls->second->m_pAcceptSockets->erase(i->second->m_SocketID);
|
|
CGuard::leaveCS(ls->second->m_AcceptLock);
|
|
}
|
|
self->m_Sockets.clear();
|
|
|
|
for (map<SRTSOCKET, CUDTSocket*>::iterator j = self->m_ClosedSockets.begin();
|
|
j != self->m_ClosedSockets.end(); ++ j)
|
|
{
|
|
j->second->m_ClosureTimeStamp = 0;
|
|
}
|
|
CGuard::leaveCS(self->m_ControlLock);
|
|
|
|
HLOGC(mglog.Debug, log << "GC: GLOBAL EXIT - releasing all CLOSED sockets.");
|
|
while (true)
|
|
{
|
|
self->checkBrokenSockets();
|
|
|
|
CGuard::enterCS(self->m_ControlLock);
|
|
bool empty = self->m_ClosedSockets.empty();
|
|
CGuard::leaveCS(self->m_ControlLock);
|
|
|
|
if (empty)
|
|
break;
|
|
|
|
CTimer::sleep();
|
|
}
|
|
|
|
THREAD_EXIT();
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int CUDT::startup()
|
|
{
|
|
return s_UDTUnited.startup();
|
|
}
|
|
|
|
int CUDT::cleanup()
|
|
{
|
|
return s_UDTUnited.cleanup();
|
|
}
|
|
|
|
SRTSOCKET CUDT::socket(int af, int, int)
|
|
{
|
|
if (!s_UDTUnited.m_bGCStatus)
|
|
s_UDTUnited.startup();
|
|
|
|
try
|
|
{
|
|
return s_UDTUnited.newSocket(af, 0);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return INVALID_SOCK;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return INVALID_SOCK;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "socket: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name()
|
|
<< ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return INVALID_SOCK;
|
|
}
|
|
}
|
|
|
|
int CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.bind(u, name, namelen);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "bind: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name()
|
|
<< ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.bind(u, udpsock);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "bind/udp: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::listen(SRTSOCKET u, int backlog)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.listen(u, backlog);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "listen: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
SRTSOCKET CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.accept(u, addr, addrlen);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return INVALID_SOCK;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "accept: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return INVALID_SOCK;
|
|
}
|
|
}
|
|
|
|
int CUDT::connect(
|
|
SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.connect(u, name, namelen, forced_isn);
|
|
}
|
|
catch (const CUDTException e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "connect: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::close(SRTSOCKET u)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.close(u);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "close: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.getpeername(u, name, namelen);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "getpeername: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.getsockname(u, name, namelen);;
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "getsockname: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::getsockopt(
|
|
SRTSOCKET u, int, SRT_SOCKOPT optname, void* optval, int* optlen)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
udt->getOpt(optname, optval, *optlen);
|
|
return 0;
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "getsockopt: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
udt->setOpt(optname, optval, optlen);
|
|
return 0;
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "setsockopt: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::send(SRTSOCKET u, const char* buf, int len, int)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
return udt->send(buf, len);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "send: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::recv(SRTSOCKET u, char* buf, int len, int)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
return udt->recv(buf, len);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "recv: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::sendmsg(
|
|
SRTSOCKET u, const char* buf, int len, int ttl, bool inorder,
|
|
uint64_t srctime)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
return udt->sendmsg(buf, len, ttl, inorder, srctime);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "sendmsg: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::sendmsg2(
|
|
SRTSOCKET u, const char* buf, int len, ref_t<SRT_MSGCTRL> r_m)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
return udt->sendmsg2(buf, len, r_m);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "sendmsg: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::recvmsg(SRTSOCKET u, char* buf, int len, uint64_t& srctime)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
return udt->recvmsg(buf, len, srctime);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "recvmsg: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, ref_t<SRT_MSGCTRL> r_m)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
return udt->recvmsg2(buf, len, r_m);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "recvmsg: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
int64_t CUDT::sendfile(
|
|
SRTSOCKET u, fstream& ifs, int64_t& offset, int64_t size, int block)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
return udt->sendfile(ifs, offset, size, block);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "sendfile: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int64_t CUDT::recvfile(
|
|
SRTSOCKET u, fstream& ofs, int64_t& offset, int64_t size, int block)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
return udt->recvfile(ofs, offset, size, block);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "recvfile: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::select(
|
|
int,
|
|
ud_set* readfds,
|
|
ud_set* writefds,
|
|
ud_set* exceptfds,
|
|
const timeval* timeout)
|
|
{
|
|
if ((!readfds) && (!writefds) && (!exceptfds))
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_NOTSUP, MN_INVAL, 0));
|
|
return ERROR;
|
|
}
|
|
|
|
try
|
|
{
|
|
return s_UDTUnited.select(readfds, writefds, exceptfds, timeout);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "select: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::selectEx(
|
|
const vector<SRTSOCKET>& fds,
|
|
vector<SRTSOCKET>* readfds,
|
|
vector<SRTSOCKET>* writefds,
|
|
vector<SRTSOCKET>* exceptfds,
|
|
int64_t msTimeOut)
|
|
{
|
|
if ((!readfds) && (!writefds) && (!exceptfds))
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_NOTSUP, MN_INVAL, 0));
|
|
return ERROR;
|
|
}
|
|
|
|
try
|
|
{
|
|
return s_UDTUnited.selectEx(fds, readfds, writefds, exceptfds, msTimeOut);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "selectEx: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::epoll_create()
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_create();
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_create: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_add_usock(eid, u, events);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_add_usock: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_add_ssock(eid, s, events);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_add_ssock: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::epoll_update_usock(
|
|
const int eid, const SRTSOCKET u, const int* events)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_update_usock(eid, u, events);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_update_usock: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::epoll_update_ssock(
|
|
const int eid, const SYSSOCKET s, const int* events)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_update_ssock(eid, s, events);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_update_ssock: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
int CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_remove_usock(eid, u);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_remove_usock: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_remove_ssock(eid, s);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_remove_ssock: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::epoll_wait(
|
|
const int eid,
|
|
set<SRTSOCKET>* readfds,
|
|
set<SRTSOCKET>* writefds,
|
|
int64_t msTimeOut,
|
|
set<SYSSOCKET>* lrfds,
|
|
set<SYSSOCKET>* lwfds)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_wait(
|
|
eid, readfds, writefds, msTimeOut, lrfds, lwfds);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_wait: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::epoll_uwait(
|
|
const int eid,
|
|
SRT_EPOLL_EVENT* fdsSet,
|
|
int fdsSize,
|
|
int64_t msTimeOut)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_uwait(eid, fdsSet, fdsSize, msTimeOut);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_uwait: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int32_t CUDT::epoll_set(
|
|
const int eid,
|
|
int32_t flags)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_set(eid, flags);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_set: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
int CUDT::epoll_release(const int eid)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.epoll_release(eid);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "epoll_release: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
CUDTException& CUDT::getlasterror()
|
|
{
|
|
return *s_UDTUnited.getError();
|
|
}
|
|
|
|
int CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous)
|
|
{
|
|
try
|
|
{
|
|
CUDT* udt = s_UDTUnited.lookup(u);
|
|
udt->bstats(perf, clear, instantaneous);
|
|
return 0;
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return ERROR;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "bstats: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
CUDT* CUDT::getUDTHandle(SRTSOCKET u)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.lookup(u);
|
|
}
|
|
catch (const CUDTException& e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return NULL;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "getUDTHandle: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
vector<SRTSOCKET> CUDT::existingSockets()
|
|
{
|
|
vector<SRTSOCKET> out;
|
|
for (std::map<SRTSOCKET,CUDTSocket*>::iterator i
|
|
= s_UDTUnited.m_Sockets.begin();
|
|
i != s_UDTUnited.m_Sockets.end(); ++i)
|
|
{
|
|
out.push_back(i->first);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
SRT_SOCKSTATUS CUDT::getsockstate(SRTSOCKET u)
|
|
{
|
|
try
|
|
{
|
|
return s_UDTUnited.getStatus(u);
|
|
}
|
|
catch (const CUDTException &e)
|
|
{
|
|
s_UDTUnited.setError(new CUDTException(e));
|
|
return SRTS_NONEXIST;
|
|
}
|
|
catch (const std::exception& ee)
|
|
{
|
|
LOGC(mglog.Fatal, log << "getsockstate: UNEXPECTED EXCEPTION: "
|
|
<< typeid(ee).name() << ": " << ee.what());
|
|
s_UDTUnited.setError(new CUDTException(MJ_UNKNOWN, MN_NONE, 0));
|
|
return SRTS_NONEXIST;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace UDT
|
|
{
|
|
|
|
int startup()
|
|
{
|
|
return CUDT::startup();
|
|
}
|
|
|
|
int cleanup()
|
|
{
|
|
return CUDT::cleanup();
|
|
}
|
|
|
|
SRTSOCKET socket(int af, int type, int protocol)
|
|
{
|
|
return CUDT::socket(af, type, protocol);
|
|
}
|
|
|
|
int bind(SRTSOCKET u, const struct sockaddr* name, int namelen)
|
|
{
|
|
return CUDT::bind(u, name, namelen);
|
|
}
|
|
|
|
int bind2(SRTSOCKET u, UDPSOCKET udpsock)
|
|
{
|
|
return CUDT::bind(u, udpsock);
|
|
}
|
|
|
|
int listen(SRTSOCKET u, int backlog)
|
|
{
|
|
return CUDT::listen(u, backlog);
|
|
}
|
|
|
|
SRTSOCKET accept(SRTSOCKET u, struct sockaddr* addr, int* addrlen)
|
|
{
|
|
return CUDT::accept(u, addr, addrlen);
|
|
}
|
|
|
|
int connect(SRTSOCKET u, const struct sockaddr* name, int namelen)
|
|
{
|
|
return CUDT::connect(u, name, namelen, 0);
|
|
}
|
|
|
|
int close(SRTSOCKET u)
|
|
{
|
|
return CUDT::close(u);
|
|
}
|
|
|
|
int getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen)
|
|
{
|
|
return CUDT::getpeername(u, name, namelen);
|
|
}
|
|
|
|
int getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen)
|
|
{
|
|
return CUDT::getsockname(u, name, namelen);
|
|
}
|
|
|
|
int getsockopt(
|
|
SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen)
|
|
{
|
|
return CUDT::getsockopt(u, level, optname, optval, optlen);
|
|
}
|
|
|
|
int setsockopt(
|
|
SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen)
|
|
{
|
|
return CUDT::setsockopt(u, level, optname, optval, optlen);
|
|
}
|
|
|
|
// DEVELOPER API
|
|
|
|
int connect_debug(
|
|
SRTSOCKET u, const struct sockaddr* name, int namelen, int32_t forced_isn)
|
|
{
|
|
return CUDT::connect(u, name, namelen, forced_isn);
|
|
}
|
|
|
|
int send(SRTSOCKET u, const char* buf, int len, int flags)
|
|
{
|
|
return CUDT::send(u, buf, len, flags);
|
|
}
|
|
|
|
int recv(SRTSOCKET u, char* buf, int len, int flags)
|
|
{
|
|
return CUDT::recv(u, buf, len, flags);
|
|
}
|
|
|
|
|
|
int sendmsg(
|
|
SRTSOCKET u, const char* buf, int len, int ttl, bool inorder,
|
|
uint64_t srctime)
|
|
{
|
|
return CUDT::sendmsg(u, buf, len, ttl, inorder, srctime);
|
|
}
|
|
|
|
int recvmsg(SRTSOCKET u, char* buf, int len, uint64_t& srctime)
|
|
{
|
|
return CUDT::recvmsg(u, buf, len, srctime);
|
|
}
|
|
|
|
int recvmsg(SRTSOCKET u, char* buf, int len)
|
|
{
|
|
uint64_t srctime;
|
|
|
|
return CUDT::recvmsg(u, buf, len, srctime);
|
|
}
|
|
|
|
int64_t sendfile(
|
|
SRTSOCKET u,
|
|
fstream& ifs,
|
|
int64_t& offset,
|
|
int64_t size,
|
|
int block)
|
|
{
|
|
return CUDT::sendfile(u, ifs, offset, size, block);
|
|
}
|
|
|
|
int64_t recvfile(
|
|
SRTSOCKET u,
|
|
fstream& ofs,
|
|
int64_t& offset,
|
|
int64_t size,
|
|
int block)
|
|
{
|
|
return CUDT::recvfile(u, ofs, offset, size, block);
|
|
}
|
|
|
|
int64_t sendfile2(
|
|
SRTSOCKET u,
|
|
const char* path,
|
|
int64_t* offset,
|
|
int64_t size,
|
|
int block)
|
|
{
|
|
fstream ifs(path, ios::binary | ios::in);
|
|
int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block);
|
|
ifs.close();
|
|
return ret;
|
|
}
|
|
|
|
int64_t recvfile2(
|
|
SRTSOCKET u,
|
|
const char* path,
|
|
int64_t* offset,
|
|
int64_t size,
|
|
int block)
|
|
{
|
|
fstream ofs(path, ios::binary | ios::out);
|
|
int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block);
|
|
ofs.close();
|
|
return ret;
|
|
}
|
|
|
|
int select(
|
|
int nfds,
|
|
UDSET* readfds,
|
|
UDSET* writefds,
|
|
UDSET* exceptfds,
|
|
const struct timeval* timeout)
|
|
{
|
|
return CUDT::select(nfds, readfds, writefds, exceptfds, timeout);
|
|
}
|
|
|
|
int selectEx(
|
|
const vector<SRTSOCKET>& fds,
|
|
vector<SRTSOCKET>* readfds,
|
|
vector<SRTSOCKET>* writefds,
|
|
vector<SRTSOCKET>* exceptfds,
|
|
int64_t msTimeOut)
|
|
{
|
|
return CUDT::selectEx(fds, readfds, writefds, exceptfds, msTimeOut);
|
|
}
|
|
|
|
int epoll_create()
|
|
{
|
|
return CUDT::epoll_create();
|
|
}
|
|
|
|
int epoll_add_usock(int eid, SRTSOCKET u, const int* events)
|
|
{
|
|
return CUDT::epoll_add_usock(eid, u, events);
|
|
}
|
|
|
|
int epoll_add_ssock(int eid, SYSSOCKET s, const int* events)
|
|
{
|
|
return CUDT::epoll_add_ssock(eid, s, events);
|
|
}
|
|
|
|
int epoll_update_usock(int eid, SRTSOCKET u, const int* events)
|
|
{
|
|
return CUDT::epoll_update_usock(eid, u, events);
|
|
}
|
|
|
|
int epoll_update_ssock(int eid, SYSSOCKET s, const int* events)
|
|
{
|
|
return CUDT::epoll_update_ssock(eid, s, events);
|
|
}
|
|
|
|
int epoll_remove_usock(int eid, SRTSOCKET u)
|
|
{
|
|
return CUDT::epoll_remove_usock(eid, u);
|
|
}
|
|
|
|
int epoll_remove_ssock(int eid, SYSSOCKET s)
|
|
{
|
|
return CUDT::epoll_remove_ssock(eid, s);
|
|
}
|
|
|
|
int epoll_wait(
|
|
int eid,
|
|
set<SRTSOCKET>* readfds,
|
|
set<SRTSOCKET>* writefds,
|
|
int64_t msTimeOut,
|
|
set<SYSSOCKET>* lrfds,
|
|
set<SYSSOCKET>* lwfds)
|
|
{
|
|
return CUDT::epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds);
|
|
}
|
|
|
|
/*
|
|
|
|
#define SET_RESULT(val, num, fds, it) \
|
|
if (val != NULL) \
|
|
{ \
|
|
if (val->empty()) \
|
|
{ \
|
|
if (num) *num = 0; \
|
|
} \
|
|
else \
|
|
{ \
|
|
if (*num > static_cast<int>(val->size())) \
|
|
*num = val->size(); \
|
|
int count = 0; \
|
|
for (it = val->begin(); it != val->end(); ++ it) \
|
|
{ \
|
|
if (count >= *num) \
|
|
break; \
|
|
fds[count ++] = *it; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
*/
|
|
|
|
template <class SOCKTYPE>
|
|
inline void set_result(set<SOCKTYPE>* val, int* num, SOCKTYPE* fds)
|
|
{
|
|
if ( !val || !num || !fds )
|
|
return;
|
|
|
|
if (*num > int(val->size()))
|
|
*num = int(val->size()); // will get 0 if val->empty()
|
|
int count = 0;
|
|
|
|
// This loop will run 0 times if val->empty()
|
|
for (typename set<SOCKTYPE>::const_iterator it = val->begin(); it != val->end(); ++ it)
|
|
{
|
|
if (count >= *num)
|
|
break;
|
|
fds[count ++] = *it;
|
|
}
|
|
}
|
|
|
|
int epoll_wait2(
|
|
int eid, SRTSOCKET* readfds,
|
|
int* rnum, SRTSOCKET* writefds,
|
|
int* wnum,
|
|
int64_t msTimeOut,
|
|
SYSSOCKET* lrfds,
|
|
int* lrnum,
|
|
SYSSOCKET* lwfds,
|
|
int* lwnum)
|
|
{
|
|
// This API is an alternative format for epoll_wait, created for
|
|
// compatability with other languages. Users need to pass in an array
|
|
// for holding the returned sockets, with the maximum array length
|
|
// stored in *rnum, etc., which will be updated with returned number
|
|
// of sockets.
|
|
|
|
set<SRTSOCKET> readset;
|
|
set<SRTSOCKET> writeset;
|
|
set<SYSSOCKET> lrset;
|
|
set<SYSSOCKET> lwset;
|
|
set<SRTSOCKET>* rval = NULL;
|
|
set<SRTSOCKET>* wval = NULL;
|
|
set<SYSSOCKET>* lrval = NULL;
|
|
set<SYSSOCKET>* lwval = NULL;
|
|
if ((readfds != NULL) && (rnum != NULL))
|
|
rval = &readset;
|
|
if ((writefds != NULL) && (wnum != NULL))
|
|
wval = &writeset;
|
|
if ((lrfds != NULL) && (lrnum != NULL))
|
|
lrval = &lrset;
|
|
if ((lwfds != NULL) && (lwnum != NULL))
|
|
lwval = &lwset;
|
|
|
|
int ret = CUDT::epoll_wait(eid, rval, wval, msTimeOut, lrval, lwval);
|
|
if (ret > 0)
|
|
{
|
|
//set<SRTSOCKET>::const_iterator i;
|
|
//SET_RESULT(rval, rnum, readfds, i);
|
|
set_result(rval, rnum, readfds);
|
|
//SET_RESULT(wval, wnum, writefds, i);
|
|
set_result(wval, wnum, writefds);
|
|
|
|
//set<SYSSOCKET>::const_iterator j;
|
|
//SET_RESULT(lrval, lrnum, lrfds, j);
|
|
set_result(lrval, lrnum, lrfds);
|
|
//SET_RESULT(lwval, lwnum, lwfds, j);
|
|
set_result(lwval, lwnum, lwfds);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut)
|
|
{
|
|
return CUDT::epoll_uwait(eid, fdsSet, fdsSize, msTimeOut);
|
|
}
|
|
|
|
int epoll_release(int eid)
|
|
{
|
|
return CUDT::epoll_release(eid);
|
|
}
|
|
|
|
ERRORINFO& getlasterror()
|
|
{
|
|
return CUDT::getlasterror();
|
|
}
|
|
|
|
int getlasterror_code()
|
|
{
|
|
return CUDT::getlasterror().getErrorCode();
|
|
}
|
|
|
|
const char* getlasterror_desc()
|
|
{
|
|
return CUDT::getlasterror().getErrorMessage();
|
|
}
|
|
|
|
int getlasterror_errno()
|
|
{
|
|
return CUDT::getlasterror().getErrno();
|
|
}
|
|
|
|
// Get error string of a given error code
|
|
const char* geterror_desc(int code, int err)
|
|
{
|
|
CUDTException e (CodeMajor(code/1000), CodeMinor(code%1000), err);
|
|
return(e.getErrorMessage());
|
|
}
|
|
|
|
int bstats(SRTSOCKET u, TRACEBSTATS* perf, bool clear)
|
|
{
|
|
return CUDT::bstats(u, perf, clear);
|
|
}
|
|
|
|
SRT_SOCKSTATUS getsockstate(SRTSOCKET u)
|
|
{
|
|
return CUDT::getsockstate(u);
|
|
}
|
|
|
|
void setloglevel(LogLevel::type ll)
|
|
{
|
|
CGuard gg(srt_logger_config.mutex);
|
|
srt_logger_config.max_level = ll;
|
|
}
|
|
|
|
void addlogfa(LogFA fa)
|
|
{
|
|
CGuard gg(srt_logger_config.mutex);
|
|
srt_logger_config.enabled_fa.set(fa, true);
|
|
}
|
|
|
|
void dellogfa(LogFA fa)
|
|
{
|
|
CGuard gg(srt_logger_config.mutex);
|
|
srt_logger_config.enabled_fa.set(fa, false);
|
|
}
|
|
|
|
void resetlogfa(set<LogFA> fas)
|
|
{
|
|
CGuard gg(srt_logger_config.mutex);
|
|
for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i)
|
|
srt_logger_config.enabled_fa.set(i, fas.count(i));
|
|
}
|
|
|
|
void resetlogfa(const int* fara, size_t fara_size)
|
|
{
|
|
CGuard gg(srt_logger_config.mutex);
|
|
srt_logger_config.enabled_fa.reset();
|
|
for (const int* i = fara; i != fara + fara_size; ++i)
|
|
srt_logger_config.enabled_fa.set(*i, true);
|
|
}
|
|
|
|
void setlogstream(std::ostream& stream)
|
|
{
|
|
CGuard gg(srt_logger_config.mutex);
|
|
srt_logger_config.log_stream = &stream;
|
|
}
|
|
|
|
void setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler)
|
|
{
|
|
CGuard gg(srt_logger_config.mutex);
|
|
srt_logger_config.loghandler_opaque = opaque;
|
|
srt_logger_config.loghandler_fn = handler;
|
|
}
|
|
|
|
void setlogflags(int flags)
|
|
{
|
|
CGuard gg(srt_logger_config.mutex);
|
|
srt_logger_config.flags = flags;
|
|
}
|
|
|
|
SRT_API bool setstreamid(SRTSOCKET u, const std::string& sid)
|
|
{
|
|
return CUDT::setstreamid(u, sid);
|
|
}
|
|
SRT_API std::string getstreamid(SRTSOCKET u)
|
|
{
|
|
return CUDT::getstreamid(u);
|
|
}
|
|
|
|
SRT_REJECT_REASON getrejectreason(SRTSOCKET u)
|
|
{
|
|
return CUDT::rejectReason(u);
|
|
}
|
|
|
|
} // namespace UDT
|