openRTSP分析1

openrtsp是live555里面作为rtsp客户端的一个例子程序。默认是接收rtsp流并保存为文件,里面对多种格式进行了处理。网上也有很多对openrtsp的分析的文章,但是个人感觉都不是太详细,一般都只有个大概的流程。这里再给这个过程捋一捋。

主要是一些重要的线路,细节部分就不予讨论。

在playCommon.cpp中是从main开始执行的。

开始是

 TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  env = BasicUsageEnvironment::createNew(*scheduler);

这个是不变的。

做一些初始化参数的工作。然后是

ourClient = createClient(*env, streamURL, verbosityLevel, progName);

createClient的初始化这里省略。

if (sendOptionsRequest) {
    // Begin by sending an "OPTIONS" command:
    getOptions(continueAfterOPTIONS);
  } else {
    continueAfterOPTIONS(NULL, 0, NULL);
  }

这里主要是发送option的函数。

其实是调用openRtsp中定义的函数

void getOptions(RTSPClient::responseHandler* afterFunc) { 
  ourRTSPClient->sendOptionsCommand(afterFunc, ourAuthenticator);
}

我们来看看这个sendOptionsCommand

unsigned RTSPClient::sendOptionsCommand(responseHandler* responseHandler, Authenticator* authenticator) {
  if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
  return sendRequest(new RequestRecord(++fCSeq, "OPTIONS", responseHandler));
}

我们来看看这个RequestRecord类的初始化

RTSPClient::RequestRecord::RequestRecord(unsigned cseq, char const* commandName, responseHandler* handler,
					 MediaSession* session, MediaSubsession* subsession, u_int32_t booleanFlags,
					 double start, double end, float scale, char const* contentStr)
  : fNext(NULL), fCSeq(cseq), fCommandName(commandName), fSession(session), fSubsession(subsession), fBooleanFlags(booleanFlags),
    fStart(start), fEnd(end), fScale(scale), fContentStr(strDup(contentStr)), fHandler(handler) {
}

这里主要数初始化了fCommondName和fHandler其他参数都是初始化了的。

