Exemplo n.º 1
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
}
Exemplo n.º 2
0
// You know the packet type and just want to parse it now
bool RTCPCompressedQTSSPacket::ParseAPPData(UInt8* inPacketBuffer, UInt32 inPacketLength)
{

	if (!this->ParseCompressedQTSSPacket(inPacketBuffer, inPacketLength))
		return false;

	APPEND_TO_DUMP_ARRAY("%s", "\n              RTCP APP QTSS Report ");

	char   printName[5];
	(void) this->GetAppPacketName(printName, sizeof(printName));
	APPEND_TO_DUMP_ARRAY("H_app_packet_name = %s, ", printName);
	APPEND_TO_DUMP_ARRAY("H_ssrc = %"   _U32BITARG_   ", ", this->GetPacketSSRC());
	APPEND_TO_DUMP_ARRAY("H_src_ID = %"   _U32BITARG_   ", ", this->GetQTSSReportSourceID());
	APPEND_TO_DUMP_ARRAY("H_vers=%d, ", this->GetQTSSPacketVersion());
	APPEND_TO_DUMP_ARRAY("H_packt_len=%d", this->GetQTSSPacketLength());

	UInt8* qtssDataBuffer = this->GetPacketBuffer() + kQTSSDataOffset;

	//packet length is given in words
	UInt32 bytesRemaining = this->GetQTSSPacketLength() * 4;
	while (bytesRemaining >= 4) //items must be at least 32 bits
	{
		// DMS - There is no guarentee that qtssDataBuffer will be 4 byte aligned, because
		// individual APP packet fields can be 6 bytes or 4 bytes or 8 bytes. So we have to
		// use the 4-byte align protection functions. Sparc and MIPS processors will crash otherwise
		UInt32 theHeader = ntohl(OS::GetUInt32FromMemory((UInt32*)&qtssDataBuffer[kQTSSItemTypeOffset]));
		UInt16 itemType = (UInt16)((theHeader & kQTSSItemTypeMask) >> kQTSSItemTypeShift);
		UInt8 itemVersion = (UInt8)((theHeader & kQTSSItemVersionMask) >> kQTSSItemVersionShift);
		UInt8 itemLengthInBytes = (UInt8)(theHeader & kQTSSItemLengthMask);

		APPEND_TO_DUMP_ARRAY("\n       h_type=%.2s(", (char*)&itemType);
		APPEND_TO_DUMP_ARRAY(", h_vers=%u", itemVersion);
		APPEND_TO_DUMP_ARRAY(", h_size=%u", itemLengthInBytes);

		qtssDataBuffer += sizeof(UInt32);   //advance past the above UInt16's & UInt8's (point it at the actual item data)

		//Update bytesRemaining (move it past current item)
		//This itemLengthInBytes is part of the packet and could therefore be bogus.
		//Make sure not to overstep the end of the buffer!
		bytesRemaining -= sizeof(UInt32);
		if (itemLengthInBytes > bytesRemaining)
			break; //don't walk off the end of the buffer
			//itemLengthInBytes = bytesRemaining;
		bytesRemaining -= itemLengthInBytes;

		switch (itemType)
		{
		case  TW0_CHARS_TO_INT('r', 'r'): //'rr': //'rrcv':
			{
				fReceiverBitRate = ntohl(OS::GetUInt32FromMemory((UInt32*)qtssDataBuffer));
				qtssDataBuffer += sizeof(fReceiverBitRate);
				APPEND_TO_DUMP_ARRAY(", rcvr_bit_rate=%"   _U32BITARG_   "", fReceiverBitRate);
			}
			break;

		case TW0_CHARS_TO_INT('l', 't'): //'lt':    //'late':
			{
				fAverageLateMilliseconds = ntohs(*(UInt16*)qtssDataBuffer);
				qtssDataBuffer += sizeof(fAverageLateMilliseconds);
				APPEND_TO_DUMP_ARRAY(", avg_late=%u", fAverageLateMilliseconds);
			}
			break;

		case TW0_CHARS_TO_INT('l', 's'): // 'ls':   //'loss':
			{
				fPercentPacketsLost = ntohs(*(UInt16*)qtssDataBuffer);
				qtssDataBuffer += sizeof(fPercentPacketsLost);
				APPEND_TO_DUMP_ARRAY(", percent_loss=%u", fPercentPacketsLost);
			}
			break;

		case TW0_CHARS_TO_INT('d', 'l'): //'dl':    //'bdly':
			{
				fAverageBufferDelayMilliseconds = ntohs(*(UInt16*)qtssDataBuffer);
				qtssDataBuffer += sizeof(fAverageBufferDelayMilliseconds);
				APPEND_TO_DUMP_ARRAY(", avg_buf_delay=%u", fAverageBufferDelayMilliseconds);
			}
			break;

		case TW0_CHARS_TO_INT(':', ')'): //:)   
			{
				fIsGettingBetter = true;
				APPEND_TO_DUMP_ARRAY(", quality=%s", "better");
			}
			break;
		case TW0_CHARS_TO_INT(':', '('): // ':(': 
			{
				fIsGettingWorse = true;
				APPEND_TO_DUMP_ARRAY(", quality=%s", "worse");
			}
			break;

		case TW0_CHARS_TO_INT(':', '|'): // ':|': 
			{
				fIsGettingWorse = true;
				APPEND_TO_DUMP_ARRAY(", quality=%s", "same");
			}
			break;

		case TW0_CHARS_TO_INT('e', 'y'): //'ey':   //'eyes':
			{
				fNumEyes = ntohl(OS::GetUInt32FromMemory((UInt32*)qtssDataBuffer));
				qtssDataBuffer += sizeof(fNumEyes);
				APPEND_TO_DUMP_ARRAY(", eyes=%"   _U32BITARG_   "", fNumEyes);

				if (itemLengthInBytes >= 2)
				{
					fNumEyesActive = ntohl(OS::GetUInt32FromMemory((UInt32*)qtssDataBuffer));
					qtssDataBuffer += sizeof(fNumEyesActive);
					APPEND_TO_DUMP_ARRAY(", eyes_actv=%"   _U32BITARG_   "", fNumEyesActive);
				}
				if (itemLengthInBytes >= 3)
				{
					fNumEyesPaused = ntohl(OS::GetUInt32FromMemory((UInt32*)qtssDataBuffer));
					qtssDataBuffer += sizeof(fNumEyesPaused);
					APPEND_TO_DUMP_ARRAY(", eyes_pausd=%"   _U32BITARG_   "", fNumEyesPaused);
				}
			}
			break;

		case TW0_CHARS_TO_INT('p', 'r'): // 'pr':  //'prcv':
			{
				fTotalPacketsReceived = ntohl(OS::GetUInt32FromMemory((UInt32*)qtssDataBuffer));
				qtssDataBuffer += sizeof(fTotalPacketsReceived);
				APPEND_TO_DUMP_ARRAY(", pckts_rcvd=%"   _U32BITARG_   "", fTotalPacketsReceived);
			}
			break;

		case TW0_CHARS_TO_INT('p', 'd'): //'pd':    //'pdrp':
			{
				fTotalPacketsDropped = ntohs(*(UInt16*)qtssDataBuffer);
				qtssDataBuffer += sizeof(fTotalPacketsDropped);
				APPEND_TO_DUMP_ARRAY(", pckts_drppd=%u", fTotalPacketsDropped);
			}
			break;

		case TW0_CHARS_TO_INT('p', 'l'): //'pl':    //'p???':
			{
				fTotalPacketsLost = ntohs(*(UInt16*)qtssDataBuffer);
				qtssDataBuffer += sizeof(fTotalPacketsLost);
				APPEND_TO_DUMP_ARRAY(", ttl_pckts_lost=%u", fTotalPacketsLost);
			}
			break;


		case TW0_CHARS_TO_INT('b', 'l'): //'bl':    //'bufl':
			{
				fClientBufferFill = ntohs(*(UInt16*)qtssDataBuffer);
				qtssDataBuffer += sizeof(fClientBufferFill);
				APPEND_TO_DUMP_ARRAY(", buffr_fill=%u", fClientBufferFill);
			}
			break;


		case TW0_CHARS_TO_INT('f', 'r'): //'fr':    //'frat':
			{
				fFrameRate = ntohs(*(UInt16*)qtssDataBuffer);
				qtssDataBuffer += sizeof(fFrameRate);
				APPEND_TO_DUMP_ARRAY(", frame_rate=%u", fFrameRate);
			}
			break;


		case TW0_CHARS_TO_INT('x', 'r'): //'xr':    //'xrat':
			{
				fExpectedFrameRate = ntohs(*(UInt16*)qtssDataBuffer);
				qtssDataBuffer += sizeof(fExpectedFrameRate);
				APPEND_TO_DUMP_ARRAY(", xpectd_frame_rate=%u", fExpectedFrameRate);
			}
			break;


		case TW0_CHARS_TO_INT('d', '#'): //'d#':    //'dry#':
			{
				fAudioDryCount = ntohs(*(UInt16*)qtssDataBuffer);
				qtssDataBuffer += sizeof(fAudioDryCount);
				APPEND_TO_DUMP_ARRAY(", aud_dry_count=%u", fAudioDryCount);
			}
			break;

		case TW0_CHARS_TO_INT('o', 'b'): //'ob': // overbuffer window size
			{
				fOverbufferWindowSize = ntohl(OS::GetUInt32FromMemory((UInt32*)qtssDataBuffer));
				qtssDataBuffer += sizeof(fOverbufferWindowSize);
				APPEND_TO_DUMP_ARRAY(", ovr_buffr_windw_siz=%"   _U32BITARG_   "", fOverbufferWindowSize);
			}
			break;

		default:
			{
				if (fDebug)
				{
					char s[12] = "";
					qtss_sprintf(s, "  [%.2s]", (char*)&itemType);
					WarnV(false, "Unknown APP('QTSS') item type");
					WarnV(false, s);
				}
			}

			break;
		}   //      switch (itemType)


		APPEND_TO_DUMP_ARRAY("%s", "),  ");

	}   //while ( bytesRemaining >= 4 )


	return true;
}
SInt64 RTPStatsUpdaterTask::Run()
{

    QTSServerInterface* theServer = QTSServerInterface::sServer;
    
    // All of this must happen atomically wrt dictionary values we are manipulating
    OSMutexLocker locker(&theServer->fMutex);
    
    //First update total bytes. This must be done because total bytes is a 64 bit number,
    //so no atomic functions can apply.
    //
    // NOTE: The line below is not thread safe on non-PowerPC platforms. This is
    // because the fPeriodicRTPBytes variable is being manipulated from within an
    // atomic_add. On PowerPC, assignments are atomic, so the assignment below is ok.
    // On a non-PowerPC platform, the following would be thread safe:
    //unsigned int periodicBytes = atomic_add(&theServer->fPeriodicRTPBytes, 0);
    unsigned int periodicBytes = theServer->fPeriodicRTPBytes;
    (void)atomic_sub(&theServer->fPeriodicRTPBytes, periodicBytes);
    theServer->fTotalRTPBytes += periodicBytes;
    
    // Same deal for packet totals
    unsigned int periodicPackets = theServer->fPeriodicRTPPackets;
    (void)atomic_sub(&theServer->fPeriodicRTPPackets, periodicPackets);
    theServer->fTotalRTPPackets += periodicPackets;
    
    // ..and for lost packet totals
    unsigned int periodicPacketsLost = theServer->fPeriodicRTPPacketsLost;
    (void)atomic_sub(&theServer->fPeriodicRTPPacketsLost, periodicPacketsLost);
    theServer->fTotalRTPPacketsLost += periodicPacketsLost;
    
    SInt64 curTime = OS::Milliseconds();
    
    //for cpu percent
        Float32 cpuTimeInSec = GetCPUTimeInSeconds();
    
    //also update current bandwidth statistic
    if (fLastBandwidthTime != 0)
    {
        Assert(curTime > fLastBandwidthTime);
        UInt32 delta = (UInt32)(curTime - fLastBandwidthTime);
        if (delta < 1000)
            WarnV(delta >= 1000, "Timer is off");
        
        //do the bandwidth computation using floating point divides
        //for accuracy and speed.
        Float32 bits = (Float32)(periodicBytes * 8);
        Float32 theTime = (Float32)delta;
        theTime /= 1000;
        bits /= theTime;
        Assert(bits >= 0);
        theServer->fCurrentRTPBandwidthInBits = (UInt32)bits;
        
        // okay let's do it for MP3 bytes now
        bits = (Float32)(((SInt64)theServer->fTotalMP3Bytes - fLastTotalMP3Bytes) * 8);
        bits /= theTime;
        theServer->fCurrentMP3BandwidthInBits = (UInt32)bits;

        //do the same computation for packets per second
        Float32 packetsPerSecond = (Float32)periodicPackets;
        packetsPerSecond /= theTime;
        Assert(packetsPerSecond >= 0);
        theServer->fRTPPacketsPerSecond = (UInt32)packetsPerSecond;
        
        //do the computation for cpu percent
        Float32 diffTime = cpuTimeInSec - theServer->fCPUTimeUsedInSec;
        theServer->fCPUPercent = (diffTime/theTime) * 100;  
		
		UInt32 numProcessors = OS::GetNumProcessors();
		
		if (numProcessors > 1)
			theServer->fCPUPercent /= numProcessors;
    }
    
    fLastTotalMP3Bytes = (SInt64)theServer->fTotalMP3Bytes;
    fLastBandwidthTime = curTime;
    // We use a running average for avg. bandwidth calculations
    theServer->fAvgMP3BandwidthInBits = (theServer->fAvgMP3BandwidthInBits
            + theServer->fCurrentMP3BandwidthInBits)/2; 
    
    //for cpu percent
    theServer->fCPUTimeUsedInSec    = cpuTimeInSec; 
    
    //also compute average bandwidth, a much more smooth value. This is done with
    //the fLastBandwidthAvg, a timestamp of the last time we did an average, and
    //fLastBytesSent, the number of bytes sent when we last did an average.
    if ((fLastBandwidthAvg != 0) && (curTime > (fLastBandwidthAvg +
        (theServer->GetPrefs()->GetAvgBandwidthUpdateTimeInSecs() * 1000))))
    {
        UInt32 delta = (UInt32)(curTime - fLastBandwidthAvg);
        SInt64 bytesSent = theServer->fTotalRTPBytes - fLastBytesSent;
        Assert(bytesSent >= 0);
        
        //do the bandwidth computation using floating point divides
        //for accuracy and speed.
        Float32 bits = (Float32)(bytesSent * 8);
        Float32 theAvgTime = (Float32)delta;
        theAvgTime /= 1000;
        bits /= theAvgTime;
        Assert(bits >= 0);
        theServer->fAvgRTPBandwidthInBits = (UInt32)bits;

        fLastBandwidthAvg = curTime;
        fLastBytesSent = theServer->fTotalRTPBytes;
        
        //if the bandwidth is above the bandwidth setting, disconnect 1 user by sending them
        //a BYE RTCP packet.
        SInt32 maxKBits = theServer->GetPrefs()->GetMaxKBitsBandwidth();
        if ((maxKBits > -1) && (theServer->fAvgRTPBandwidthInBits > ((UInt32)maxKBits * 1024)))
        {
            //we need to make sure that all of this happens atomically wrt the session map
            OSMutexLocker locker(theServer->GetRTPSessionMap()->GetMutex());
            RTPSessionInterface* theSession = this->GetNewestSession(theServer->fRTPMap);
            if (theSession != NULL)
                if ((curTime - theSession->GetSessionCreateTime()) <
                        theServer->GetPrefs()->GetSafePlayDurationInSecs() * 1000)
                    theSession->Signal(Task::kKillEvent);
        }
    }
    else if (fLastBandwidthAvg == 0)
    {
        fLastBandwidthAvg = curTime;
        fLastBytesSent = theServer->fTotalRTPBytes;
    }
    
    (void)this->GetEvents();//we must clear the event mask!
    return theServer->GetPrefs()->GetTotalBytesUpdateTimeInSecs() * 1000;
}
/* 在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
}