Пример #1
0
void    Socket::SetSocketBufSize(UInt32 inNewSize)
{

#if SOCKET_DEBUG
	int value;
	int buffSize = sizeof(value);
	int error = ::getsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (void*)&value,  (socklen_t*)&buffSize);
#endif

    int bufSize = inNewSize;
    int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (char*)&bufSize, sizeof(int));
    AssertV(err == 0, OSThread::GetErrno());
    
#if SOCKET_DEBUG
	int setValue;
	error = ::getsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (void*)&setValue,  (socklen_t*)&buffSize);
	qtss_printf("Socket::SetSocketBufSize ");
	if (fState & kBound)
	{	if (NULL != this->GetLocalAddrStr())
			this->GetLocalAddrStr()->PrintStr(":");
		if (NULL != this->GetLocalPortStr())
			this->GetLocalPortStr()->PrintStr(" ");
	}
	else
		qtss_printf("unbound ");
	qtss_printf("socket=%d old SO_SNDBUF =%d inNewSize=%d setValue=%d\n", (int) fFileDesc, value, bufSize, setValue);
#endif

}
Пример #2
0
OS_Error TCPListenerSocket::Initialize(UInt32 addr, UInt16 port)
{
    OS_Error err = this->TCPSocket::Open();
    if (0 == err) do
    {   
        // set SO_REUSEADDR socket option before calling bind.
#ifndef __Win32__
        // this causes problems on NT (multiple processes can bind simultaneously),
        // so don't do it on NT.
        this->ReuseAddr();
#endif
        err = this->Bind(addr, port);
        if (err != 0) break; // don't assert this is just a port already in use.

        //
        // Unfortunately we need to advertise a big buffer because our TCP sockets
        // can be used for incoming broadcast data. This could force the server
        // to run out of memory faster if it gets bogged down, but it is unavoidable.
        this->SetSocketRcvBufSize(96 * 1024);       
        err = this->Listen(kListenQueueLength);
        AssertV(err == 0, OSThread::GetErrno()); 
        if (err != 0) break;
        
    } while (false);
    
    return err;
}
/* used in QTSServer::CreateListeners() */
OS_Error TCPListenerSocket::Initialize(UInt32 addr, UInt16 port)
{
	/* 创建Socket并设置为非阻塞模式 */
    OS_Error err = this->TCPSocket::Open();
    if (0 == err) do
    {   
        // set SO_REUSEADDR socket option before calling bind.
        // this causes problems on NT (multiple processes can bind simultaneously),
        // so don't do it on NT.
        this->ReuseAddr();

		/* 将入参和新建的Socket绑定 */
        err = this->Bind(addr, port);
        if (err != 0) break; // don't assert this is just a port already in use.

        //
        // Unfortunately we need to advertise a big buffer because our TCP sockets
        // can be used for incoming broadcast data. This could force the server
        // to run out of memory faster(更快地使内存溢出) if it gets bogged down(陷入困境, 停顿), but it is unavoidable.
		/* 设置Socket接受buffer大小(我们需要大的缓存以多播数据) */
        this->SetSocketRcvBufSize(96 * 1024); 
		/* 设置侦听的最大等待连接的队列长度为128 */
        err = this->Listen(kListenQueueLength);
        AssertV(err == 0, OSThread::GetErrno()); 
        if (err != 0) break;
        
    } while (false);
    
    return err;
}
Пример #4
0
/* used in TCPListenerSocket::ProcessEvent(),那里第一个入参是服务器接受客户端连接的服务器端的Socket,客户端的ip地址是第二个参数,所以这两个参数联系紧密 */
void TCPSocket::Set(int inSocket, struct sockaddr_in* remoteaddr)
{
	/* 从入参设置本地和远端的Socket */
    fRemoteAddr = *remoteaddr;
    fFileDesc = inSocket;
    
	/* 当服务器接受的客户端的Socket不是初始状态时,一定要成功获取它在服务器端对应的Socket的信息(包括相应的IP地址) */
    if ( inSocket != EventContext::kInvalidFileDesc ) 
    {
        //make sure to find out what IP address this connection is actually occuring on. That
        //way, we can report correct information to clients asking what the connection's IP is
        socklen_t len = sizeof(fLocalAddr);

		/* 获取客户端的Socket的在服务器本地上的Socket信息,详见<<Windows网络编程第二版>>.
		   这里fRemoteAddr和fLocalAddr是已接受的该客户端已经连接的对应的IP地址 */
        int err = ::getsockname(fFileDesc, (struct sockaddr*)&fLocalAddr, &len);
		/* 确保能成功获取本地Socket信息 */
        AssertV(err == 0, OSThread::GetErrno());
		/* 确保该Socket已绑定ip和连接 */
        fState |= kBound;
        fState |= kConnected;
    }
    else
		/* 若服务器接受的客户端的Socket是初始状态时,它的状态只能是0 */
        fState = 0;
}
Пример #5
0
OS_Error UDPSocket::SetMulticastInterface(UInt32 inLocalAddr)
{
    // set the outgoing interface for multicast datagrams on this socket
    in_addr theLocalAddr;
    theLocalAddr.s_addr = inLocalAddr;
    int err = setsockopt(fFileDesc, IPPROTO_IP, IP_MULTICAST_IF, (char*)&theLocalAddr, sizeof(theLocalAddr));
    AssertV(err == 0, OSThread::GetErrno());
    if (err == -1)
        return (OS_Error)OSThread::GetErrno();
    else
        return OS_NoErr;
}
Пример #6
0
int  OSEventThread::SetFileDescNonBlocking(int inFileDesc)
{
#ifdef __Win32__
    u_long one = 1;
    int err = ::ioctlsocket(inFileDesc, FIONBIO, &one);
#else
    int flag = ::fcntl(inFileDesc, F_GETFL, 0);
    int err = ::fcntl(inFileDesc, F_SETFL, flag | O_NONBLOCK);
#endif
    AssertV(err == 0, OSThread::GetErrno());
    return err;
}
void EventContext::InitNonBlocking(int inFileDesc)
{
    fFileDesc = inFileDesc;

#ifdef __Win32__
    u_long one = 1;
    int err = ::ioctlsocket(fFileDesc, FIONBIO, &one);
#else
    int flag = ::fcntl(fFileDesc, F_GETFL, 0);
    int err = ::fcntl(fFileDesc, F_SETFL, flag | O_NONBLOCK);
#endif
    AssertV(err == 0, OSThread::GetErrno());
}
Пример #8
0
void TCPClientSocket::SetOptions(int sndBufSize, int rcvBufSize)
{   //set options on the socket

	//qtss_printf("TCPClientSocket::SetOptions sndBufSize=%d,rcvBuf=%d,keepAlive=%d,noDelay=%d\n",sndBufSize,rcvBufSize,(int)keepAlive,(int)noDelay);
	int err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_SNDBUF, (char*)&sndBufSize, sizeof(int));
	AssertV(err == 0, OSThread::GetErrno());

	err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_RCVBUF, (char*)&rcvBufSize, sizeof(int));
	AssertV(err == 0, OSThread::GetErrno());