unsigned RTSPClient::sendRequest(RequestRecord* request) {
  char* cmd = NULL;
  do {
    Boolean connectionIsPending = False;
    if (!fRequestsAwaitingConnection.isEmpty()) {
      // A connection is currently pending (with at least one enqueued request).  Enqueue this request also:
      connectionIsPending = True;
    } else if (fInputSocketNum < 0) { // we need to open a connection
      int connectResult = openConnection();
      if (connectResult < 0) break; // an error occurred
      else if (connectResult == 0) {
	// A connection is pending
        connectionIsPending = True;
      } // else the connection succeeded.  Continue sending the command.u
    }
    if (connectionIsPending) {
      fRequestsAwaitingConnection.enqueue(request);
      return request->cseq();
    }

    // If requested (and we're not already doing it, or have done it), set up the special protocol for tunneling RTSP-over-HTTP:
    if (fTunnelOverHTTPPortNum != 0 && strcmp(request->commandName(), "GET") != 0 && fOutputSocketNum == fInputSocketNum) {
      if (!setupHTTPTunneling1()) break;
      fRequestsAwaitingHTTPTunneling.enqueue(request);
      return request->cseq();
    }

    // Construct and send the command:

    // First, construct command-specific headers that we need:

    char* cmdURL = fBaseURL; // by default
    Boolean cmdURLWasAllocated = False;

    char const* protocolStr = "RTSP/1.0"; // by default

    char* extraHeaders = (char*)""; // by default
    Boolean extraHeadersWereAllocated = False; 

    char* contentLengthHeader = (char*)""; // by default
    Boolean contentLengthHeaderWasAllocated = False;

    char const* contentStr = request->contentStr(); // by default
    if (contentStr == NULL) contentStr = "";
    unsigned contentStrLen = strlen(contentStr);
    if (contentStrLen > 0) {
      char const* contentLengthHeaderFmt =
	"Content-Length: %d\r\n";
      unsigned contentLengthHeaderSize = strlen(contentLengthHeaderFmt)
	+ 20 /* max int len */;
      contentLengthHeader = new char[contentLengthHeaderSize];
      sprintf(contentLengthHeader, contentLengthHeaderFmt, contentStrLen);
      contentLengthHeaderWasAllocated = True;
    }
	//根据commandName不同执行不同的程序段。
    if (strcmp(request->commandName(), "DESCRIBE") == 0) {
      extraHeaders = (char*)"Accept: application/sdp\r\n";
    } else if (strcmp(request->commandName(), "OPTIONS") == 0) {
    } else if (strcmp(request->commandName(), "ANNOUNCE") == 0) {
      extraHeaders = (char*)"Content-Type: application/sdp\r\n";
    } else if (strcmp(request->commandName(), "SETUP") == 0) {
      MediaSubsession& subsession = *request->subsession();
      Boolean streamUsingTCP = (request->booleanFlags()&0x1) != 0;
      Boolean streamOutgoing = (request->booleanFlags()&0x2) != 0;
      Boolean forceMulticastOnUnspecified = (request->booleanFlags()&0x4) != 0;

      char const *prefix, *separator, *suffix;
      constructSubsessionURL(subsession, prefix, separator, suffix);

      char const* transportFmt;
      if (strcmp(subsession.protocolName(), "UDP") == 0) {
	suffix = "";
	transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n";
      } else {
	transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n";
      }

      cmdURL = new char[strlen(prefix) + strlen(separator) + strlen(suffix) + 1];
      cmdURLWasAllocated = True;
      sprintf(cmdURL, "%s%s%s", prefix, separator, suffix);

      // Construct a "Transport:" header.
      char const* transportTypeStr;
      char const* modeStr = streamOutgoing ? ";mode=receive" : "";
          // Note: I think the above is nonstandard, but DSS wants it this way
      char const* portTypeStr;
      portNumBits rtpNumber, rtcpNumber;
      if (streamUsingTCP) { // streaming over the RTSP connection
	transportTypeStr = "/TCP;unicast";
	portTypeStr = ";interleaved";
	rtpNumber = fTCPStreamIdCount++;
	rtcpNumber = fTCPStreamIdCount++;
      } else { // normal RTP streaming
	unsigned connectionAddress = subsession.connectionEndpointAddress();
        Boolean requestMulticastStreaming
	  = IsMulticastAddress(connectionAddress) || (connectionAddress == 0 && forceMulticastOnUnspecified);
	transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast";
	portTypeStr = ";client_port";
	rtpNumber = subsession.clientPortNum();
	if (rtpNumber == 0) {
	  envir().setResultMsg("Client port number unknown\n");
	  delete[] cmdURL;
	  break;
	}
	rtcpNumber = rtpNumber + 1;
      }
      unsigned transportSize = strlen(transportFmt)
	+ strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 /* max port len */;
      char* transportStr = new char[transportSize];
      sprintf(transportStr, transportFmt,
	      transportTypeStr, modeStr, portTypeStr, rtpNumber, rtcpNumber);

      // When sending more than one "SETUP" request, include a "Session:" header in the 2nd and later commands:
      char* sessionStr = createSessionString(fLastSessionId);

      // The "Transport:" and "Session:" (if present) headers make up the 'extra headers':
      extraHeaders = new char[transportSize + strlen(sessionStr)];
      extraHeadersWereAllocated = True;
      sprintf(extraHeaders, "%s%s", transportStr, sessionStr);
      delete[] transportStr; delete[] sessionStr;
    } else if (strcmp(request->commandName(), "GET") == 0 || strcmp(request->commandName(), "POST") == 0) {
      // We will be sending a HTTP (not a RTSP) request.
      // Begin by re-parsing our RTSP URL, just to get the stream name, which we'll use as our 'cmdURL' in the subsequent request:
      char* username;
      char* password;
      NetAddress destAddress;
      portNumBits urlPortNum;
      if (!parseRTSPURL(envir(), fBaseURL, username, password, destAddress, urlPortNum, (char const**)&cmdURL)) break;
      if (cmdURL[0] == '\0') cmdURL = (char*)"/";
      delete[] username;
      delete[] password;

      protocolStr = "HTTP/1.0";

      if (strcmp(request->commandName(), "GET") == 0) {
	// Create a 'session cookie' string, using MD5:
	struct {
	  struct timeval timestamp;
	  unsigned counter;
	} seedData;
	gettimeofday(&seedData.timestamp, NULL);
	seedData.counter = ++fSessionCookieCounter;
	our_MD5Data((unsigned char*)(&seedData), sizeof seedData, fSessionCookie);
	// DSS seems to require that the 'session cookie' string be 22 bytes long:
	fSessionCookie[23] = '\0';
	
	char const* const extraHeadersFmt =
	  "x-sessioncookie: %s\r\n"
	  "Accept: application/x-rtsp-tunnelled\r\n"
	  "Pragma: no-cache\r\n"
	  "Cache-Control: no-cache\r\n";
	unsigned extraHeadersSize = strlen(extraHeadersFmt)
	  + strlen(fSessionCookie);
	extraHeaders = new char[extraHeadersSize];
	extraHeadersWereAllocated = True;
	sprintf(extraHeaders, extraHeadersFmt,
	fSessionCookie);
      } else { // "POST"
	char const* const extraHeadersFmt =
	  "x-sessioncookie: %s\r\n"
	  "Content-Type: application/x-rtsp-tunnelled\r\n"
	  "Pragma: no-cache\r\n"
	  "Cache-Control: no-cache\r\n"
	  "Content-Length: 32767\r\n"
	  "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n";
	unsigned extraHeadersSize = strlen(extraHeadersFmt)
	  + strlen(fSessionCookie);
	extraHeaders = new char[extraHeadersSize];
	extraHeadersWereAllocated = True;
	sprintf(extraHeaders, extraHeadersFmt,
		fSessionCookie);
      }
    } else { // "PLAY", "PAUSE", "TEARDOWN", "RECORD", "SET_PARAMETER", "GET_PARAMETER"
      // First, make sure that we have a RTSP session in progress
      if (fLastSessionId == NULL) {
	envir().setResultMsg("No RTSP session is currently in progress\n");
	break;
      }

      char const* sessionId;
      float originalScale;
      if (request->session() != NULL) {
	// Session-level operation
	cmdURL = (char*)sessionURL(*request->session());

	sessionId = fLastSessionId;
	originalScale = request->session()->scale();
      } else {
	// Media-level operation
	char const *prefix, *separator, *suffix;
	constructSubsessionURL(*request->subsession(), prefix, separator, suffix);
	cmdURL = new char[strlen(prefix) + strlen(separator) + strlen(suffix) + 1];
	cmdURLWasAllocated = True;
	sprintf(cmdURL, "%s%s%s", prefix, separator, suffix);
	
	sessionId = request->subsession()->sessionId();
	originalScale = request->subsession()->scale();
      }

      if (strcmp(request->commandName(), "PLAY") == 0) {
	// Create "Session:", "Scale:", and "Range:" headers; these make up the 'extra headers':
	char* sessionStr = createSessionString(sessionId);
	char* scaleStr = createScaleString(request->scale(), originalScale);
	char* rangeStr = createRangeString(request->start(), request->end());
	extraHeaders = new char[strlen(sessionStr) + strlen(scaleStr) + strlen(rangeStr) + 1];
	extraHeadersWereAllocated = True;
	sprintf(extraHeaders, "%s%s%s", sessionStr, scaleStr, rangeStr);
	delete[] sessionStr; delete[] scaleStr; delete[] rangeStr;
      } else {
	// Create a "Session:" header; this makes up our 'extra headers':
	extraHeaders = createSessionString(sessionId);
	extraHeadersWereAllocated = True;
      }
    }

    char* authenticatorStr = createAuthenticatorString(request->commandName(), fBaseURL);

    char const* const cmdFmt =
      "%s %s %s\r\n"
      "CSeq: %d\r\n"
      "%s"
      "%s"
      "%s"
      "%s"
      "\r\n"
      "%s";
    unsigned cmdSize = strlen(cmdFmt)
      + strlen(request->commandName()) + strlen(cmdURL) + strlen(protocolStr)
      + 20 /* max int len */
      + strlen(authenticatorStr)
      + fUserAgentHeaderStrLen
      + strlen(extraHeaders)
      + strlen(contentLengthHeader)
      + contentStrLen;
    cmd = new char[cmdSize];
    sprintf(cmd, cmdFmt,
	    request->commandName(), cmdURL, protocolStr,
	    request->cseq(),
	    authenticatorStr,
	    fUserAgentHeaderStr,
            extraHeaders,
	    contentLengthHeader,
	    contentStr);
    delete[] authenticatorStr;
    if (cmdURLWasAllocated) delete[] cmdURL;
    if (extraHeadersWereAllocated) delete[] extraHeaders;
    if (contentLengthHeaderWasAllocated) delete[] contentLengthHeader;

    if (fVerbosityLevel >= 1) envir() << "Sending request: " << cmd << "\n";

    if (fTunnelOverHTTPPortNum != 0 && strcmp(request->commandName(), "GET") != 0 && strcmp(request->commandName(), "POST") != 0) {
      // When we're tunneling RTSP-over-HTTP, we Base-64-encode the request before we send it.
      // (However, we don't do this for the HTTP "GET" and "POST" commands that we use to set up the tunnel.)
      char* origCmd = cmd;
      cmd = base64Encode(origCmd, strlen(cmd));
      if (fVerbosityLevel >= 1) envir() << "\tThe request was base-64 encoded to: " << cmd << "\n\n";
      delete[] origCmd;
    }
	//将命令发送出去,上面是对发送命令做一个拼接
    if (send(fOutputSocketNum, cmd, strlen(cmd), 0) < 0) {
      char const* errFmt = "%s send() failed: ";
      unsigned const errLength = strlen(errFmt) + strlen(request->commandName());
      char* err = new char[errLength];
      sprintf(err, errFmt, request->commandName());
      envir().setResultErrMsg(err);
      delete[] err;
      break;
    }

    // The command send succeeded, so enqueue the request record, so that its response (when it comes) can be handled:
    fRequestsAwaitingResponse.enqueue(request);

    delete[] cmd;
    return request->cseq();
  } while (0);

  // An error occurred, so call the response handler immediately (indicating the error):
  delete[] cmd;
  handleRequestError(request);
  delete request;
  return 0;
}

