Task* CRTConnListener::GetSessionTask(int osSocket, struct sockaddr_in* addr) { TCPSocket* theSocket = NULL; Assert(osSocket != EventContext::kInvalidFileDesc); // when the server is behing a round robin DNS, the client needs to knwo the IP address ot the server // so that it can direct the "POST" half of the connection to the same machine when tunnelling RTSP thru HTTP CRTConnection* theTask = new CRTConnection(); if(NULL == theTask) return NULL; theSocket = theTask->GetSocket(); // out socket is not attached to a unix socket yet. //set options on the socket int sndBufSize = 96L * 1024L; theSocket->Set(osSocket, addr); theSocket->InitNonBlocking(osSocket); //we are a server, always disable nagle algorithm theSocket->NoDelay(); theSocket->KeepAlive(); theSocket->SetSocketBufSize(sndBufSize); //setup the socket. When there is data on the socket, //theTask will get an kReadEvent event theSocket->RequestEvent(EV_RE); theTask->SetTimer(30*1000); StrPtrLen* remoteStr = theSocket->GetRemoteAddrStr(); LI("CRTConnListener Get a connection,ip:%.*s port:%d \n",remoteStr->Len, remoteStr->Ptr, ntohs(addr->sin_port)); this->RunNormal(); return theTask; }
//acceptor模块调用GetSessionTask函数,创建一个连接会话,处理该socket句柄上的所有任务。 Task* ApsAppTcpListenerSocket::GetSessionTask(int osSocket, struct sockaddr_in* addr) { Assert(osSocket != EventContext::kInvalidFileDesc); TCPSocket* theSocket = NULL; //new one web accept session ApsAppSrvSession* theTask = new ApsAppSrvSession(); ApsLogger::Debug("New PushClientSession, socket fd = %d task = %p!",osSocket,theTask); if(NULL == theTask)//no enough memory, TCPListenerSocket will close the socket! { ApsLogger::Debug("PushClientSession create error: no memory!"); return NULL; } theSocket = theTask->GetSocket(); // out socket is not attached to a unix socket yet. //set options on the socket int sndBufSize = 1000L * 1024L; theSocket->Set(osSocket, addr); theSocket->InitNonBlocking(osSocket); theSocket->NoDelay(); theSocket->KeepAlive(); theSocket->SetSocketBufSize(sndBufSize); theSocket->RequestEvent(EV_RE); this->RunNormal(); return theTask; }
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 }
/* 在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 }