#if __FreeBSD__ || __MacOSX__
	struct timeval time;
	//int len = sizeof(time);
	time.tv_sec = 0;
	time.tv_usec = 0;

	err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_RCVTIMEO, (char*)&time, sizeof(time));
	AssertV(err == 0, OSThread::GetErrno());

	err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_SNDTIMEO, (char*)&time, sizeof(time));
	AssertV(err == 0, OSThread::GetErrno());
#endif

}
Пример #9
0
QTSS_Error CreateRTPFileSession(QTSS_StandardRTSP_Params* inParamBlock, const StrPtrLen& inPath, FileSession** outFile)
{   
    *outFile = NEW FileSession();
    StrPtrLen thePath(inPath);
    RTPFileSession::ErrorCode theErr = (*outFile)->fFile.Initialize(thePath, 8);
    if (theErr != RTPFileSession::errNoError)
    {
        delete *outFile;
        *outFile = NULL;
        
        if (theErr == RTPFileSession::errFileNotFound)
            return QTSSModuleUtils::SendErrorResponse(  inParamBlock->inRTSPRequest,
                                                        qtssClientNotFound,
                                                        sBadQTFileErr);
        AssertV(0, theErr);
    }
    return QTSS_NoErr;
}
void EventContext::Cleanup()
{
    int err = 0;
    if (fFileDesc != kInvalidFileDesc)
    {
        //if this object is registered in the table, unregister it now
        if (fUniqueID > 0)
        {
            fEventThread->fRefTable.UnRegister(&fRef);

#if !MACOSXEVENTQUEUE
            select_removeevent(fFileDesc);//The eventqueue / select shim requires this
#ifdef __Win32__
            err = ::closesocket(fFileDesc);
#endif

#else
            //On Linux (possibly other UNIX implementations) you MUST NOT close the fd before
            //removing the fd from the select mask, and having the select function wake up
            //to register this fact. If you close the fd first, bad things may happen, like
            //the socket not getting unbound from the port & IP addr.
            //
            //So, what we do is have the select thread itself call close. This is triggered
            //by calling removeevent.
            err = ::close(fFileDesc);
#endif
        }
        else
#ifdef __Win32__
            err = ::closesocket(fFileDesc);
#else
            err = ::close(fFileDesc);
#endif
    }

    fFileDesc = kInvalidFileDesc;
    fUniqueID = 0;
	fWatchEventCalled = false;

    AssertV(err == 0, OSThread::GetErrno());//we don't really care if there was an error, but it's nice to know
}
Пример #11
0
void TCPSocket::Set(int inSocket, struct sockaddr_in* remoteaddr)
{
    fRemoteAddr = *remoteaddr;
    fFileDesc = inSocket;
    
    if ( inSocket != EventContext::kInvalidFileDesc ) 
    {
        //make sure to find out what IP address this connection is actually occuring on. That
        //way, we can report correct information to clients asking what the connection's IP is
#if __Win32__ || __osf__ || __sgi__ || __hpux__	
        int len = sizeof(fLocalAddr);
#else
        socklen_t len = sizeof(fLocalAddr);
#endif
        int err = ::getsockname(fFileDesc, (struct sockaddr*)&fLocalAddr, &len);
        AssertV(err == 0, OSThread::GetErrno());
        fState |= kBound;
        fState |= kConnected;
    }
    else
        fState = 0;
}
Пример #12
0
void TCPListenerSocket::ProcessEvent(int /*eventBits*/)
{
    //we are executing on the same thread as every other
    //socket, so whatever you do here has to be fast.
    
    struct sockaddr_in addr;
#if __Win32__ || __osf__ || __sgi__ || __hpux__	
    int size = sizeof(addr);
#else
    socklen_t size = sizeof(addr);
#endif
    Task* theTask = NULL;
    TCPSocket* theSocket = NULL;
    
    //fSocket data member of TCPSocket.
	int osSocket = accept(fFileDesc, (struct sockaddr*)&addr, &size);

//test osSocket = -1;
	if (osSocket == -1)
	{
        //take a look at what this error is.
        int acceptError = OSThread::GetErrno();
        if (acceptError == EAGAIN)
        { 
            //If it's EAGAIN, there's nothing on the listen queue right now,
            //so modwatch and return
            this->RequestEvent(EV_RE);
            return;
        }
		
//test acceptError = ENFILE;
//test acceptError = EINTR;
//test acceptError = ENOENT;
		 
        //if these error gets returned, we're out of file desciptors, 
        //the server is going to be failing on sockets, logs, qtgroups and qtuser auth file accesses and movie files. The server is not functional.
		if (acceptError == EMFILE || acceptError == ENFILE)
        {           
#ifndef __Win32__

			QTSSModuleUtils::LogErrorStr(qtssFatalVerbosity,  "Out of File Descriptors. Set max connections lower and check for competing usage from other processes. Exiting.");
#endif

			exit (EXIT_FAILURE);	
        }
        else
        {   
            char errStr[256];
            errStr[sizeof(errStr) -1] = 0;
            qtss_snprintf(errStr, sizeof(errStr) -1, "accept error = %d '%s' on socket. Clean up and continue.", acceptError, strerror(acceptError)); 
            WarnV( (acceptError == 0), errStr);
            
            theTask = this->GetSessionTask(&theSocket);
            if (theTask == NULL)
            {   
                close(osSocket);
            }
            else
            {  
                theTask->Signal(Task::kKillEvent); // just clean up the task
            }
            
            if (theSocket)
                theSocket->fState &= ~kConnected; // turn off connected state
            
            return;
        }
	}
	
    theTask = this->GetSessionTask(&theSocket);
    if (theTask == NULL)
    {    //this should be a disconnect. do an ioctl call?
        close(osSocket);
        if (theSocket)
            theSocket->fState &= ~kConnected; // turn off connected state
    }
    else
    {   
        Assert(osSocket != EventContext::kInvalidFileDesc);
        
        //set options on the socket
        //we are a server, always disable nagle algorithm
        int one = 1;
        int err = ::setsockopt(osSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(int));
        AssertV(err == 0, OSThread::GetErrno());
        
        err = ::setsockopt(osSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(int));
        AssertV(err == 0, OSThread::GetErrno());
    
        int sndBufSize = 96L * 1024L;
        err = ::setsockopt(osSocket, SOL_SOCKET, SO_SNDBUF, (char*)&sndBufSize, sizeof(int));
        AssertV(err == 0, OSThread::GetErrno());
    
        //setup the socket. When there is data on the socket,
        //theTask will get an kReadEvent event
        theSocket->Set(osSocket, &addr);
        theSocket->InitNonBlocking(osSocket);
        theSocket->SetTask(theTask);
        theSocket->RequestEvent(EV_RE);
        
        theTask->SetThreadPicker(Task::GetBlockingTaskThreadPicker()); //The RTSP Task processing threads
    }
    


    if (fSleepBetweenAccepts)
    { 	
        // We are at our maximum supported sockets
        // slow down so we have time to process the active ones (we will respond with errors or service).
        // wake up and execute again after sleeping. The timer must be reset each time through
        //qtss_printf("TCPListenerSocket slowing down\n");
        this->SetIdleTimer(kTimeBetweenAcceptsInMsec); //sleep 1 second
    }
    else
    { 	
        // sleep until there is a read event outstanding (another client wants to connect)
        //qtss_printf("TCPListenerSocket normal speed\n");
        this->RequestEvent(EV_RE);
    }

    fOutOfDescriptors = false; // always false for now  we don't properly handle this elsewhere in the code
}
Пример #13
0
void    Socket::SetSocketBufSize(UInt32 inNewSize)
{
    int bufSize = inNewSize;
    int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (char*)&bufSize, sizeof(int));
    AssertV(err == 0, OSThread::GetErrno());
}
void EventThread::Entry()
{
    struct eventreq theCurrentEvent;
    ::memset( &theCurrentEvent, '\0', sizeof(theCurrentEvent) );

    while (!IsStopRequested())
    {
        int theErrno = EINTR;
        while (theErrno == EINTR)
        {
#if MACOSXEVENTQUEUE
            int theReturnValue = waitevent(&theCurrentEvent, NULL);
#else
            int theReturnValue = select_waitevent(&theCurrentEvent, NULL);
#endif
            //Sort of a hack. In the POSIX version of the server, waitevent can return
            //an actual POSIX errorcode.
            if (theReturnValue >= 0)
                theErrno = theReturnValue;
            else
                theErrno = OSThread::GetErrno();
            if(IsStopRequested()) break;
        }
        if (IsStopRequested()) break;

        AssertV(theErrno == 0, theErrno);

        //ok, there's data waiting on this socket. Send a wakeup.
        if (theCurrentEvent.er_data != NULL)
        {
            //The cookie in this event is an ObjectID. Resolve that objectID into
            //a pointer.
            StrPtrLen idStr((char*)&theCurrentEvent.er_data, sizeof(theCurrentEvent.er_data));
            OSRef* ref = fRefTable.Resolve(&idStr);
            if (ref != NULL)
            {
                EventContext* theContext = (EventContext*)ref->GetObject();
#if DEBUG
                theContext->fModwatched = false;
#endif
                theContext->ProcessEvent(theCurrentEvent.er_eventbits);
                fRefTable.Release(ref);


            }
        }

#if EVENT_CONTEXT_DEBUG
        SInt64  yieldStart = OS::Milliseconds();
#endif

        this->ThreadYield();

#if EVENT_CONTEXT_DEBUG
        SInt64  yieldDur = OS::Milliseconds() - yieldStart;
        static SInt64   numZeroYields;

        if ( yieldDur > 1 )
        {
            qtss_printf( "EventThread time in OSTHread::Yield %i, numZeroYields %i\n", (SInt32)yieldDur, (SInt32)numZeroYields );
            numZeroYields = 0;
        }
        else
            numZeroYields++;
#endif
    }
}
void EventContext::RequestEvent(int theMask)
{
#if DEBUG
    fModwatched = true;
#endif

    //
    // The first time this function gets called, we're supposed to
    // call watchevent. Each subsequent time, call modwatch. That's
    // the way the MacOS X event queue works.

    if (fWatchEventCalled)
    {
        fEventReq.er_eventbits = theMask;
#if MACOSXEVENTQUEUE
        if (modwatch(&fEventReq, theMask) != 0)
#else
        if (select_modwatch(&fEventReq, theMask) != 0)
#endif
            AssertV(false, OSThread::GetErrno());
    }
    else
    {
        //allocate a Unique ID for this socket, and add it to the ref table

#ifdef __Win32__
        //
        // Kind of a hack. On Win32, the way that we pass around the unique ID is
        // by making it the message ID of our Win32 message (see win32ev.cpp).
        // Messages must be >= WM_USER. Hence this code to restrict the numberspace
        // of our UniqueIDs.
        if (!compare_and_store(8192, WM_USER, &sUniqueID))  // Fix 2466667: message IDs above a
            fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1);         // level are ignored, so wrap at 8192
        else
            fUniqueID = (PointerSizedInt)WM_USER;
#else
        if (!compare_and_store(10000000, 1, &sUniqueID))
            fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1);
        else
            fUniqueID = 1;