上面这段代码主要是针对不同的命令做一些拼接,然后send出去。但是我们看看这个send使用的socket是fOutputSocketNum是什么时候建立的呢。

在sendReques中先判断fRequestsAwaitingConnection是否为isEmpty,如果不是就会openConnection()

在我们再来看看这个openConnection

int RTSPClient::openConnection() {
  do {
    // Set up a connection to the server.  Begin by parsing the URL:

    char* username;
    char* password;
    NetAddress destAddress;
    portNumBits urlPortNum;
    char const* urlSuffix;
    if (!parseRTSPURL(envir(), fBaseURL, username, password, destAddress, urlPortNum, &urlSuffix)) break;
    portNumBits destPortNum = fTunnelOverHTTPPortNum == 0 ? urlPortNum : fTunnelOverHTTPPortNum;
    if (username != NULL || password != NULL) {
      fCurrentAuthenticator.setUsernameAndPassword(username, password);
      delete[] username;
      delete[] password;
    }

    // We don't yet have a TCP socket (or we used to have one, but it got closed).  Set it up now.
    fInputSocketNum = fOutputSocketNum = setupStreamSocket(envir(), 0);
    if (fInputSocketNum < 0) break;
      
    // Connect to the remote endpoint:
    fServerAddress = *(netAddressBits*)(destAddress.data());
    int connectResult = connectToServer(fInputSocketNum, destPortNum);
    if (connectResult < 0) break;
    else if (connectResult > 0) {
      // The connection succeeded.  Arrange to handle responses to requests sent on it:
      envir().taskScheduler().setBackgroundHandling(fInputSocketNum, SOCKET_READABLE,
						    (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this);
    }
    return connectResult;
  } while (0);
  
  resetTCPSockets();
  return -1;
}

我们看到这里fInputSocketNum = fOutputSocketNum = setupStreamSocket(envir(), 0);了

这里面主要是

int setupStreamSocket(UsageEnvironment& env,
                      Port port, Boolean makeNonBlocking) {
  if (!initializeWinsockIfNecessary()) {
    socketErr(env, "Failed to initialize 'winsock': ");
    return -1;
  }

  int newSocket = createSocket(SOCK_STREAM);
  if (newSocket < 0) {
    socketErr(env, "unable to create stream socket: ");
    return newSocket;
  }

  int reuseFlag = groupsockPriv(env)->reuseFlag;
  reclaimGroupsockPriv(env);
  if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR,
		 (const char*)&reuseFlag, sizeof reuseFlag) < 0) {
    socketErr(env, "setsockopt(SO_REUSEADDR) error: ");
    closeSocket(newSocket);
    return -1;
  }

  // SO_REUSEPORT doesn't really make sense for TCP sockets, so we
  // normally don't set them.  However, if you really want to do this
  // #define REUSE_FOR_TCP
