DSS 代码分析【点播请求】

播请求:RTSP端口监听流程

服务器启动后会将监听的端口加入到EventThread::Entry()阻塞等待请求的到来,具体流程如下:

1. 服务器启动时会调用QTSServer::Initialize()---->QTSServer::CreateListeners()


Bool16 QTSServer::CreateListeners(Bool16 startListeningNow, QTSServerPrefs* inPrefs, UInt16 inPortOverride)
{
       ......
	
	// Create any new  listeners we need
	for (UInt32 count3 = 0; count3 < theTotalRTSPPortTrackers; count3++)
	{
		if (theRTSPPortTrackers[count3].fNeedsCreating)
		{
			newListenerArray[curPortIndex] = NEW RTSPListenerSocket();
			QTSS_Error err = newListenerArray[curPortIndex]->Initialize(theRTSPPortTrackers[count3].fIPAddr, theRTSPPortTrackers[count3].fPort);

			char thePortStr[20];
			qtss_sprintf(thePortStr, "%hu", theRTSPPortTrackers[count3].fPort);

			//
			// If there was an error creating this listener, destroy it and log an error
			if ((startListeningNow) && (err != QTSS_NoErr))
				delete newListenerArray[curPortIndex];

			if (err == EADDRINUSE)
				QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssListenPortInUse, 0, thePortStr);
			else if (err == EACCES)
				QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssListenPortAccessDenied, 0, thePortStr);
			else if (err != QTSS_NoErr)
				QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssListenPortError, 0, thePortStr);
			else
			{
				//
				// This listener was successfully created.
				if (startListeningNow)
					newListenerArray[curPortIndex]->RequestEvent(EV_RE);
				curPortIndex++;
			}
		}
	}

	......
}

2.QTSServer::CreateListeners()调用RTSPListenerSocket::Initialize()完成端口的绑定与监听。

    

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;
}


3.QTSServer::CreateListeners()调用RTSPListenerSocket::RequestEvent()注册触发响应的IO事件,并将RTSPListenerSocket实例对应的id加入到EventThread的处理列表fRefTable中

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 defined(__linux__) && !defined(EASY_DEVICE)
		if (addEpollEvent(&fEventReq, theMask) != 0)
#else
		if (select_modwatch(&fEventReq, theMask) != 0)//注册监听的IO事件
#endif
#endif  
			AssertV(false, OSThread::GetErrno());
	}
	else
	{
		//allocate a Unique ID for this socket, and add it to the ref table

		//johnson find the bug
		bool bFindValid = false;
#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.
		do
		{
			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;

			//If the fUniqueID is used, find a new one until it's free
			OSRef * ref = fEventThread->fRefTable.Resolve(&fUniqueIDStr);
			if (ref != NULL)
			{
				fEventThread->fRefTable.Release(ref);
			}
			else
			{
				bFindValid = true;// ok, it's free
			}
		} while (0);
#else
		do
		{
			if (!compare_and_store(10000000, 1, &sUniqueID))
				fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1);
			else
				fUniqueID = 1;

			//If the fUniqueID is used, find a new one until it's free
			OSRef * ref = fEventThread->fRefTable.Resolve(&fUniqueIDStr);
			if (ref != NULL)
			{
				fEventThread->fRefTable.Release(ref);
			}
			else
			{
				bFindValid = true;// ok, it's free
			}
		} while (0);
#endif

		fRef.Set(fUniqueIDStr, this);
		fEventThread->fRefTable.Register(&fRef);//将ID加入到EventThread的处理列表中

		//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 defined(__linux__) && !defined(EASY_DEVICE)
		if (addEpollEvent(&fEventReq, theMask) != 0)
#else
		if (select_modwatch(&fEventReq, theMask) != 0)
#endif
#endif  
			//this should never fail, but if it does, cleanup.
			AssertV(false, OSThread::GetErrno());

	}
}
4.EventThread::Entry()中调用select_waitevent(&theCurrentEvent, NULL)阻塞等待注册IO事件发生。

播请求:RTSP连接建立

客户端发起点播请求,触发服务器的建立RTSP连接事件

针对RTSP协议,Darwing Streaming Server554端口上侦听,当有连接请求到达时,通过accept调用返回一个socket,对应的后续RTSP请求都是通过这个socket来传送的。我们把RTSP相关的事件分成两类,一类是RTSP连接请求,一类是RTSP请求。先来看RTSP连接请求的过程:

1. RTSP连接到达后,select_waitevent函数阻塞等待网络IO事件会返回,代码在EventContext.cppEventThread::Entry中。

void EventThread::Entry()
{
	struct eventreq theCurrentEvent;
	::memset(&theCurrentEvent, '\0', sizeof(theCurrentEvent));

	while (true)
	{
		int theErrno = EINTR;
		while (theErrno == EINTR)
		{
#if MACOSXEVENTQUEUE
			int theReturnValue = waitevent(&theCurrentEvent, NULL);
#else

#if defined(__linux__) && !defined(EASY_DEVICE)
			int theReturnValue = epoll_waitevent(&theCurrentEvent, NULL);
#else
			int theReturnValue = select_waitevent(&theCurrentEvent, NULL);
#endif
#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();
		}
     ......
}


2.select_waitevent返回当前的事件对象,通过事件对象中的er_data获取相应的id,使用该id在EventThread::fRefTable中对应的EventContext。得到的是EventContext类型的派生类RTSPListenerSocket。相应的代码在EventContext.cpp中的Entry

void EventThread::Entry()
{
......
		//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);
			}
		}
......
}


3.entry函数中接着ProcessEvent处理事件。相应的代码在EventContext.cpp中的Entry中

theContext->ProcessEvent(theCurrentEvent.er_eventbits);

注意,由于对应的EventContext类其实指向的是RTSPListenerSocket,因此调用的应该是TCPListenerSocket::ProcessEvent

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 Message 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
}

4.TCPListenerSocket::ProcessEvent方法中,会调用accept得到连接的socket,通过执行theTask = this->GetSessionTask(&theSocket),实际调用的是RTSPListenerSocket::GetSessionTask。

Task*   RTSPListenerSocket::GetSessionTask(TCPSocket** outSocket)
{
	Assert(outSocket != NULL);

	// when the server is behing a round robin DNS, the client needs to knwo the IP address ot the server
	// so that it can direct the "POST" half of the connection to the same machine when tunnelling RTSP thru HTTP
	Bool16  doReportHTTPConnectionAddress = QTSServerInterface::GetServer()->GetPrefs()->GetDoReportHTTPConnectionAddress();

	RTSPSession* theTask = NEW RTSPSession(doReportHTTPConnectionAddress);
	*outSocket = theTask->GetSocket();  // out socket is not attached to a unix socket yet.

	if (this->OverMaxConnections(0))
		this->SlowDown();
	else
		this->RunNormal();

	return theTask;
}

5.RTSPListenerSocket::GetSessionTask方法中,

调用RTSPSession* theTask = NEW RTSPSession(doReportHTTPConnectionAddress)建立了一个新的RTSPSession


6.TCPListenerSocket.cpp文件中的TCPListenerSocket::ProcessEvent方法,会对将accept返回的osSocket作为fd设置给theSocket(TCPSocket)。然后调用this->RequestEvent(EV_RE)刚刚建立好的RTSP连接的fd加入到侦听队列,等待网络IO事件的到来


点播请求:RTSP请求处理

RTSP请求的处理流程步骤如下:

1. RTSP请求到达后,被select_waitevent函数捕获,代码在EventContext.cppEventThread::Entry中。

int theReturnValue = waitevent(&theCurrentEvent, NULL);


2. EventThread::fRefTable,获取对应的EventContext。得到的是EventContext类。相应的代码在EventContext.cpp的Entry中,如下:

//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);
			}
}

3. ProcessEvent,处理事件。

注意,此时调用的是EventContext::ProcessEvent

virtual void ProcessEvent(int /*eventBits*/)
{
		if (EVENTCONTEXT_DEBUG)
		{
			if (fTask == NULL)
				qtss_printf("EventContext::ProcessEvent context=%p task=NULL\n", (void *) this);
			else
				qtss_printf("EventContext::ProcessEvent context=%p task=%p TaskName=%s\n", (void *)this, (void *)fTask, fTask->fTaskName);
		}

		if (fTask != NULL)
			fTask->Signal(Task::kReadEvent);
}


4. EventContext::ProcessEvent方法在EventContext.h中实现,然后调用了fTask->Signal(Task::kReadEvent)RTSPSession加入到TaskThread的队列中等待RTSPSession::Run()被调用执行,fTaskRTSPSession类的实例


5. 续就是RTSPSession::Run()RTSP请求的具体的处理。



你可能感兴趣的:(DSS流媒体服务器,DSS流媒体服务器)