#endif

        fRef.Set(fUniqueIDStr, this);
        Assert(fEventThread->fRefTable.Register(&fRef) == OS_NoErr);

        //fill out the eventreq data structure
        ::memset( &fEventReq, '\0', sizeof(fEventReq));
        fEventReq.er_type = EV_FD;
        fEventReq.er_handle = fFileDesc;
        fEventReq.er_eventbits = theMask;
        fEventReq.er_data = (void*)fUniqueID;

        fWatchEventCalled = true;
#if MACOSXEVENTQUEUE
        if (watchevent(&fEventReq, theMask) != 0)
#else
        if (select_watchevent(&fEventReq, theMask) != 0)
#endif
            //this should never fail, but if it does, cleanup.
            AssertV(false, OSThread::GetErrno());

    }
}
/* 在TCPListenerSocket中,ProcessEvent 函数(继承EventContext 的ProcessEvent()函数)被重载用来创建Socket和Task 对象得配对 */
void TCPListenerSocket::ProcessEvent(int /*eventBits*/)
{
    //we are executing on the same thread as every other
    //socket, so whatever you do here has to be fast.
	/* 该函数运行于系统唯一的EventThread 线程中,所以要尽量快速,以免占用过多的系统资源 */

	/* 在accept()中存放接受的远处客户端的ip地址 */
    struct sockaddr_in addr;
    socklen_t size = sizeof(addr);


	/* 注意theTask(通过派生类TCPSocket)和theSocket都是TCPListenerSocket的基类 */
	/**************** 注意:通过子类重载GetSessionTask()使Task(具体说,是RTSPSession)和TCPSocket配对 ***********************/
    Task* theTask = NULL;
    TCPSocket* theSocket = NULL;
    
    //fSocket data member of TCPSocket.
	/* 服务器端的Socket接受客户端的Socket的连接请求,成功后返回服务器新建的接受连接的Socket的描述符,否则返回INVALID_SOCKET */
	int osSocket = accept(fFileDesc, (struct sockaddr*)&addr, &size);

//test osSocket = -1;
	/* 假如出错了,进行错误处理.以下全是错误处理!! */
	if (osSocket == -1)
	{
        //take a look at what this error is.
		/* 获取具体的出错原因 */
        int acceptError = OSThread::GetErrno();

		/* 对得出的错误分情形讨论: */

//test acceptError = EAGAIN;
        if (acceptError == EAGAIN)
        { 
            //If it's EAGAIN, there's nothing on the listen queue right now,
            //so modwatch and return
			/* 在同一socket端口上请求监听指定的EV_RE事件 */
            this->RequestEvent(EV_RE);
            return;
        }
		
//test acceptError = ENFILE;
//test acceptError = EINTR;
//test acceptError = ENOENT;
		 
        //if these error gets returned, we're out of file descriptors, 
        //the server is going to be failing on sockets, logs, qtgroups and qtuser auth file accesses and movie files. The server is not functional.
		// 文件描述符用光,这时服务器会丧失功能,直接退出
		if (acceptError == EMFILE || acceptError == ENFILE)
        {           
			QTSSModuleUtils::LogErrorStr(qtssFatalVerbosity,  "Out of File Descriptors. Set max connections lower and check for competing usage from other processes. Exiting.");
			exit (EXIT_FAILURE);	
        }
        else //假如是其它错误(除EAGAIN\EMFILE\ENFILE以外的),在屏幕上显示错误信息,同时将配对的任务和新建的TCPSocket分别删除和关闭,并返回 
        {   
            char errStr[256];
			/* 确保末尾null-terminated */
            errStr[sizeof(errStr) -1] = 0;
			/* 得到指定格式的errStr形如"accept error = 1 '*****error' on socket. Clean up and continue." */
            qtss_snprintf(errStr, sizeof(errStr) -1, "accept error = %d '%s' on socket. Clean up and continue.", acceptError, strerror(acceptError)); 
            /* 当条件不成立时,在屏幕上显示错误信息 */
			WarnV( (acceptError == 0), errStr);
            
			/**************** 注意:通过子类重载GetSessionTask()使Task和TCPSocket配对 ***********************/
			/* 用RTSPListenerSocket::GetSessionTask()获取Session Task和socket,并对结果分析 */
            theTask = this->GetSessionTask(&theSocket);
            if (theTask == NULL)
            {   
				/* 若没有获取任务,就关闭Socket */
                close(osSocket);
            }
            else
            {  
                theTask->Signal(Task::kKillEvent); // just clean up the task
            }
            
			/* 假如RTSPSession中相对应的theSocket非空,就将其状态设为非连接 */
            if (theSocket)
                theSocket->fState &= ~kConnected; // turn off connected state
            
            return;
        }
	}/* errors handling */
	
	/* 得到Session Task并作错误处理 */
	/* 注意以后的派生类(当是RTSPListenerSocket)去获取任务,并设置好Task和outSocket */
	/**************** 注意:通过子类重载GetSessionTask()使Task和TCPSocket配对 ***********************/
    theTask = this->GetSessionTask(&theSocket);
	/* 如果没有获得任务,将已接受连接的服务器端的osSocket关闭,将服务器上RTSPSession中相对应的theSocket关闭 */
    if (theTask == NULL)
    {    //this should be a disconnect. do an ioctl call?
        close(osSocket);
        if (theSocket)
            theSocket->fState &= ~kConnected; // turn off connected state
    }
	/* 假如成功获取到任务,就分别设置这相对应的两个Socket的相关属性 */
    else//创建Task成功,接着创建Socket 对象
    {   
		/* 确保接受连接的服务器端Socket不是初始状态 */
        Assert(osSocket != EventContext::kInvalidFileDesc);
        
        //set options on the socket
        //we are a server, always disable NAGLE ALGORITHM
		/* 设置接受连接的服务器端Socket是:非延迟的,保持活跃,指定大小的传送缓存 */
        int one = 1;
        int err = ::setsockopt(osSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(int));
        AssertV(err == 0, OSThread::GetErrno());
        
        err = ::setsockopt(osSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(int));
        AssertV(err == 0, OSThread::GetErrno());
    
		/* 设置服务器端传送缓存大小96K字节 */
        int sndBufSize = 96L * 1024L;
        err = ::setsockopt(osSocket, SOL_SOCKET, SO_SNDBUF, (char*)&sndBufSize, sizeof(int));
        AssertV(err == 0, OSThread::GetErrno());
    
        //setup the socket. When there is data on the socket, theTask will get an kReadEvent event
        //
		/* 用服务器接受连接时新建的Socket和连接它的客户端的IP地址等初始化RTSPSession中的TCPSocket数据成员 */
        theSocket->Set(osSocket, &addr);
		/* 设置RTSPSession中的TCPSocket为非阻塞的 */
        theSocket->InitNonBlocking(osSocket);
		/**************** 注意:通过子类重载GetSessionTask()使Task和TCPSocket配对 ***********************/
		/* 给RTSPSession中的TCPSocket数据成员设定任务,就是它所在的RTSPSession对象实例,使RTSPSession和TCPSocket紧密配对配对 */
        theSocket->SetTask(theTask);
		// 完成上述设置后,刚建立连接的这个RTSPSession对象实例的TCPSocket向TaskThread请求读入Client发送的数据
        theSocket->RequestEvent(EV_RE);
    }  

	/* 在两次accept()间休眠吗?进行速度调整! */
    if (fSleepBetweenAccepts)
    { 	
        // We are at our maximum supported sockets
        // slow down so we have time to process the active ones (we will respond with errors or service).
        // wake up and execute again after sleeping. The timer must be reset each time through
        //qtss_printf("TCPListenerSocket slowing down\n");
		// 我们已经用光文件描述符,此处需要设置空闲任务计时器,让当前线程休眠1s
        this->SetIdleTimer(kTimeBetweenAcceptsInMsec); //sleep 1 second
    }
    else
    { 	
        // sleep until there is a read event outstanding (another client wants to connect)
        //qtss_printf("TCPListenerSocket normal speed\n");
		//处理完一次连接请求后,服务器端的侦听TCPListenerSocket对象还要接着监听,等待接入新的Client连接
        this->RequestEvent(EV_RE);
    }

    fOutOfDescriptors = false; // always false for now  we don't properly handle this elsewhere in the code
}