#ifdef REUSE_FOR_TCP
#if defined(__WIN32__) || defined(_WIN32)
  // Windoze doesn't properly handle SO_REUSEPORT
#else
#ifdef SO_REUSEPORT
  if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT,
		 (const char*)&reuseFlag, sizeof reuseFlag) < 0) {
    socketErr(env, "setsockopt(SO_REUSEPORT) error: ");
    closeSocket(newSocket);
    return -1;
  }
#endif
#endif
#endif

  // Note: Windoze requires binding, even if the port number is 0
#if defined(__WIN32__) || defined(_WIN32)
#else
  if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) {
#endif
    MAKE_SOCKADDR_IN(name, ReceivingInterfaceAddr, port.num());
    if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) {
      char tmpBuffer[100];
      sprintf(tmpBuffer, "bind() error (port number: %d): ",
	      ntohs(port.num()));
      socketErr(env, tmpBuffer);
      closeSocket(newSocket);
      return -1;
    }
#if defined(__WIN32__) || defined(_WIN32)
#else
  }
#endif

  if (makeNonBlocking) {
    if (!makeSocketNonBlocking(newSocket)) {
      socketErr(env, "failed to make non-blocking: ");
      closeSocket(newSocket);
      return -1;
    }
  }

  return newSocket;
}

