void DebugLevel_1(FILE* statusFile, FILE* stdOut, Bool16 printHeader ) { char* thePrefStr = NULL; static char numStr[12] =""; static char dateStr[25] =""; UInt32 theLen = 0; if ( printHeader ) { printf("****************************************"); } delete [] thePrefStr; thePrefStr = NULL; (void)QTSS_GetValueAsString(sServer, qtssCurrentSessionCount, 0, &thePrefStr); print_status(statusFile, stdOut,"%11s", thePrefStr); delete [] thePrefStr; thePrefStr = NULL; qtss_snprintf(numStr,sizeof(numStr) -1, "%" _U32BITARG_ "", (SInt32) sServer->GetNumThinned() ); print_status(statusFile, stdOut,"%11s", numStr); char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes]; (void) QTSSRollingLog::FormatDate(theDateBuffer, false); qtss_snprintf(dateStr,sizeof(dateStr) -1, "%s", theDateBuffer ); print_status(statusFile, stdOut,"%24s\n", dateStr); }
// This sends the HTTP response to the server that contains the RTSPtext Ref movie QTSS_Error SendTheResponse(QTSS_RTSPSessionObject theSession, QTSS_StreamRef stream, StrPtrLen& movie) { QTSS_Error err = QTSS_NoErr; char theMovieFile[512]; theMovieFile[sizeof(theMovieFile) -1] = 0; char tmp[600]; tmp[sizeof(tmp) -1] = 0; char tmp2[80]; tmp2[sizeof(tmp2) -1] = 0; UInt8 x1, x2, x3, x4; // send the HTTP reply header to the client err= QTSS_Write(stream, sResponseHeader, ::strlen(sResponseHeader), NULL, qtssWriteFlagsBufferData); if (err != QTSS_NoErr) return QTSS_NoErr; UInt32 ip4address = 0; UInt32 len = sizeof(ip4address); err = QTSS_GetValue(theSession, qtssRTSPSesLocalAddr, 0, &ip4address, &len); // Format the server IP address for building the RTSP address in the reply. x1 = (UInt8)((ip4address >> 24) & 0xff); x2 = (UInt8)((ip4address >> 16) & 0xff); x3 = (UInt8)((ip4address >> 8) & 0xff); x4 = (UInt8)((ip4address) & 0xff); if (movie.Len > sizeof(theMovieFile) -1 ) movie.Len = sizeof(theMovieFile) -1; ::memcpy(theMovieFile, movie.Ptr, movie.Len); theMovieFile[movie.Len] = '\0'; UInt16 port = sRTSPReplyPort; if (0 == port) { len = sizeof(port); err = QTSS_GetValue(theSession, qtssRTSPSesLocalPort, 0, &port, &len); } // construct the RTSP address reply string for the client. qtss_snprintf(tmp,sizeof(tmp) -1, "rtsptext\r\nrtsp://%d.%d.%d.%d:%d%s\r\n", x1,x2,x3,x4, port, theMovieFile); // send the 'Content-Length:' part of the HTTP reply qtss_snprintf(tmp2, sizeof(tmp2) -1, "Content-Length: %d\r\n\r\n", (int) ::strlen(tmp)); err = QTSS_Write(stream, tmp2, ::strlen(tmp2), NULL, qtssWriteFlagsBufferData); if (err != QTSS_NoErr) return QTSS_NoErr; // send the formatted RTSP reference part of the reply err = QTSS_Write(stream, tmp, ::strlen(tmp), NULL, qtssWriteFlagsBufferData); if (err != QTSS_NoErr) return QTSS_NoErr; // flush the pending write to the client. err = QTSS_Flush(stream); return err; }
HTTPSessionInterface::~HTTPSessionInterface() { // If the input socket is != output socket, the input socket was created dynamically if (fInputSocketP != fOutputSocketP) delete fInputSocketP; char remoteAddress[20] = { 0 }; StrPtrLen theIPAddressStr(remoteAddress, sizeof(remoteAddress)); QTSS_GetValue(this, EasyHTTPSesRemoteAddrStr, 0, static_cast<void*>(theIPAddressStr.Ptr), &theIPAddressStr.Len); char msgStr[2048] = { 0 }; switch (fSessionType) { case EasyCameraSession: this->UnRegDevSession(); qtss_snprintf(msgStr, sizeof(msgStr), "EasyCameraSession offline from ip[%s], device_serial[%s]", remoteAddress, device_->serial_.c_str()); break; case EasyNVRSession: this->UnRegDevSession(); qtss_snprintf(msgStr, sizeof(msgStr), "EasyNVRSession offline from ip[%s]", remoteAddress); break; case EasyHTTPSession: qtss_snprintf(msgStr, sizeof(msgStr), "EasyHTTPSession offline from ip[%s]", remoteAddress); break; default: qtss_snprintf(msgStr, sizeof(msgStr), "Unknown session offline from ip[%s]", remoteAddress); break; } QTSServerInterface::LogError(qtssMessageVerbosity, msgStr); }
void DoDescribeAddRequiredSDPLines2(QTSS_StandardRTSP_Params* inParams, ReflectorSession* theSession, QTSS_TimeVal modDate, ResizeableStringFormatter *editedSDP, StrPtrLen* theSDPPtr) { SDPContainer checkedSDPContainer; checkedSDPContainer.SetSDPBuffer( theSDPPtr ); if (!checkedSDPContainer.HasReqLines()) { if (!checkedSDPContainer.HasLineType('v')) { // add v line editedSDP->Put("v=0\r\n"); } if (!checkedSDPContainer.HasLineType('s')) { // add s line char* theSDPName = NULL; (void)QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFilePath, 0, &theSDPName); QTSSCharArrayDeleter thePathStrDeleter(theSDPName); editedSDP->Put("s="); editedSDP->Put(theSDPName); editedSDP->PutEOL(); } if (!checkedSDPContainer.HasLineType('t')) { // add t line editedSDP->Put("t=0 0\r\n"); } if (!checkedSDPContainer.HasLineType('o')) { // add o line editedSDP->Put("o=broadcast_sdp "); char tempBuff[256]= ""; tempBuff[255] = 0; qtss_snprintf(tempBuff,sizeof(tempBuff) - 1, "%lu", (UInt64) theSession); editedSDP->Put(tempBuff); editedSDP->Put(" "); // modified date is in milliseconds. Convert to NTP seconds as recommended by rfc 2327 qtss_snprintf(tempBuff, sizeof(tempBuff) - 1, "%"_64BITARG_"d", (SInt64) (modDate/1000) + 2208988800LU); editedSDP->Put(tempBuff); editedSDP->Put(" IN IP4 "); UInt32 buffLen = sizeof(tempBuff) -1; (void)QTSS_GetValue(inParams->inClientSession, qtssCliSesHostName, 0, &tempBuff, &buffLen); editedSDP->Put(tempBuff, buffLen); editedSDP->PutEOL(); } } editedSDP->Put(*theSDPPtr); }
void WriteStartupMessage() { if (sStartedUp) return; sStartedUp = true; //format a date for the startup time char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes]; Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false); char tempBuffer[kMaxLogStringLen]; if (result) qtss_snprintf(tempBuffer,sizeof(tempBuffer), "# Streaming STARTUP %s\n", theDateBuffer); // log startup message to error log as well. if ((result) && (sErrorLog != NULL)) sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll); //write the expire date to the log if ( QTSSExpirationDate::WillSoftwareExpire() && sErrorLog != NULL ) { QTSSExpirationDate::sPrintExpirationDate(tempBuffer); sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll); } }
void FormattedTotalBytesBuffer(char *outBuffer, int outBufferLen, UInt64 totalBytes) { Float32 displayBytes = 0.0; char sizeStr[] = "B"; char* format = NULL; if (totalBytes > 1073741824 ) //GBytes { displayBytes = (Float32) ( (Float64) (SInt64) totalBytes / (Float64) (SInt64) 1073741824 ); sizeStr[0] = 'G'; format = "%.4f%s "; } else if (totalBytes > 1048576 ) //MBytes { displayBytes = (Float32) (SInt32) totalBytes / (Float32) (SInt32) 1048576; sizeStr[0] = 'M'; format = "%.3f%s "; } else if (totalBytes > 1024 ) //KBytes { displayBytes = (Float32) (SInt32) totalBytes / (Float32) (SInt32) 1024; sizeStr[0] = 'K'; format = "%.2f%s "; } else { displayBytes = (Float32) (SInt32) totalBytes; //Bytes sizeStr[0] = 'B'; format = "%4.0f%s "; } outBuffer[outBufferLen -1] = 0; qtss_snprintf(outBuffer, outBufferLen -1, format , displayBytes, sizeStr); }
void RelaySession::Register() { qtssRelaySessionObjectType = 0; // create relay session object type (void)QTSS_CreateObjectType(&qtssRelaySessionObjectType); // Add the static attributes to the qtssRelaySessionObjectType object (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sRelayNameName, NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sRelayNameName, &sRelayName); // relay name (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceTypeName, NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceTypeName, &sSourceType); // source type (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceIPAddrName, NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceIPAddrName, &sSourceIPAddr); // source addr (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceInIPAddrName, NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceInIPAddrName, &sSourceInIPAddr); // interface addr (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceUDPPortsName, NULL, qtssAttrDataTypeUInt16); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceUDPPortsName, &sSourceUDPPorts); // udp ports (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceRTSPPortName, NULL, qtssAttrDataTypeUInt16); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceRTSPPortName, &sSourceRTSPPort); // rtsp port (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceURLName, NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceURLName, &sSourceURL); // url (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceUsernameName, NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceUsernameName, &sSourceUsername); // username (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourcePasswordName, NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourcePasswordName, &sSourcePassword); // password (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceTTLName, NULL, qtssAttrDataTypeUInt16); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceTTLName, &sSourceTTL); // ttl (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sRelayOutputObjectName, NULL, qtssAttrDataTypeQTSS_Object); (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sRelayOutputObjectName, &sRelayOutputObject); // relay output //char* strEnd = NULL; char* relayStr = "QTSS_Relay/"; //kVersionString is changed now -- it doesn't contain any spaces or the build number //strEnd = strchr(kVersionString, ' '); //Assert(strEnd != NULL); #ifndef __Win32__ //qtss_snprintf(sRelayUserAgent, ::strlen(relayStr) + (strEnd - kVersionString) + 1, "%s/%s", relayStr, kVersionString); qtss_snprintf(sRelayUserAgent, ::strlen(relayStr) + ::strlen(kVersionString) + 1, "%s%s", relayStr, kVersionString); #else //_snprintf(sRelayUserAgent, ::strlen(relayStr) + (strEnd - kVersionString) + 1, "%s/%s", relayStr, kVersionString); _snprintf(sRelayUserAgent, ::strlen(relayStr) + ::strlen(kVersionString) + 1, "%s%s", relayStr, kVersionString); #endif }
void QTSSModuleUtils::LogPrefErrorStr( QTSS_ErrorVerbosity inVerbosity, char* preference, char* inMessage) { if (inMessage == NULL || preference == NULL) { Assert(0); return; } char buffer[1024]; qtss_snprintf(buffer,sizeof(buffer), "Server preference %s %s", preference, inMessage); (void)QTSS_Write(sErrorLog, buffer, ::strlen(buffer), NULL, inVerbosity); }
void RTSPRequestInterface::AppendContentLength(UInt32 contentLength) { if (!fStandardHeadersWritten) this->WriteStandardHeaders(); char dataSize[10]; dataSize[sizeof(dataSize) -1] = 0; qtss_snprintf(dataSize, sizeof(dataSize) -1, "%lu", contentLength); StrPtrLen contentLengthStr(dataSize); this->AppendHeader(qtssContentLengthHeader, &contentLengthStr); }
void WriteShutdownMessage() { if (!sStartedUp) return; sStartedUp = false; //log shutdown message //format a date for the shutdown time char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes]; Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false); char tempBuffer[kMaxLogStringLen]; if (result) qtss_snprintf(tempBuffer, sizeof(tempBuffer), "# Streaming SHUTDOWN %s\n", theDateBuffer); if ( result && sErrorLog != NULL ) sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll); }
QTSS_Error QTSSModule::SetupModule(QTSS_CallbacksPtr inCallbacks, QTSS_MainEntryPointPtr inEntrypoint) { QTSS_Error theErr = QTSS_NoErr; // Load fragment from disk if necessary if ((fFragment != NULL) && (inEntrypoint == NULL)) theErr = this->LoadFromDisk(&inEntrypoint); if (theErr != QTSS_NoErr) return theErr; // At this point, we must have an entrypoint if (inEntrypoint == NULL) return QTSS_NotAModule; // Invoke the private initialization routine QTSS_PrivateArgs thePrivateArgs; thePrivateArgs.inServerAPIVersion = QTSS_API_VERSION; thePrivateArgs.inCallbacks = inCallbacks; thePrivateArgs.outStubLibraryVersion = 0; thePrivateArgs.outDispatchFunction = NULL; theErr = (inEntrypoint)(&thePrivateArgs); if (theErr != QTSS_NoErr) return theErr; if (thePrivateArgs.outStubLibraryVersion > thePrivateArgs.inServerAPIVersion) return QTSS_WrongVersion; // Set the dispatch function so we'll be able to invoke this module later on fDispatchFunc = thePrivateArgs.outDispatchFunction; //Log char msgStr[2048]; char* moduleName = NULL; (void)this->GetValueAsString(qtssModName, 0, &moduleName); qtss_snprintf(msgStr, sizeof(msgStr), "Loading Module...%s [%s]", moduleName, (fFragment == NULL) ? "static" : "dynamic"); delete moduleName; QTSServerInterface::LogError(qtssMessageVerbosity, msgStr); return QTSS_NoErr; }
Bool16 XMLParser::ParseFile(char* errorBuffer, int errorBufferSize) { if (fRootTag != NULL) { delete fRootTag; // flush old data fRootTag = NULL; } fFile.Set(fFilePath); if (errorBufferSize < 500) errorBuffer = NULL; // Just a hack to avoid checking everywhere if ((fFile.GetLength() == 0) || fFile.IsDir()) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Couldn't read xml file"); return false; // we don't have a valid file; } char* fileData = NEW char[ (SInt32) (fFile.GetLength() + 1)]; UInt32 theLengthRead = 0; fFile.Read(0, fileData, (UInt32) fFile.GetLength(), &theLengthRead); StrPtrLen theDataPtr(fileData, theLengthRead); StringParser theParser(&theDataPtr); fRootTag = NEW XMLTag(); Bool16 result = fRootTag->ParseTag(&theParser, fVerifier, errorBuffer, errorBufferSize); if (!result) { // got error parsing file delete fRootTag; fRootTag = NULL; } delete []fileData; fFile.Close(); return result; }
CServiceSession::~CServiceSession() { char remoteAddress[20] = {0}; StrPtrLen theIPAddressStr(remoteAddress,sizeof(remoteAddress)); QTSS_GetValue(this, qtssRTSPSesRemoteAddrStr, 0, (void*)theIPAddressStr.Ptr, &theIPAddressStr.Len); char msgStr[2048] = { 0 }; qtss_snprintf(msgStr, sizeof(msgStr), "session offline from ip[%s]",remoteAddress); QTSServerInterface::LogError(qtssMessageVerbosity, msgStr); // Invoke the session closing modules QTSS_RoleParams theParams; theParams.rtspSessionClosingParams.inRTSPSession = this; //会话断开时,调用模块进行一些停止的工作 for (UInt32 x = 0; x < QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPSessionClosingRole); x++) (void)QTSServerInterface::GetModule(QTSSModule::kRTSPSessionClosingRole, x)->CallDispatch(QTSS_RTSPSessionClosing_Role, &theParams); fLiveSession = false; //used in Clean up request to remove the RTP session. this->CleanupRequest();// Make sure that all our objects are deleted //if (fSessionType == qtssServiceSession) // QTSServerInterface::GetServer()->AlterCurrentServiceSessionCount(-1); }
void HTTPSessionInterface::UnRegDevSession() { if (fAuthenticated) { char msgStr[512]; qtss_snprintf(msgStr, sizeof(msgStr), "Device unregister,Device_serial[%s]\n", fDevice.serial_.c_str()); QTSServerInterface::LogError(qtssMessageVerbosity, msgStr); QTSServerInterface::GetServer()->GetDeviceSessionMap()->UnRegister(fDevice.serial_);//add //在redis上删除设备 //QTSServerInterface::GetServer()->RedisDelDevName(fDevice.serial_.c_str()); QTSS_RoleParams theParams; theParams.StreamNameParams.inStreamName = (char *)(fDevice.serial_.c_str()); UInt32 numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kDelDevNameRole); for ( UInt32 currentModule=0;currentModule < numModules; currentModule++) { QTSSModule* theModule = QTSServerInterface::GetModule(QTSSModule::kDelDevNameRole, currentModule); (void)theModule->CallDispatch(QTSS_DelDevName_Role, &theParams); } } }
void HTTPSessionInterface::UnRegDevSession() const { if (fAuthenticated) { char msgStr[512]; qtss_snprintf(msgStr, sizeof(msgStr), "Device unregister£¬Device_serial[%s]\n", device_->serial_.c_str()); QTSServerInterface::LogError(qtssMessageVerbosity, msgStr); QTSServerInterface::GetServer()->GetDeviceSessionMap()->UnRegister(device_->serial_);//add //ÔÚredisÉÏɾ³ýÉ豸 //QTSServerInterface::GetServer()->RedisDelDevice(fDevice.serial_.c_str()); QTSS_RoleParams theParams; theParams.DeviceInfoParams.serial_ = new char[64]; strncpy(theParams.DeviceInfoParams.serial_, device_->serial_.c_str(), device_->serial_.size() + 1); UInt32 numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kRedisDelDeviceRole); for (UInt32 currentModule = 0; currentModule < numModules; currentModule++) { QTSSModule* theModule = QTSServerInterface::GetModule(QTSSModule::kRedisDelDeviceRole, currentModule); (void)theModule->CallDispatch(Easy_RedisDelDevice_Role, &theParams); } delete[] theParams.DeviceInfoParams.serial_; } }
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 }
SDPLineSorter::SDPLineSorter(SDPContainer *rawSDPContainerPtr, Float32 adjustMediaBandwidthPercent, SDPContainer *insertMediaLinesArray) : fSessionLineCount(0), fSDPSessionHeaders(NULL, 0), fSDPMediaHeaders(NULL, 0) { Assert(rawSDPContainerPtr != NULL); if (NULL == rawSDPContainerPtr) return; StrPtrLen theSDPData(rawSDPContainerPtr->fSDPBuffer.Ptr, rawSDPContainerPtr->fSDPBuffer.Len); StrPtrLen *theMediaStart = rawSDPContainerPtr->GetLine(rawSDPContainerPtr->FindHeaderLineType('m', 0)); if (theMediaStart && theMediaStart->Ptr && theSDPData.Ptr) { UInt32 mediaLen = theSDPData.Len - (UInt32)(theMediaStart->Ptr - theSDPData.Ptr); char *mediaStartPtr = theMediaStart->Ptr; fMediaHeaders.Set(mediaStartPtr, mediaLen); StringParser sdpParser(&fMediaHeaders); SDPLine sdpLine; Bool16 foundLine = false; Bool16 newMediaSection = true; SDPLine* insertLine = NULL; while (sdpParser.GetDataRemaining() > 0) { foundLine = sdpParser.GetThruEOL(&sdpLine); if (!foundLine) { break; } if (sdpLine.GetHeaderType() == 'm') newMediaSection = true; if (insertMediaLinesArray && newMediaSection && (sdpLine.GetHeaderType() == 'a')) { newMediaSection = false; for (insertLine = insertMediaLinesArray->GetLine(0); insertLine; insertLine = insertMediaLinesArray->GetNextLine()) fSDPMediaHeaders.Put(*insertLine); } if (('b' == sdpLine.GetHeaderType()) && (1.0 != adjustMediaBandwidthPercent)) { StringParser bLineParser(&sdpLine); bLineParser.ConsumeUntilDigit(); UInt32 bandwidth = (UInt32)(.5 + (adjustMediaBandwidthPercent * (Float32)bLineParser.ConsumeInteger())); if (bandwidth < 1) bandwidth = 1; char bandwidthStr[10]; qtss_snprintf(bandwidthStr, sizeof(bandwidthStr) - 1, "%" _U32BITARG_ "", bandwidth); bandwidthStr[sizeof(bandwidthStr) - 1] = 0; fSDPMediaHeaders.Put(sMaxBandwidthTag); fSDPMediaHeaders.Put(bandwidthStr); } else fSDPMediaHeaders.Put(sdpLine); fSDPMediaHeaders.Put(SDPLineSorter::sEOL); } fMediaHeaders.Set(fSDPMediaHeaders.GetBufPtr(), fSDPMediaHeaders.GetBytesWritten()); } fSessionLineCount = rawSDPContainerPtr->FindHeaderLineType('m', 0); if (fSessionLineCount < 0) // didn't find it use the whole buffer { fSessionLineCount = rawSDPContainerPtr->GetNumLines(); } for (SInt16 sessionLineIndex = 0; sessionLineIndex < fSessionLineCount; sessionLineIndex++) fSessionSDPContainer.AddHeaderLine((StrPtrLen *)rawSDPContainerPtr->GetLine(sessionLineIndex)); //qtss_printf("\nSession raw Lines:\n"); fSessionSDPContainer.PrintAllLines(); SInt16 numHeaderTypes = sizeof(SDPLineSorter::sSessionOrderedLines) - 1; Bool16 addLine = true; for (SInt16 fieldTypeIndex = 0; fieldTypeIndex < numHeaderTypes; fieldTypeIndex++) { SInt32 lineIndex = fSessionSDPContainer.FindHeaderLineType(SDPLineSorter::sSessionOrderedLines[fieldTypeIndex], 0); StrPtrLen *theHeaderLinePtr = fSessionSDPContainer.GetLine(lineIndex); while (theHeaderLinePtr != NULL) { addLine = this->ValidateSessionHeader(theHeaderLinePtr); if (addLine) { fSDPSessionHeaders.Put(*theHeaderLinePtr); fSDPSessionHeaders.Put(SDPLineSorter::sEOL); } if (NULL != ::strchr(sessionSingleLines, theHeaderLinePtr->Ptr[0])) // allow 1 of this type: use first found break; // move on to next line type lineIndex = fSessionSDPContainer.FindHeaderLineType(SDPLineSorter::sSessionOrderedLines[fieldTypeIndex], lineIndex + 1); theHeaderLinePtr = fSessionSDPContainer.GetLine(lineIndex); } } fSessionHeaders.Set(fSDPSessionHeaders.GetBufPtr(), fSDPSessionHeaders.GetBytesWritten()); }
QTSS_Error Authenticate(QTSS_StandardRTSP_Params* inParams) { if ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) ) { return QTSS_RequestFailed; } QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest; QTSS_Error theErr = QTSS_NoErr; char *movieUrlTrunc = NULL; theErr = QTSS_GetValueAsString(theRTSPRequest,qtssRTSPReqTruncAbsoluteURL, 0, &movieUrlTrunc); QTSSCharArrayDeleter movieUrlTruncDeleter(movieUrlTrunc); if (theErr != QTSS_NoErr) return QTSS_RequestFailed; char *movieUrlFilename = NULL; theErr = QTSS_GetValueAsString(theRTSPRequest,qtssRTSPReqFileName, 0, &movieUrlFilename); QTSSCharArrayDeleter movieUrlFilenameDeleter(movieUrlFilename); if (theErr != QTSS_NoErr) return QTSS_RequestFailed; // Return unless the requested filename matches the redirect URL if (::strncmp(movieUrlFilename, sRedirectUrl, (::strlen(movieUrlFilename) <= ::strlen(sRedirectUrl)) ? ::strlen(sRedirectUrl) : ::strlen(movieUrlFilename))) return QTSS_NoErr; // Get the client's bandwidth header UInt32 bandwidthBits = 0; UInt32 len = sizeof(bandwidthBits); (void)QTSS_GetValue(theRTSPRequest, qtssRTSPReqBandwidthBits, 0, (void *)&bandwidthBits, &len); // Prepare a filename extension to use for redirection. // These numbers match the "Streaming Speed" options in the QuickTime Player 7 settings. // The extension will be appended to the requested URL. char theNewFilename[kBuffSize]; ::memset(theNewFilename, 0, kBuffSize); if (bandwidthBits <= 0) { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_50kbit.3gp", movieUrlTrunc, movieUrlFilename); } else if (bandwidthBits <= 28000) { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_50kbit.3gp", movieUrlTrunc, movieUrlFilename); } else if (bandwidthBits <= 56000) { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_50kbit.3gp", movieUrlTrunc, movieUrlFilename); } else if (bandwidthBits <= 112000) { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_100kbit.mov", movieUrlTrunc, movieUrlFilename); } else if (bandwidthBits <= 256000) { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_100kbit.mov", movieUrlTrunc, movieUrlFilename); } else if (bandwidthBits <= 384000) { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_300kbit.mov", movieUrlTrunc, movieUrlFilename); } else if (bandwidthBits <= 512000) { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_300kbit.mov", movieUrlTrunc, movieUrlFilename); } else if (bandwidthBits <= 768000) { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_300kbit.mov", movieUrlTrunc, movieUrlFilename); } else if (bandwidthBits <= 1000000) { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_h264_1mbit.mp4", movieUrlTrunc, movieUrlFilename); } else if (bandwidthBits <= 1500000) { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_h264_1mbit.mp4", movieUrlTrunc, movieUrlFilename); } else { qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_h264_1mbit.mp4", movieUrlTrunc, movieUrlFilename); } // In order to send the redirect, we need to get a QTSS_StreamRef // so we can send data to the client. Get the QTSS_StreamRef out of the request. QTSS_StreamRef* theStreamRef = NULL; UInt32 strRefLen = 0; theErr = QTSS_GetValuePtr(theRTSPRequest, qtssRTSPReqStreamRef, 0, (void**)&theStreamRef, &strRefLen); if (( QTSS_NoErr != theErr ) || ( sizeof(QTSS_StreamRef) != strRefLen) ) { return QTSS_RequestFailed; } // Send the redirect UInt32 theLenWritten = 0; (void)QTSS_Write(*theStreamRef, sRedirect.Ptr, sRedirect.Len, &theLenWritten, 0); (void)QTSS_Write(*theStreamRef, sLocation.Ptr, sLocation.Len, &theLenWritten, 0); (void)QTSS_Write(*theStreamRef, theNewFilename, ::strlen(theNewFilename), &theLenWritten, 0); (void)QTSS_Write(*theStreamRef, sRedirectEnd.Ptr, sRedirectEnd.Len, &theLenWritten, 0); (void)QTSS_Flush(*theStreamRef); return QTSS_NoErr; }
//启动服务 QTSS_ServerState StartServer(XMLPrefsParser* inPrefsSource, PrefsSource* inMessagesSource, UInt16 inPortOverride, int statsUpdateInterval, QTSS_ServerState inInitialState, Bool16 inDontFork, UInt32 debugLevel, UInt32 debugOptions) { //Mark when we are done starting up. If auto-restart is enabled, we want to make sure //to always exit with a status of 0 if we encountered a problem WHILE STARTING UP. This //will prevent infinite-auto-restart-loop type problems Bool16 doneStartingUp = false; QTSS_ServerState theServerState = qtssStartingUpState; //服务单元状态更新间隔 sStatusUpdateInterval = statsUpdateInterval; //Initialize utility classes OS::Initialize(); OSThread::Initialize(); //建立网络事件(R/W/E事件)线程,仅建立,But未启动 Socket::Initialize(); //获取系统网卡数量sNumIPAddrs及对应的具体ip,存储在sIPAddrInfoArray结构体数组中 SocketUtils::Initialize(!inDontFork); /* #if !MACOSXEVENTQUEUE ::select_startevents();//initialize the select() implementation of the event queue #endif */ #if !MACOSXEVENTQUEUE #ifndef __Win32__ ::epollInit(); #else ::select_startevents();//initialize the select() implementation of the event queue #endif #endif //初始化系统属性字典 QTSSDictionaryMap::Initialize(); //初始化Server对象属性字典,包括其他具有属性字典的类,都要先进行Initialize QTSServerInterface::Initialize();//此部分必须在QTSServer对象构造前调用 sServer = NEW QTSServer(); sServer->SetDebugLevel(debugLevel); sServer->SetDebugOptions(debugOptions); //重新解析xml配置 inPrefsSource->Parse(); //准备开启监听,接收并处理相关事务 Bool16 createListeners = true; if (qtssShuttingDownState == inInitialState) createListeners = false; //初始化服务实例 sServer->Initialize(inPrefsSource, inMessagesSource, inPortOverride, createListeners); if (inInitialState == qtssShuttingDownState) { sServer->InitModules(inInitialState); return inInitialState; } if (sServer->GetServerState() != qtssFatalErrorState) { UInt32 numShortTaskThreads = 0; UInt32 numBlockingThreads = 0; UInt32 numThreads = 0; UInt32 numProcessors = 0; if (OS::ThreadSafe()) { //工作线程 numShortTaskThreads = sServer->GetPrefs()->GetNumThreads(); // whatever the prefs say if (numShortTaskThreads == 0) { numProcessors = OS::GetNumProcessors(); // 1 worker thread per processor, up to 2 threads. // Note:Limiting the number of worker threads to 2 on a MacOS X system with > 2 cores // results in better performance on those systems, as of MacOS X 10.5. Future // improvements should make this limit unnecessary. if (numProcessors > 2) numShortTaskThreads = 2; else numShortTaskThreads = numProcessors; } //协议处理线程 numBlockingThreads = sServer->GetPrefs()->GetNumBlockingThreads(); // whatever the prefs say if (numBlockingThreads == 0) numBlockingThreads = 1; } if (numShortTaskThreads == 0) numShortTaskThreads = 1; if (numBlockingThreads == 0) numBlockingThreads = 1; numThreads = numShortTaskThreads + numBlockingThreads; //qtss_printf("Add threads shortask=%lu blocking=%lu\n",numShortTaskThreads, numBlockingThreads); TaskThreadPool::SetNumShortTaskThreads(numShortTaskThreads); TaskThreadPool::SetNumBlockingTaskThreads(numBlockingThreads); TaskThreadPool::AddThreads(numThreads); sServer->InitNumThreads(numThreads); #if DEBUG qtss_printf("Number of task threads: %" _U32BITARG_ "\n",numThreads); #endif // Start up the server's global tasks, and start listening //超时事件处理线程 TimeoutTask::Initialize(); // The TimeoutTask mechanism is task based, // we therefore must do this after adding task threads // this be done before starting the sockets and server tasks } //Make sure to do this stuff last. Because these are all the threads that //do work in the server, this ensures that no work can go on while the server //is in the process of staring up if (sServer->GetServerState() != qtssFatalErrorState) { //Idle事务处理线程 IdleTask::Initialize(); Socket::StartThread(); OSThread::Sleep(1000); // // On Win32, in order to call modwatch the Socket EventQueue thread must be // created first. Modules call modwatch from their initializer, and we don't // want to prevent them from doing that, so module initialization is separated // out from other initialization, and we start the Socket EventQueue thread first. // The server is still prevented from doing anything as of yet, because there // aren't any TaskThreads yet. sServer->InitModules(inInitialState); sServer->StartTasks(); theServerState = sServer->GetServerState(); } if (theServerState != qtssFatalErrorState) { CleanPid(true); WritePid(!inDontFork); doneStartingUp = true; //Log char msgStr[128]; qtss_snprintf(msgStr, sizeof(msgStr), "EasyCMS Service done starting up"); QTSServerInterface::LogError(qtssMessageVerbosity, msgStr); OSMemory::SetMemoryError(ENOMEM); } //// SWITCH TO RUN USER AND GROUP ID //if (!sServer->SwitchPersonality()) // theServerState = qtssFatalErrorState; // Tell the caller whether the server started up or not return theServerState; }
MP3Broadcaster::MP3Broadcaster(char* ipaddr, int port, char* config, char* playList, char* workingDir, Bool16 useICY) : mValid(true), mPort(8000), mBitRate(0), mFrequency(0), mUpcomingSongsListSize(7), mRecentSongsListSize(0), mLogging(0), mShowCurrent(true), mShowUpcoming(true), mNumErrors(0), mNumWarnings(0), mPreflight(false), mCleanupDone(false), mTempPicker(NULL), mSocket(NULL, 0), mLog(NULL), mUseICY(useICY) { sBroadcaster = this; strcpy(mIPAddr, "128.0.0.1"); strcpy(mPlayListPath, DEFAULTPATHS_ETC_DIR "mp3playlist.ply"); strcpy(mWorkingDirPath, DEFAULTPATHS_ETC_DIR); strcpy(mPlayMode, "sequential"); strcpy(mMountPoint, "/"); strcpy(mGenre, "Pop"); strcpy(mURL, ""); strcpy(mPIDFile, ""); //see if there is a defaults File. //if there is load it and over-ride the other defaults int len = ::strlen(config) + 10; char *defaultFileName = new char[len]; qtss_snprintf(defaultFileName, len, "%s%s", config, ".def"); (void) ::ParseConfigFile( false, defaultFileName, ConfigSetter, this ); //ignore if no defaults file delete [] defaultFileName; int err = ::ParseConfigFile( false, config, ConfigSetter, this ); if (err) { mValid = false; return; } if (ipaddr != NULL) { // override config file // size limit for any IP addr is 255 strncpy(mIPAddr, ipaddr, 255); } if (port != 0) { // override config file mPort = port; } if (playList) { // override config file // size limit for any playlist string is PATH_MAX - 1 strncpy(mPlayListPath, playList, PATH_MAX-1); } if (workingDir) { // override config file // size limit for any working path is PATH_MAX - extension - 1 strncpy(mWorkingDirPath, playList, PATH_MAX-12); } CreateWorkingFilePath(".current", mCurrentFile); CreateWorkingFilePath(".upcoming", mUpcomingFile); CreateWorkingFilePath(".replacelist", mReplaceFile); CreateWorkingFilePath(".stoplist", mStopFile); CreateWorkingFilePath(".insertlist", mInsertFile); CreateWorkingFilePath("mp3_broadcast.log", mLogFile); }
void DebugLevel_1(FILE* statusFile, FILE* stdOut, Bool16 printHeader ) { char* thePrefStr = NULL; static char numStr[12] =""; static char dateStr[25] =""; UInt32 theLen = 0; if ( printHeader ) { print_status(statusFile,stdOut,"%s", " RTP-Conns RTSP-Conns HTTP-Conns kBits/Sec Pkts/Sec RTP-Playing AvgDelay CurMaxDelay MaxDelay AvgQuality NumThinned Time\n"); } (void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurConn, 0, &thePrefStr); print_status(statusFile, stdOut,"%11s", thePrefStr); delete [] thePrefStr; thePrefStr = NULL; (void)QTSS_GetValueAsString(sServer, qtssRTSPCurrentSessionCount, 0, &thePrefStr); print_status(statusFile, stdOut,"%11s", thePrefStr); delete [] thePrefStr; thePrefStr = NULL; (void)QTSS_GetValueAsString(sServer, qtssRTSPHTTPCurrentSessionCount, 0, &thePrefStr); print_status(statusFile, stdOut,"%11s", thePrefStr); delete [] thePrefStr; thePrefStr = NULL; UInt32 curBandwidth = 0; theLen = sizeof(curBandwidth); (void)QTSS_GetValue(sServer, qtssRTPSvrCurBandwidth, 0, &curBandwidth, &theLen); qtss_snprintf(numStr, 11, "%"_U32BITARG_"", curBandwidth/1024); print_status(statusFile, stdOut,"%11s", numStr); (void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurPackets, 0, &thePrefStr); print_status(statusFile, stdOut,"%11s", thePrefStr); delete [] thePrefStr; thePrefStr = NULL; UInt32 currentPlaying = sServer->GetNumRTPPlayingSessions(); qtss_snprintf( numStr, sizeof(numStr) -1, "%"_U32BITARG_"", currentPlaying); print_status(statusFile, stdOut,"%14s", numStr); //is the server keeping up with the streams? //what quality are the streams? SInt64 totalRTPPaackets = sServer->GetTotalRTPPackets(); SInt64 deltaPackets = totalRTPPaackets - sLastDebugPackets; sLastDebugPackets = totalRTPPaackets; SInt64 totalQuality = sServer->GetTotalQuality(); SInt64 deltaQuality = totalQuality - sLastDebugTotalQuality; sLastDebugTotalQuality = totalQuality; SInt64 currentMaxLate = sServer->GetCurrentMaxLate(); SInt64 totalLate = sServer->GetTotalLate(); sServer->ClearTotalLate(); sServer->ClearCurrentMaxLate(); sServer->ClearTotalQuality(); ::qtss_snprintf(numStr, sizeof(numStr) -1, "%s", "0"); if (deltaPackets > 0) qtss_snprintf(numStr, sizeof(numStr) -1, "%"_S32BITARG_"", (SInt32) ((SInt64)totalLate / (SInt64) deltaPackets )); print_status(statusFile, stdOut,"%11s", numStr); qtss_snprintf(numStr,sizeof(numStr) -1, "%"_S32BITARG_"", (SInt32) currentMaxLate); print_status(statusFile, stdOut,"%11s", numStr); qtss_snprintf(numStr,sizeof(numStr) -1, "%"_S32BITARG_"", (SInt32) sServer->GetMaxLate() ); print_status(statusFile, stdOut,"%11s", numStr); ::qtss_snprintf(numStr, sizeof(numStr) -1, "%s", "0"); if (deltaPackets > 0) qtss_snprintf(numStr, sizeof(numStr) -1, "%"_S32BITARG_"", (SInt32) ((SInt64) deltaQuality / (SInt64) deltaPackets)); print_status(statusFile, stdOut,"%11s", numStr); qtss_snprintf(numStr,sizeof(numStr) -1, "%"_S32BITARG_"", (SInt32) sServer->GetNumThinned() ); print_status(statusFile, stdOut,"%11s", numStr); char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes]; (void) QTSSRollingLog::FormatDate(theDateBuffer, false); qtss_snprintf(dateStr,sizeof(dateStr) -1, "%s", theDateBuffer ); print_status(statusFile, stdOut,"%24s\n", dateStr); }
void SendStats(QTSS_StreamRef inStream, UInt32 refreshInterval, Bool16 displayHelp, StrPtrLen* fieldList) { struct FieldIndex { char* fieldName; int fieldIndex; }; const FieldIndex kFieldIndexes[] = { {"title", 1}, {"dnsname", 2}, {"curtime", 3}, {"", 4}, {"serververs", 5}, {"serverbornon", 6}, {"serverstatus", 7}, {"", 8}, {"", 9}, {"", 10}, {"", 11}, {"", 12}, {"", 13}, {"currtp", 14}, {"currtsp", 15}, {"currtsphttp", 16}, {"curthru", 17}, {"curpkts", 18}, {"totbytes", 19}, {"totconns", 20}, {"", 21}, {"connlimit", 22}, {"thrulimit", 23}, {"moviedir", 24}, {"rtspip", 25}, {"rtspport", 26}, {"rtsptimeout", 27}, {"rtptimeout", 28}, {"secstobuffer", 29}, {"", 30}, {"accesslog", 31}, {"accesslogdir",32}, {"accesslogname", 33}, {"accessrollsize", 34}, {"accessrollinterval", 35}, {"", 36}, {"errorlog", 37}, {"errorlogdir", 38}, {"errorlogname", 39}, {"errorrollsize", 40}, {"errorrollinterval", 41}, {"errorloglevel", 42}, {"", 43}, {"assertbreak", 44}, {"autostart", 45}, {"totbytesupdateinterval", 46}, {"reflectordelay", 47}, {"reflectorbucketsize", 48}, {"historyinterval", 49}, {"outoffiledesc", 50}, {"numudpsockets", 51}, {"apiversion", 52}, {"numreliableudpbuffers", 53}, {"reliableudpwastedbytes", 54}, {"numtaskthreads", 55} }; const int kMaxFieldNum = 55; static char* kEmptyStr = "?"; char* thePrefStr = kEmptyStr; char buffer[1024]; (void)QTSS_Write(inStream, sResponseHeader, ::strlen(sResponseHeader), NULL, 0); if (refreshInterval > 0) { qtss_sprintf(buffer, "<META HTTP-EQUIV=Refresh CONTENT=%"_U32BITARG_">\n", refreshInterval); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } //qtss_sprintf(buffer, "<body text=\"#000000\" bgcolor=\"#C0C0C0\" link=\"#0000FF\" vlink=\"#551A8B\" alink=\"#0000FF\">\n"); //(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); char *theHTML = "<HTML><BODY>\n"; (void)QTSS_Write(inStream, theHTML, ::strlen(theHTML), NULL, 0); if (displayHelp) { #ifndef __MacOSX__ static StrPtrLen sHelpLine1("<P><b>Streaming Server Statistics Help</b></P>\n"); #else static StrPtrLen sHelpLine1("<P><b>QuickTime Streaming Server Statistics Help</b></P>\n"); #endif static StrPtrLen sHelpLine2("<P>Example:</P>\n"); static StrPtrLen sHelpLine3("<BLOCKQUOTE><P>http://server/statsURL?help&refresh=15&fields=curtime,cpuload </P>\n"); static StrPtrLen sHelpLine4("\"?\" means that there are options being attached to the stats request.<BR>\n"); static StrPtrLen sHelpLine5("\"&\" separates multiple stats options<BR>\n<BR>\n"); static StrPtrLen sHelpLine6("<P>The three possible parameters to stats are:</P>\n"); static StrPtrLen sHelpLine7("<P>\"help\" -- shows the help information you're reading right now.</P>\n"); static StrPtrLen sHelpLine8("<P>\"refresh=[n]\" -- tells the browser to automatically update the page every [n] seconds.</P>\n"); static StrPtrLen sHelpLine9("<P>\"fields=[fieldList]\" -- show only the fields specified in comma delimited [fieldList]</P>\n"); static StrPtrLen sHelpLine10("<BLOCKQUOTE>The following fields are available for use with the \"fields\" option:</P><BLOCKQUOTE><DL>\n"); (void)QTSS_Write(inStream, sHelpLine1.Ptr, sHelpLine1.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine2.Ptr, sHelpLine2.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine3.Ptr, sHelpLine3.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine4.Ptr, sHelpLine4.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine5.Ptr, sHelpLine5.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine6.Ptr, sHelpLine6.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine7.Ptr, sHelpLine7.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine8.Ptr, sHelpLine8.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine9.Ptr, sHelpLine9.Len, NULL, 0); (void)QTSS_Write(inStream, sHelpLine10.Ptr, sHelpLine10.Len, NULL, 0); for (short i = 0; i < kMaxFieldNum; i++) { qtss_sprintf(buffer, "<DT><I>%s</I></DT>\n", kFieldIndexes[i].fieldName); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } static StrPtrLen sHelpLine11("</DL></BLOCKQUOTE></BLOCKQUOTE></BLOCKQUOTE><BR><P><HR>"); (void)QTSS_Write(inStream, sHelpLine11.Ptr, sHelpLine11.Len, NULL, 0); } StringParser fieldNamesParser(fieldList); StrPtrLen fieldName; int fieldNum = 0; do { if (fieldList != NULL) { fieldNum = 0; fieldNamesParser.ConsumeWord(&fieldName); for (short i = 0; i < kMaxFieldNum; i++) { if ( fieldName.Equal(StrPtrLen(kFieldIndexes[i].fieldName, ::strlen(kFieldIndexes[i].fieldName))) ) { fieldNum = kFieldIndexes[i].fieldIndex; break; } } } else { fieldNum++; if ( fieldNum > kMaxFieldNum ) fieldNum = 0; } UInt32 theLen = 0; switch (fieldNum) { case 1: { #if __MacOSX__ static StrPtrLen sStatsLine1("<TITLE>QuickTime Streaming Server Stats</TITLE><BR>\n"); (void)QTSS_Write(inStream, sStatsLine1.Ptr, sStatsLine1.Len, NULL, 0); #else static StrPtrLen sStatsLine1("<TITLE>Streaming Server Stats</TITLE><BR>\n"); (void)QTSS_Write(inStream, sStatsLine1.Ptr, sStatsLine1.Len, NULL, 0); #endif #if __MacOSX__ static StrPtrLen sStatsLine2("<center><h1>QuickTime Streaming Server Statistics</h1></center>\n"); (void)QTSS_Write(inStream, sStatsLine2.Ptr, sStatsLine2.Len, NULL, 0); #else static StrPtrLen sStatsLine2("<center><h1>Streaming Server Statistics</h1></center>\n"); (void)QTSS_Write(inStream, sStatsLine2.Ptr, sStatsLine2.Len, NULL, 0); #endif } break; case 2: { StrPtrLen theDNS; (void)QTSS_GetValuePtr(sServer, qtssSvrDefaultDNSName, 0, (void**)&theDNS.Ptr, &theDNS.Len); if ( theDNS.Ptr == NULL ) { // no DNS, try for the IP address only. (void)QTSS_GetValuePtr(sServer, qtssSvrDefaultIPAddr, 0, (void**)&theDNS.Ptr, &theDNS.Len); } if ( theDNS.Ptr != NULL ) { qtss_sprintf(buffer, "<b>DNS Name (default): </b> %s<BR>\n", theDNS.Ptr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 3: { char uptimebuffer[1024]; time_t curTime = ::time(NULL); qtss_sprintf(uptimebuffer, "<b>Current Time: </b> %s<BR>\n", qtss_ctime(&curTime, buffer, sizeof(buffer))); (void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0); time_t upTime = curTime - sStartupTime; #define kDaySeconds (24 * 60 * 60) #define kHourSeconds (60 * 60) #define kMinuteSeconds 60 UInt32 upTimeDays = upTime / kDaySeconds; UInt32 upTimeHours = (upTime % kDaySeconds) / kHourSeconds; UInt32 upTimeMinutes = (upTime % kHourSeconds) / kMinuteSeconds; UInt32 upTimeSeconds = (upTime % kMinuteSeconds); qtss_snprintf(uptimebuffer,sizeof(uptimebuffer), "<b>Up Time Total Seconds: </b> %"_U32BITARG_"<BR>\n", upTime); uptimebuffer[sizeof(uptimebuffer) -1] = 0; (void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0); qtss_snprintf(uptimebuffer,sizeof(uptimebuffer), "<b>Up Time: </b> %"_U32BITARG_" days %"_U32BITARG_" hours %"_U32BITARG_" minutes %"_U32BITARG_" seconds <BR>\n", upTimeDays, upTimeHours,upTimeMinutes, upTimeSeconds); uptimebuffer[sizeof(uptimebuffer) -1] = 0; (void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0); } break; case 4: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 5: { StrPtrLen theVersion; (void)QTSS_GetValuePtr(sServer, qtssSvrRTSPServerHeader, 0, (void**)&theVersion.Ptr, &theVersion.Len); Assert(theVersion.Ptr != NULL); if (theVersion.Len > 7) //Skip the "Server:" text theVersion.Ptr += 7; qtss_sprintf(buffer, "<b>Server Version: </b>%s<BR>\n", theVersion.Ptr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 6: { StrPtrLen theBuildDate; (void)QTSS_GetValuePtr(sServer, qtssSvrServerBuildDate, 0, (void**)&theBuildDate.Ptr, &theBuildDate.Len); Assert(theBuildDate.Ptr != NULL); qtss_sprintf(buffer, "<b>Server Build Date: </b> %s<BR>\n", theBuildDate.Ptr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 7: { char statusBuffer[1024]; const char* states[] = { "Starting Up", "Running", "Refusing Connections", "Fatal Error", "Shutting Down" }; QTSS_ServerState theState = qtssRunningState; theLen = sizeof(theState); (void)QTSS_GetValue(sServer, qtssSvrState, 0, &theState, &theLen); if (theState == qtssRunningState) { qtss_snprintf(statusBuffer, sizeof(statusBuffer), "<b>Status: </b> %s since %s<BR>", states[theState], qtss_ctime(&sStartupTime,buffer,sizeof(buffer))); } else qtss_snprintf(statusBuffer,sizeof(statusBuffer), "<b>Status: </b> %s<BR>", states[theState]); (void)QTSS_Write(inStream, statusBuffer, ::strlen(statusBuffer), NULL, 0); } break; case 8: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 9: { //NOOP } break; case 10: { //NOOP } break; case 11: { //NOOP } break; case 12: { /* struct vm_statistics vmStats = {}; if (vm_statistics (current_task (), &vmStats) != KERN_SUCCESS) memset (&stats, '\0', sizeof (vmStats)) ; */ (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 13: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; //********************************** case 14: { (void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurConn, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Current RTP Connections: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 15: { (void)QTSS_GetValueAsString(sServer, qtssRTSPCurrentSessionCount, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Current RTSP Connections: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 16: { (void)QTSS_GetValueAsString(sServer, qtssRTSPHTTPCurrentSessionCount, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Current RTSP over HTTP Connections: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 17: { UInt32 curBandwidth = 0; theLen = sizeof(curBandwidth); (void)QTSS_GetValue(sServer, qtssRTPSvrCurBandwidth, 0, &curBandwidth, &theLen); qtss_sprintf(buffer, "<b>Current Throughput: </b> %"_U32BITARG_" kbits<BR>\n", curBandwidth/1024); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 18: { (void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurPackets, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Current Packets Per Second: </b> %s <BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 19: { (void)QTSS_GetValueAsString(sServer, qtssRTPSvrTotalBytes, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Total Bytes Served: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 20: { (void)QTSS_GetValueAsString(sServer, qtssRTPSvrTotalConn, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Total Connections: </b> %s<BR>", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 21: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; //************************************** case 22: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaximumConnections, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Maximum Connections: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 23: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaximumBandwidth, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Maximum Throughput: </b> %s Kbits<BR>\n",thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 24: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMovieFolder, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Movie Folder Path: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 25: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTSPIPAddr, 0, &thePrefStr); qtss_sprintf(buffer, "<b>RTSP IP Address: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 26: { static StrPtrLen sRTSPPortsStart("<b>RTSP Ports: </b> "); (void)QTSS_Write(inStream, sRTSPPortsStart.Ptr, sRTSPPortsStart.Len, NULL, 0); StrPtrLen thePort; for (UInt32 theIndex = 0; true; theIndex++) { QTSS_Error theErr = QTSS_GetValuePtr(sServer, qtssSvrRTSPPorts, theIndex, (void**)&thePort.Ptr, &thePort.Len); if (theErr != QTSS_NoErr) break; Assert(thePort.Ptr != NULL); char temp[20]; qtss_sprintf(temp, "%u ", *(UInt16*)thePort.Ptr); (void)QTSS_Write(inStream, temp, ::strlen(temp), NULL, 0); } static StrPtrLen sRTSPPortsEnd("<BR>\n"); (void)QTSS_Write(inStream, sRTSPPortsEnd.Ptr, sRTSPPortsEnd.Len, NULL, 0); } break; case 27: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTSPTimeout, 0, &thePrefStr); qtss_sprintf(buffer, "<b>RTP Timeout: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 28: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTPTimeout, 0, &thePrefStr); qtss_sprintf(buffer, "<b>RTP Timeout: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 29: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 30: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 31: { if ( sAccessLogPrefs != NULL ) { thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logging"); qtss_sprintf(buffer, "<b>Access Logging: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 32: { if ( sAccessLogPrefs != NULL ) { thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_dir"); qtss_sprintf(buffer, "<b>Access Log Directory: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 33: { if ( sAccessLogPrefs != NULL ) { thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_name"); qtss_sprintf(buffer, "<b>Access Log Name: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 34: { if ( sAccessLogPrefs != NULL ) { thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_size"); qtss_sprintf(buffer, "<b>Access Log Roll Size: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 35: { if ( sAccessLogPrefs != NULL ) { thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_interval"); qtss_sprintf(buffer, "<b>Access Log Roll Interval (days): </b> %s<BR>", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 36: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 37: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogEnabled, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Logging: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 38: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogDir, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Log Directory: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 39: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogName, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Log Name: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 40: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaxErrorLogSize, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Log Roll Size: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 41: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorRollInterval, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Log Roll Interval (days): </b> %s<BR>\n",thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 42: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogVerbosity, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Error Log Verbosity: </b> %s<BR>", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 43: { (void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0); } break; case 44: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsBreakOnAssert, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Break On Assert: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 45: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsAutoRestart, 0, &thePrefStr); qtss_sprintf(buffer, "<b>AutoStart: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 46: { (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsTotalBytesUpdate, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Total Bytes Update Interval: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 47: { if (sReflectorPrefs != NULL) { thePrefStr = GetPrefAsString(sReflectorPrefs, "reflector_delay"); qtss_sprintf(buffer, "<b>Reflector Delay Time: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 48: { if (sReflectorPrefs != NULL) { thePrefStr = GetPrefAsString(sReflectorPrefs, "reflector_bucket_size"); qtss_sprintf(buffer, "<b>Reflector Bucket Size: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 49: { if ( sSvrControlPrefs != NULL) { thePrefStr = GetPrefAsString(sSvrControlPrefs, "history_update_interval"); qtss_sprintf(buffer, "<b>History Update Interval: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } } break; case 50: { Bool16 isOutOfDescriptors = false; theLen = sizeof(isOutOfDescriptors); (void)QTSS_GetValue(sServer, qtssSvrIsOutOfDescriptors, 0, &isOutOfDescriptors, &theLen); qtss_sprintf(buffer, "<b>Out of file descriptors: </b> %d<BR>\n", isOutOfDescriptors); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 51: { (void)QTSS_GetValueAsString(sServer, qtssRTPSvrNumUDPSockets, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Number of UDP sockets: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 52: { UInt32 apiVersion = 0; UInt32 size = sizeof(UInt32); (void)QTSS_GetValue(sServer, qtssServerAPIVersion, 0, &apiVersion, &size); qtss_sprintf(buffer, "<b>API version: </b> %d.%d<BR>\n", (int)( (UInt32) (apiVersion & (UInt32) 0xFFFF0000L) >> 16), (int)(apiVersion &(UInt32) 0x0000FFFFL)); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 53: { UInt32 reliableUDPBuffers = 0; UInt32 blahSize = sizeof(reliableUDPBuffers); (void)QTSS_GetValue(sServer, qtssSvrNumReliableUDPBuffers, 0, &reliableUDPBuffers, &blahSize); qtss_sprintf(buffer, "<b>Num Reliable UDP Retransmit Buffers: </b> %"_U32BITARG_"<BR>\n", reliableUDPBuffers); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 54: { UInt32 wastedBufSpace = 0; UInt32 blahSize2 = sizeof(wastedBufSpace); (void)QTSS_GetValue(sServer, qtssSvrReliableUDPWastageInBytes, 0, &wastedBufSpace, &blahSize2); qtss_sprintf(buffer, "<b>Amount of buffer space being wasted in UDP Retrans buffers: </b> %"_U32BITARG_"<BR>\n", wastedBufSpace); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; case 55: { (void)QTSS_GetValueAsString(sServer, qtssSvrNumThreads, 0, &thePrefStr); qtss_sprintf(buffer, "<b>Number of Task Threads: </b> %s<BR>\n", thePrefStr); (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0); } break; default: break; } //switch fieldNum if (fieldList != NULL && !fieldNamesParser.Expect(',')) fieldNum = 0; if (thePrefStr != kEmptyStr) delete [] thePrefStr; thePrefStr = kEmptyStr; } while (fieldNum != 0); theHTML = "</BODY></HTML>\n"; (void)QTSS_Write(inStream, theHTML, ::strlen(theHTML), NULL, 0); }
bool XMLTag::ParseTag(StringParser* parser, DTDVerifier* verifier, char* errorBuffer, int errorBufferSize) { while (true) { if (!parser->GetThru(NULL, '<')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Couldn't find a valid tag"); return false; // couldn't find beginning of tag } char c = parser->PeekFast(); if (c == '/') { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "End tag with no begin tag on line %d", parser->GetCurrentLineNumber()); return false; // we shouldn't be seeing a close tag here } if ((c != '!') && (c != '?')) break; // this should be the beginning of a regular tag ConsumeIfComment(parser); // otherwise this is a processing instruction or a c-data, so look for the next tag } int tagStartLine = parser->GetCurrentLineNumber(); StrPtrLen temp; parser->ConsumeUntil(&temp, sNonNameMask); if (temp.Len == 0) { if (errorBuffer != NULL) { if (parser->GetDataRemaining() == 0) qtss_snprintf(errorBuffer, errorBufferSize, "Unexpected end of file on line %d", parser->GetCurrentLineNumber()); else qtss_snprintf(errorBuffer, errorBufferSize,"Unexpected character (%c) on line %d", parser->PeekFast(), parser->GetCurrentLineNumber()); } return false; // bad file } fTag = temp.GetAsCString(); parser->ConsumeWhitespace(); while ((parser->PeekFast() != '>') && (parser->PeekFast() != '/')) { // we must have an attribute value for this tag XMLAttribute* attr = new XMLAttribute; fAttributes.EnQueue(&attr->fElem); parser->ConsumeUntil(&temp, sNonNameMask); if (temp.Len == 0) { if (errorBuffer != NULL) { if (parser->GetDataRemaining() == 0) qtss_snprintf(errorBuffer, errorBufferSize, "Unexpected end of file on line %d", parser->GetCurrentLineNumber()); else qtss_snprintf(errorBuffer, errorBufferSize,"Unexpected character (%c) on line %d", parser->PeekFast(), parser->GetCurrentLineNumber()); } return false; // bad file } attr->fAttrName = temp.GetAsCString(); if (!parser->Expect('=')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"Missing '=' after attribute %s on line %d", attr->fAttrName, parser->GetCurrentLineNumber()); return false; // bad attribute specification } if (!parser->Expect('"')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"Attribute %s value not in quotes on line %d", attr->fAttrName, parser->GetCurrentLineNumber()); return false; // bad attribute specification } parser->ConsumeUntil(&temp, '"'); attr->fAttrValue = temp.GetAsCString(); if (!parser->Expect('"')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Attribute %s value not in quotes on line %d", attr->fAttrName, parser->GetCurrentLineNumber()); return false; // bad attribute specification } if (verifier && !verifier->IsValidAttributeName(fTag, attr->fAttrName)) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Attribute %s not allowed in tag %s on line %d", attr->fAttrName, fTag, parser->GetCurrentLineNumber()); return false; // bad attribute specification } if (verifier && !verifier->IsValidAttributeValue(fTag, attr->fAttrName, attr->fAttrValue)) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"Bad value for attribute %s on line %d", attr->fAttrName, parser->GetCurrentLineNumber()); return false; // bad attribute specification } parser->ConsumeWhitespace(); } if (parser->PeekFast() == '/') { // this is an empty element tag, i.e. no contents or end tag (e.g <TAG attr="value" /> parser->Expect('/'); if (!parser->Expect('>')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"'>' must follow '/' on line %d", parser->GetCurrentLineNumber()); return false; // bad attribute specification } return true; // we're done with this tag } if (!parser->Expect('>')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"Bad format for tag <%s> on line %d", fTag, parser->GetCurrentLineNumber()); return false; // bad attribute specification } while(true) { parser->ConsumeUntil(&temp, '<'); // this is either value or whitespace if (parser->GetDataRemaining() < 4) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Reached end of file without end for tag <%s> declared on line %d", fTag, tagStartLine); return false; } if ((*parser)[1] == '/') { // we'll only assign a value if there were no embedded tags if (fEmbeddedTags.GetLength() == 0 && (!verifier || verifier->CanHaveValue(fTag))) fValue = temp.GetAsCString(); else { // otherwise this needs to have been just whitespace StringParser tempParser(&temp); tempParser.ConsumeWhitespace(); if (tempParser.GetDataRemaining() > 0) { if (errorBuffer) { if (fEmbeddedTags.GetLength() > 0) qtss_snprintf(errorBuffer, errorBufferSize,"Unexpected text outside of tag on line %d", tagStartLine); else qtss_snprintf(errorBuffer, errorBufferSize, "Tag <%s> on line %d not allowed to have data", fTag, tagStartLine); } } } break; // we're all done with this tag } if (((*parser)[1] != '!') && ((*parser)[1] != '?')) { // this must be the beginning of an embedded tag XMLTag* tag = NEW XMLTag(); fEmbeddedTags.EnQueue(&tag->fElem); if (!tag->ParseTag(parser, verifier, errorBuffer, errorBufferSize)) return false; if (verifier && !verifier->IsValidSubtag(fTag, tag->GetTagName())) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize, "Tag %s not allowed in tag %s on line %d", tag->GetTagName(), fTag, parser->GetCurrentLineNumber()); return false; // bad attribute specification } } else { parser->ConsumeLength(NULL, 1); // skip '<' ConsumeIfComment(parser); } } parser->ConsumeLength(NULL, 2); // skip '</' parser->ConsumeUntil(&temp, sNonNameMask); if (!temp.Equal(fTag)) { char* newTag = temp.GetAsCString(); if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"End tag </%s> on line %d doesn't match tag <%s> declared on line %d", newTag, parser->GetCurrentLineNumber(),fTag, tagStartLine); delete newTag; return false; // bad attribute specification } if (!parser->GetThru(NULL, '>')) { if (errorBuffer != NULL) qtss_snprintf(errorBuffer, errorBufferSize,"Couldn't find end of tag <%s> declared on line %d", fTag, tagStartLine); return false; // bad attribute specification } return true; }
QTSS_Error QTSSModuleUtils::SendHTTPErrorResponse( QTSS_RTSPRequestObject inRequest, QTSS_SessionStatusCode inStatusCode, Bool16 inKillSession, char *errorMessage) { static Bool16 sFalse = false; //set status code for access log (void)QTSS_SetValue(inRequest, qtssRTSPReqStatusCode, 0, &inStatusCode, sizeof(inStatusCode)); if (inKillSession) // tell the server to end the session (void)QTSS_SetValue(inRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse)); ResizeableStringFormatter theErrorMessage(NULL, 0); //allocates and deletes memory ResizeableStringFormatter bodyMessage(NULL,0); //allocates and deletes memory char messageLineBuffer[64]; // used for each line static const int maxMessageBufferChars = sizeof(messageLineBuffer) -1; messageLineBuffer[maxMessageBufferChars] = 0; // guarantee termination // ToDo: put in a more meaningful http error message for each error. Not required by spec. // ToDo: maybe use the HTTP protcol class static error strings. char* errorMsg = "error"; DateBuffer theDate; DateTranslator::UpdateDateBuffer(&theDate, 0); // get the current GMT date and time UInt32 realCode = 0; UInt32 len = sizeof(realCode); (void) QTSS_GetValue(inRequest, qtssRTSPReqRealStatusCode, 0, (void*)&realCode,&len); char serverHeaderBuffer[64]; // the qtss Server: header field len = sizeof(serverHeaderBuffer) -1; // leave room for terminator (void) QTSS_GetValue(sServer, qtssSvrRTSPServerHeader, 0, (void*)serverHeaderBuffer,&len); serverHeaderBuffer[len] = 0; // terminate. qtss_snprintf(messageLineBuffer,maxMessageBufferChars, "HTTP/1.1 %"_U32BITARG_" %s",realCode, errorMsg); theErrorMessage.Put(messageLineBuffer,::strlen(messageLineBuffer)); theErrorMessage.PutEOL(); theErrorMessage.Put(serverHeaderBuffer,::strlen(serverHeaderBuffer)); theErrorMessage.PutEOL(); qtss_snprintf(messageLineBuffer,maxMessageBufferChars, "Date: %s",theDate.GetDateBuffer()); theErrorMessage.Put(messageLineBuffer,::strlen(messageLineBuffer)); theErrorMessage.PutEOL(); Bool16 addBody = (errorMessage != NULL && ::strlen(errorMessage) != 0); // body error message so add body headers if (addBody) // body error message so add body headers { // first create the html body static const StrPtrLen htmlBodyStart("<html><body>\n"); bodyMessage.Put(htmlBodyStart.Ptr,htmlBodyStart.Len); //<h1>errorMessage</h1>\n static const StrPtrLen hStart("<h1>"); bodyMessage.Put(hStart.Ptr,hStart.Len); bodyMessage.Put(errorMessage,::strlen(errorMessage)); static const StrPtrLen hTerm("</h1>\n"); bodyMessage.Put(hTerm.Ptr,hTerm.Len); static const StrPtrLen htmlBodyTerm("</body></html>\n"); bodyMessage.Put(htmlBodyTerm.Ptr,htmlBodyTerm.Len); // write body headers static const StrPtrLen bodyHeaderType("Content-Type: text/html"); theErrorMessage.Put(bodyHeaderType.Ptr,bodyHeaderType.Len); theErrorMessage.PutEOL(); qtss_snprintf(messageLineBuffer,maxMessageBufferChars, "Content-Length: %"_U32BITARG_"", bodyMessage.GetBytesWritten()); theErrorMessage.Put(messageLineBuffer,::strlen(messageLineBuffer)); theErrorMessage.PutEOL(); } static const StrPtrLen headerClose("Connection: close"); theErrorMessage.Put(headerClose.Ptr,headerClose.Len); theErrorMessage.PutEOL(); theErrorMessage.PutEOL(); // terminate headers with empty line if (addBody) // add html body { theErrorMessage.Put(bodyMessage.GetBufPtr(),bodyMessage.GetBytesWritten()); } // // Now that we've formatted the message into the temporary buffer, // write it out to the request stream and the Client Session object (void)QTSS_Write(inRequest, theErrorMessage.GetBufPtr(), theErrorMessage.GetBytesWritten(), NULL, 0); (void)QTSS_SetValue(inRequest, qtssRTSPReqRespMsg, 0, theErrorMessage.GetBufPtr(), theErrorMessage.GetBytesWritten()); return QTSS_RequestFailed; }
QTSS_Error LogError(QTSS_RoleParamPtr inParamBlock) { Assert(NULL != inParamBlock->errorParams.inBuffer); if (inParamBlock->errorParams.inBuffer == NULL) return QTSS_NoErr; UInt16 verbLvl = (UInt16) inParamBlock->errorParams.inVerbosity; if (verbLvl >= qtssIllegalVerbosity) verbLvl = qtssFatalVerbosity; QTSServerPrefs* thePrefs = QTSServerInterface::GetServer()->GetPrefs(); OSMutexLocker locker(sLogMutex); if (thePrefs->GetErrorLogVerbosity() >= inParamBlock->errorParams.inVerbosity) { size_t inStringLen = ::strlen(inParamBlock->errorParams.inBuffer); size_t lastStringLen = ::strlen(sLastErrorString); Bool16 isDuplicate = true; if (inStringLen > sizeof(sLastErrorString) -1) //truncate to max char buffer subtract \0 terminator inStringLen = sizeof(sLastErrorString) -1; if (lastStringLen != inStringLen) //same size? isDuplicate = false; // different sizes else if (::strncmp(inParamBlock->errorParams.inBuffer, sLastErrorString, lastStringLen ) != 0 ) //same chars? isDuplicate = false; //different chars //is this error message the same as the last one we received? if ( isDuplicate ) { //yes? increment count and bail if it's not the first time we've seen this message (otherwise fall thourhg and write it to the log) sDupErrorStringCount++; return QTSS_NoErr; } else { //we have a new error message, write a "previous line" message before writing the new log entry if ( sDupErrorStringCount >= 1 ) { /*** clean this up - lots of duplicate code ***/ //The error logger is the bottleneck for any and all messages printed by the server. //For debugging purposes, these messages can be printed to stdout as well. if (thePrefs->IsScreenLoggingEnabled()) qtss_printf("--last message repeated %d times\n", sDupErrorStringCount); CheckErrorLogState(); if (sErrorLog == NULL) return QTSS_NoErr; //timestamp the error char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes]; Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false); //for now, just ignore the error. if (!result) theDateBuffer[0] = '\0'; char tempBuffer[kMaxLogStringLen]; qtss_snprintf(tempBuffer,sizeof(tempBuffer), "%s: --last message repeated %d times\n", theDateBuffer, sDupErrorStringCount); sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll); sDupErrorStringCount = 0; } ::strncpy(sLastErrorString, inParamBlock->errorParams.inBuffer, sizeof(sLastErrorString)); sLastErrorString[sizeof(sLastErrorString)-1] = '\0'; } //The error logger is the bottleneck for any and all messages printed by the server. //For debugging purposes, these messages can be printed to stdout as well. if (thePrefs->IsScreenLoggingEnabled()) qtss_printf("%s %s\n", sErrorLevel[verbLvl], inParamBlock->errorParams.inBuffer); CheckErrorLogState(); if (sErrorLog == NULL) return QTSS_NoErr; //timestamp the error char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes]; Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false); //for now, just ignore the error. if (!result) theDateBuffer[0] = '\0'; char tempBuffer[kMaxLogStringLen]; qtss_snprintf(tempBuffer,sizeof(tempBuffer), "%s: %s %s\n", theDateBuffer, sErrorLevel[verbLvl], inParamBlock->errorParams.inBuffer); tempBuffer[sizeof(tempBuffer)-2] = '\n'; //make sure the entry has a line feed before the \0 terminator tempBuffer[sizeof(tempBuffer)-1] = '\0'; //make sure it is 0 terminated. sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll); } return QTSS_NoErr; }
Bool16 PLBroadcastDef::SetDefaults( const char* setupFileName ) { Bool16 didFail = false; if (mDestAddress != NULL) mIgnoreFileIP = true; if ( !didFail && !mIgnoreFileIP) didFail = this->SetValue( &mDestAddress, SocketUtils::GetIPAddrStr(0)->Ptr ); if ( !didFail ) didFail = this->SetValue( &mBasePort, "5004" ); if ( !didFail ) didFail = this->SetValue( &mPlayMode, "sequential_looped" ); if ( !didFail ) didFail = this->SetValue( &mMaxUpcomingMovieListSize, "7" ); if ( !didFail ) didFail = this->SetValue( &mLogging, "enabled" ); if ( !didFail ) didFail = this->SetValue( &mRTSPPort, "554" ); char nameBuff[512]; ::strcpy( nameBuff, "broadcast" ); if (setupFileName) ::strcpy( nameBuff, setupFileName ); int baseLen = strlen(nameBuff); /* if you want to add .log to the base name of the description file with the .ext stripped, un comment this code. rt 8.12.99 */ char *ext = NULL; ext = ::strrchr( nameBuff, '.' ); if ( ext ) { *ext = 0; baseLen = ::strlen(nameBuff); } ::strcat( nameBuff, ".log" ); if ( !didFail ) didFail = this->SetValue( &mLogFile, nameBuff ); nameBuff[baseLen] = 0; ::strcat( nameBuff, ".ply" ); if ( !didFail ) didFail = this->SetValue( &mPlayListFile, nameBuff ); // nameBuff[baseLen] = 0; // ::strcat( nameBuff, ".mov" ); // if ( !didFail ) // didFail = this->SetValue( &mSDPReferenceMovie, nameBuff ); nameBuff[baseLen] = 0; ::strcat( nameBuff, ".sdp" ); if ( !didFail ) didFail = this->SetValue( &mSDPFile, nameBuff ); if ( !didFail ) didFail = this->SetValue( &mDestSDPFile, "no_name" ); /* current, upcoming, and replacelist created by [email protected] */ nameBuff[baseLen] = 0; ::strcat( nameBuff, ".current" ); if ( !didFail ) didFail = this->SetValue( &mCurrentFile, nameBuff ); nameBuff[baseLen] = 0; ::strcat( nameBuff, ".upcoming" ); if ( !didFail ) didFail = this->SetValue( &mUpcomingFile, nameBuff ); nameBuff[baseLen] = 0; ::strcat( nameBuff, ".replacelist" ); if ( !didFail ) didFail = this->SetValue( &mReplaceFile, nameBuff ); nameBuff[baseLen] = 0; ::strcat( nameBuff, ".stoplist" ); if ( !didFail ) didFail = this->SetValue( &mStopFile, nameBuff ); nameBuff[baseLen] = 0; ::strcat( nameBuff, ".insertlist" ); if ( !didFail ) didFail = this->SetValue( &mInsertFile, nameBuff ); if ( !didFail ) didFail = this->SetValue( &mShowCurrent, "enabled" ); if ( !didFail ) didFail = this->SetValue( &mShowUpcoming, "enabled" ); if ( !didFail ) didFail = this->SetValue( &mStartTime, "0" ); if ( !didFail ) didFail = this->SetValue( &mEndTime, "0" ); if ( !didFail ) didFail = this->SetValue( &mIsDynamic, "disabled" ); if ( !didFail ) didFail = this->SetValue( &mName, "" ); if ( !didFail ) didFail = this->SetValue( &mPassword, "" ); if ( !didFail ) didFail = this->SetValue( &mTTL, "1" ); if ( !didFail ) didFail = this->SetValue( &mClientBufferDelay, "0" ); //see if there is a defaults File. //if there is one load it and over-ride the other defaults if (NULL != setupFileName) { int len = ::strlen(setupFileName) + 10; char *defaultFileName = new char[len]; qtss_snprintf(defaultFileName, len, "%s%s", setupFileName, ".def"); (void) ::ParseConfigFile( false, defaultFileName, ConfigSetter, this ); //ignore if no defaults file delete [] defaultFileName; } /* ***************************************************** */ return didFail; }
/*! \brief 事件由ServiceSession Task进行处理,大多数为网络报文处理事件 \param \return 处理完成返回0,断开Session返回-1 \ingroup \see */ SInt64 CServiceSession::Run() { //获取事件类型 EventFlags events = this->GetEvents(); QTSS_Error err = QTSS_NoErr; QTSSModule* theModule = NULL; UInt32 numModules = 0; // Some callbacks look for this struct in the thread object OSThreadDataSetter theSetter(&fModuleState, NULL); //超时事件或者Kill事件,进入释放流程:清理 & 返回-1 if (events & Task::kKillEvent) fLiveSession = false; if(events & Task::kTimeoutEvent) { //客户端Session超时,暂时不处理 char msgStr[512]; qtss_snprintf(msgStr, sizeof(msgStr), "session timeout,release session\n"); QTSServerInterface::LogError(qtssMessageVerbosity, msgStr); return -1; } //正常事件处理流程 while (this->IsLiveSession()) { //报文处理以状态机的形式,可以方便多次处理同一个消息 switch (fState) { case kReadingFirstRequest://首次对Socket进行读取 { if ((err = fInputStream.ReadRequest()) == QTSS_NoErr) { //如果RequestStream返回QTSS_NoErr,就表示已经读取了目前所到达的网络数据 //但,还不能构成一个整体报文,还要继续等待读取... fInputSocketP->RequestEvent(EV_RE); return 0; } if ((err != QTSS_RequestArrived) && (err != E2BIG)) { // Any other error implies that the client has gone away. At this point, // we can't have 2 sockets, so we don't need to do the "half closed" check // we do below Assert(err > 0); Assert(!this->IsLiveSession()); break; } if ((err == QTSS_RequestArrived) || (err == E2BIG)) fState = kHaveCompleteMessage; } continue; case kReadingRequest://读取请求报文 { //读取锁,已经在处理一个报文包时,不进行新网络报文的读取和处理 OSMutexLocker readMutexLocker(&fReadMutex); //网络请求报文存储在fInputStream中 if ((err = fInputStream.ReadRequest()) == QTSS_NoErr) { //如果RequestStream返回QTSS_NoErr,就表示已经读取了目前所到达的网络数据 //但,还不能构成一个整体报文,还要继续等待读取... fInputSocketP->RequestEvent(EV_RE); return 0; } if ((err != QTSS_RequestArrived) && (err != E2BIG) && (err != QTSS_BadArgument)) { //Any other error implies that the input connection has gone away. // We should only kill the whole session if we aren't doing HTTP. // (If we are doing HTTP, the POST connection can go away) Assert(err > 0); if (fOutputSocketP->IsConnected()) { // If we've gotten here, this must be an HTTP session with // a dead input connection. If that's the case, we should // clean up immediately so as to not have an open socket // needlessly lingering around, taking up space. Assert(fOutputSocketP != fInputSocketP); Assert(!fInputSocketP->IsConnected()); fInputSocketP->Cleanup(); return 0; } else { Assert(!this->IsLiveSession()); break; } } fState = kHaveCompleteMessage; } case kHaveCompleteMessage://读取到完整的请求报文 { Assert( fInputStream.GetRequestBuffer() ); Assert(fRequest == NULL); //根据具体请求报文构造HTTPRequest请求类 fRequest = NEW HTTPRequest(&sServiceStr, fInputStream.GetRequestBuffer()); //在这里,我们已经读取了一个完整的Request,并准备进行请求的处理,直到响应报文发出 //在此过程中,此Session的Socket不进行任何网络数据的读/写; fReadMutex.Lock(); fSessionMutex.Lock(); //清空发送缓冲区 fOutputStream.ResetBytesWritten(); //网络请求超过了缓冲区,返回Bad Request if (err == E2BIG) { //返回HTTP报文,错误码408 //(void)QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientBadRequest, qtssMsgRequestTooLong); fState = kSendingResponse; break; } // Check for a corrupt base64 error, return an error if (err == QTSS_BadArgument) { //返回HTTP报文,错误码408 //(void)QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientBadRequest, qtssMsgBadBase64); fState = kSendingResponse; break; } Assert(err == QTSS_RequestArrived); fState = kFilteringRequest; } case kFilteringRequest: { //刷新Session保活时间 fTimeoutTask.RefreshTimeout(); //对请求报文进行解析 QTSS_Error theErr = SetupRequest(); //当SetupRequest步骤未读取到完整的网络报文,需要进行等待 if(theErr == QTSS_WouldBlock) { this->ForceSameThread(); fInputSocketP->RequestEvent(EV_RE); // We are holding mutexes, so we need to force // the same thread to be used for next Run() return 0;//返回0表示有事件才进行通知,返回>0表示规定事件后调用Run } //每一步都检测响应报文是否已完成,完成则直接进行回复响应 if (/*fRequest->HasResponseBeenSent()*/fOutputStream.GetBytesWritten() > 0) { fState = kSendingResponse; break; } fState = kPreprocessingRequest; break; } case kPreprocessingRequest: { //请求预处理过程 //TODO:报文处理过程 fState = kCleaningUp; break; } case kProcessingRequest: { if (fOutputStream.GetBytesWritten() == 0) { //如果到这里,响应报文还没有形成,返回500 Server Internal Error ////QTSSModuleUtils::SendErrorResponse(fRequest, qtssServerInternal, qtssMsgNoModuleForRequest); } fState = kSendingResponse; } case kSendingResponse: { //响应报文发送,确保完全发送 Assert(fRequest != NULL); //发送响应报文 err = fOutputStream.Flush(); if (err == EAGAIN) { // If we get this error, we are currently flow-controlled and should // wait for the socket to become writeable again //如果收到Socket EAGAIN错误,那么我们需要等Socket再次可写的时候再调用发送 fSocket.RequestEvent(EV_WR); this->ForceSameThread(); // We are holding mutexes, so we need to force // the same thread to be used for next Run() return 0; } else if (err != QTSS_NoErr) { // Any other error means that the client has disconnected, right? Assert(!this->IsLiveSession()); break; } fState = kCleaningUp; } case kCleaningUp: { // Cleaning up consists of making sure we've read all the incoming Request Body // data off of the socket if (this->GetRemainingReqBodyLen() > 0) { err = this->DumpRequestData(); if (err == EAGAIN) { fInputSocketP->RequestEvent(EV_RE); this->ForceSameThread(); // We are holding mutexes, so we need to force // the same thread to be used for next Run() return 0; } } //一次请求的读取、处理、响应过程完整,等待下一次网络报文! this->CleanupRequest(); fState = kReadingRequest; } } } //清空Session占用的所有资源 this->CleanupRequest(); //Session引用数为0,返回-1后,系统会将此Session删除 if (fObjectHolders == 0) return -1; //如果流程走到这里,Session实际已经无效了,应该被删除,但没有,因为还有其他地方引用了Session对象 return 0; }
/* 在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 }