我们看到这里主要是做了建立socket和bind的工作。

再看下面做做了connectToServer

int RTSPClient::connectToServer(int socketNum, portNumBits remotePortNum) {
  MAKE_SOCKADDR_IN(remoteName, fServerAddress, htons(remotePortNum));
  if (fVerbosityLevel >= 1) {
    envir() << "Opening connection to " << AddressString(remoteName).val() << ", port " << remotePortNum << "...\n";
  }
  if (connect(socketNum, (struct sockaddr*) &remoteName, sizeof remoteName) != 0) {
    int const err = envir().getErrno();
    if (err == EINPROGRESS || err == EWOULDBLOCK) {
      // The connection is pending; we'll need to handle it later.  Wait for our socket to be 'writable', or have an exception.
      envir().taskScheduler().setBackgroundHandling(socketNum, SOCKET_WRITABLE|SOCKET_EXCEPTION,
						    (TaskScheduler::BackgroundHandlerProc*)&connectionHandler, this);
      return 0;
    }
    envir().setResultErrMsg("connect() failed: ");
    if (fVerbosityLevel >= 1) envir() << "..." << envir().getResultMsg() << "\n";
    return -1;
  }
  if (fVerbosityLevel >= 1) envir() << "...local connection opened\n";

  return 1;
}

主要是tcp的connection操作。这里会执行if里面的,我们来看看这个setBackgroundHandling

void BasicTaskScheduler
  ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
  if (socketNum < 0) return;
  FD_CLR((unsigned)socketNum, &fReadSet);
  FD_CLR((unsigned)socketNum, &fWriteSet);
  FD_CLR((unsigned)socketNum, &fExceptionSet);
  if (conditionSet == 0) {
    fHandlers->clearHandler(socketNum);
    if (socketNum+1 == fMaxNumSockets) {
      --fMaxNumSockets;
    }
  } else {
    fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
    if (socketNum+1 > fMaxNumSockets) {
      fMaxNumSockets = socketNum+1;
    }
    if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);
    if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);
    if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);
  }
}

主要是将socket加入到set集中,这里设置的write和excetion

这里的assignHandler主要是将handlerProc加入到fHandlers中,并且将socketNum加入到这个节点中。

if (connectionIsPending) {
      fRequestsAwaitingConnection.enqueue(request);
      return request->cseq();
    }

执行到上面的代码,即将request加入到fRequestsAwaitingConnection中,然后就返回。

回到main主程序,  env->taskScheduler().doEventLoop(); 

void BasicTaskScheduler0::doEventLoop(char* watchVariable) {
  // Repeatedly loop, handling readble sockets and timed events:
  while (1) {
    if (watchVariable != NULL && *watchVariable != 0) break;
		SingleStep();
  }
}

那让我们来分析一下这个SingleStep

void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
  fd_set readSet = fReadSet; // make a copy for this select() call
  fd_set writeSet = fWriteSet; // ditto
  fd_set exceptionSet = fExceptionSet; // ditto

//计算select超时的时间
  DelayInterval const& timeToDelay = fDelayQueue.timeToNextAlarm();
  struct timeval tv_timeToDelay;
  tv_timeToDelay.tv_sec = timeToDelay.seconds();
  tv_timeToDelay.tv_usec = timeToDelay.useconds();
  // Very large "tv_sec" values cause select() to fail.
  // Don't make it any larger than 1 million seconds (11.5 days)
  const long MAX_TV_SEC = MILLION;
  if (tv_timeToDelay.tv_sec > MAX_TV_SEC) {
    tv_timeToDelay.tv_sec = MAX_TV_SEC;
  }
  // Also check our "maxDelayTime" parameter (if it's > 0):
  if (maxDelayTime > 0 &&
      (tv_timeToDelay.tv_sec > (long)maxDelayTime/MILLION ||
       (tv_timeToDelay.tv_sec == (long)maxDelayTime/MILLION &&
	tv_timeToDelay.tv_usec > (long)maxDelayTime%MILLION))) {
    tv_timeToDelay.tv_sec = maxDelayTime/MILLION;
    tv_timeToDelay.tv_usec = maxDelayTime%MILLION;
  }

  int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);
  if (selectResult < 0) {
#if defined(__WIN32__) || defined(_WIN32)
    int err = WSAGetLastError();
    // For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if
    // it was called with no entries set in "readSet".  If this happens, ignore it:
    if (err == WSAEINVAL && readSet.fd_count == 0) {
      err = EINTR;
      // To stop this from happening again, create a dummy socket:
      int dummySocketNum = socket(AF_INET, SOCK_DGRAM, 0);
      FD_SET((unsigned)dummySocketNum, &fReadSet);
    }
    if (err != EINTR) {
#else
    if (errno != EINTR && errno != EAGAIN) {
#endif
	// Unexpected error - treat this as fatal:
#if !defined(_WIN32_WCE)
	perror("BasicTaskScheduler::SingleStep(): select() fails");
#endif
	internalError();
      }
  }

  // Call the handler function for one readable socket:
  HandlerIterator iter(*fHandlers);
  HandlerDescriptor* handler;
  // To ensure forward progress through the handlers, begin past the last
  // socket number that we handled:
  if (fLastHandledSocketNum >= 0) {
    while ((handler = iter.next()) != NULL) {
      if (handler->socketNum == fLastHandledSocketNum) break;
    }
    if (handler == NULL) {
      fLastHandledSocketNum = -1;
      iter.reset(); // start from the beginning instead
    }
  }
//查找可执行的handler,执行
  while ((handler = iter.next()) != NULL) {
    int sock = handler->socketNum; // alias
    int resultConditionSet = 0;
    if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
    if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
    if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
    if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
      fLastHandledSocketNum = sock;
          // Note: we set "fLastHandledSocketNum" before calling the handler,
          // in case the handler calls "doEventLoop()" reentrantly.
      (*handler->handlerProc)(handler->clientData, resultConditionSet);
      break;
    }
  }
如果任然没有找到可执行的handler,从头开始找
  if (handler == NULL && fLastHandledSocketNum >= 0) {
    // We didn't call a handler, but we didn't get to check all of them,
    // so try again from the beginning:
    iter.reset();
    while ((handler = iter.next()) != NULL) {
      int sock = handler->socketNum; // alias
      int resultConditionSet = 0;
      if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
      if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
      if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
      if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
	fLastHandledSocketNum = sock;
	    // Note: we set "fLastHandledSocketNum" before calling the handler,
            // in case the handler calls "doEventLoop()" reentrantly.
	(*handler->handlerProc)(handler->clientData, resultConditionSet);
	break;
      }
    }
    if (handler == NULL) fLastHandledSocketNum = -1;//because we didn't call a handler
  }

响应事件
  // Also handle any newly-triggered event (Note that we do this *after* calling a socket handler,
  // in case the triggered event handler modifies The set of readable sockets.)
  if (fTriggersAwaitingHandling != 0) {
    if (fTriggersAwaitingHandling == fLastUsedTriggerMask) {
      // Common-case optimization for a single event trigger:
      fTriggersAwaitingHandling = 0;
      if (fTriggeredEventHandlers[fLastUsedTriggerNum] != NULL) {
	(*fTriggeredEventHandlers[fLastUsedTriggerNum])(fTriggeredEventClientDatas[fLastUsedTriggerNum]);
      }
    } else {
      // Look for an event trigger that needs handling (making sure that we make forward progress through all possible triggers):
      unsigned i = fLastUsedTriggerNum;
      EventTriggerId mask = fLastUsedTriggerMask;

      do {
	i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
	mask >>= 1;
	if (mask == 0) mask = 0x80000000;

	if ((fTriggersAwaitingHandling&mask) != 0) {
	  fTriggersAwaitingHandling &=~ mask;
	  if (fTriggeredEventHandlers[i] != NULL) {
	    (*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);
	  }

	  fLastUsedTriggerMask = mask;
	  fLastUsedTriggerNum = i;
	  break;
	}
      } while (i != fLastUsedTriggerNum);
    }
  }

  // Also handle any delayed event that may have come due.
  fDelayQueue.handleAlarm();
}




你可能感兴趣的:(openRTSP分析1)