void Socket::SetSocketBufSize(UInt32 inNewSize) { #if SOCKET_DEBUG int value; int buffSize = sizeof(value); int error = ::getsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (void*)&value, (socklen_t*)&buffSize); #endif int bufSize = inNewSize; int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (char*)&bufSize, sizeof(int)); AssertV(err == 0, OSThread::GetErrno()); #if SOCKET_DEBUG int setValue; error = ::getsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (void*)&setValue, (socklen_t*)&buffSize); qtss_printf("Socket::SetSocketBufSize "); if (fState & kBound) { if (NULL != this->GetLocalAddrStr()) this->GetLocalAddrStr()->PrintStr(":"); if (NULL != this->GetLocalPortStr()) this->GetLocalPortStr()->PrintStr(" "); } else qtss_printf("unbound "); qtss_printf("socket=%d old SO_SNDBUF =%d inNewSize=%d setValue=%d\n", (int) fFileDesc, value, bufSize, setValue); #endif }
OS_Error TCPListenerSocket::Initialize(UInt32 addr, UInt16 port) { OS_Error err = this->TCPSocket::Open(); if (0 == err) do { // set SO_REUSEADDR socket option before calling bind. #ifndef __Win32__ // this causes problems on NT (multiple processes can bind simultaneously), // so don't do it on NT. this->ReuseAddr(); #endif err = this->Bind(addr, port); if (err != 0) break; // don't assert this is just a port already in use. // // Unfortunately we need to advertise a big buffer because our TCP sockets // can be used for incoming broadcast data. This could force the server // to run out of memory faster if it gets bogged down, but it is unavoidable. this->SetSocketRcvBufSize(96 * 1024); err = this->Listen(kListenQueueLength); AssertV(err == 0, OSThread::GetErrno()); if (err != 0) break; } while (false); return err; }
/* used in QTSServer::CreateListeners() */ OS_Error TCPListenerSocket::Initialize(UInt32 addr, UInt16 port) { /* 创建Socket并设置为非阻塞模式 */ OS_Error err = this->TCPSocket::Open(); if (0 == err) do { // set SO_REUSEADDR socket option before calling bind. // this causes problems on NT (multiple processes can bind simultaneously), // so don't do it on NT. this->ReuseAddr(); /* 将入参和新建的Socket绑定 */ err = this->Bind(addr, port); if (err != 0) break; // don't assert this is just a port already in use. // // Unfortunately we need to advertise a big buffer because our TCP sockets // can be used for incoming broadcast data. This could force the server // to run out of memory faster(更快地使内存溢出) if it gets bogged down(陷入困境, 停顿), but it is unavoidable. /* 设置Socket接受buffer大小(我们需要大的缓存以多播数据) */ this->SetSocketRcvBufSize(96 * 1024); /* 设置侦听的最大等待连接的队列长度为128 */ err = this->Listen(kListenQueueLength); AssertV(err == 0, OSThread::GetErrno()); if (err != 0) break; } while (false); return err; }
/* used in TCPListenerSocket::ProcessEvent(),那里第一个入参是服务器接受客户端连接的服务器端的Socket,客户端的ip地址是第二个参数,所以这两个参数联系紧密 */ void TCPSocket::Set(int inSocket, struct sockaddr_in* remoteaddr) { /* 从入参设置本地和远端的Socket */ fRemoteAddr = *remoteaddr; fFileDesc = inSocket; /* 当服务器接受的客户端的Socket不是初始状态时,一定要成功获取它在服务器端对应的Socket的信息(包括相应的IP地址) */ if ( inSocket != EventContext::kInvalidFileDesc ) { //make sure to find out what IP address this connection is actually occuring on. That //way, we can report correct information to clients asking what the connection's IP is socklen_t len = sizeof(fLocalAddr); /* 获取客户端的Socket的在服务器本地上的Socket信息,详见<<Windows网络编程第二版>>. 这里fRemoteAddr和fLocalAddr是已接受的该客户端已经连接的对应的IP地址 */ int err = ::getsockname(fFileDesc, (struct sockaddr*)&fLocalAddr, &len); /* 确保能成功获取本地Socket信息 */ AssertV(err == 0, OSThread::GetErrno()); /* 确保该Socket已绑定ip和连接 */ fState |= kBound; fState |= kConnected; } else /* 若服务器接受的客户端的Socket是初始状态时,它的状态只能是0 */ fState = 0; }
OS_Error UDPSocket::SetMulticastInterface(UInt32 inLocalAddr) { // set the outgoing interface for multicast datagrams on this socket in_addr theLocalAddr; theLocalAddr.s_addr = inLocalAddr; int err = setsockopt(fFileDesc, IPPROTO_IP, IP_MULTICAST_IF, (char*)&theLocalAddr, sizeof(theLocalAddr)); AssertV(err == 0, OSThread::GetErrno()); if (err == -1) return (OS_Error)OSThread::GetErrno(); else return OS_NoErr; }
int OSEventThread::SetFileDescNonBlocking(int inFileDesc) { #ifdef __Win32__ u_long one = 1; int err = ::ioctlsocket(inFileDesc, FIONBIO, &one); #else int flag = ::fcntl(inFileDesc, F_GETFL, 0); int err = ::fcntl(inFileDesc, F_SETFL, flag | O_NONBLOCK); #endif AssertV(err == 0, OSThread::GetErrno()); return err; }
void EventContext::InitNonBlocking(int inFileDesc) { fFileDesc = inFileDesc; #ifdef __Win32__ u_long one = 1; int err = ::ioctlsocket(fFileDesc, FIONBIO, &one); #else int flag = ::fcntl(fFileDesc, F_GETFL, 0); int err = ::fcntl(fFileDesc, F_SETFL, flag | O_NONBLOCK); #endif AssertV(err == 0, OSThread::GetErrno()); }
void TCPClientSocket::SetOptions(int sndBufSize, int rcvBufSize) { //set options on the socket //qtss_printf("TCPClientSocket::SetOptions sndBufSize=%d,rcvBuf=%d,keepAlive=%d,noDelay=%d\n",sndBufSize,rcvBufSize,(int)keepAlive,(int)noDelay); int err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_SNDBUF, (char*)&sndBufSize, sizeof(int)); AssertV(err == 0, OSThread::GetErrno()); err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_RCVBUF, (char*)&rcvBufSize, sizeof(int)); AssertV(err == 0, OSThread::GetErrno()); #if __FreeBSD__ || __MacOSX__ struct timeval time; //int len = sizeof(time); time.tv_sec = 0; time.tv_usec = 0; err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_RCVTIMEO, (char*)&time, sizeof(time)); AssertV(err == 0, OSThread::GetErrno()); err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_SNDTIMEO, (char*)&time, sizeof(time)); AssertV(err == 0, OSThread::GetErrno()); #endif }
QTSS_Error CreateRTPFileSession(QTSS_StandardRTSP_Params* inParamBlock, const StrPtrLen& inPath, FileSession** outFile) { *outFile = NEW FileSession(); StrPtrLen thePath(inPath); RTPFileSession::ErrorCode theErr = (*outFile)->fFile.Initialize(thePath, 8); if (theErr != RTPFileSession::errNoError) { delete *outFile; *outFile = NULL; if (theErr == RTPFileSession::errFileNotFound) return QTSSModuleUtils::SendErrorResponse( inParamBlock->inRTSPRequest, qtssClientNotFound, sBadQTFileErr); AssertV(0, theErr); } return QTSS_NoErr; }
void EventContext::Cleanup() { int err = 0; if (fFileDesc != kInvalidFileDesc) { //if this object is registered in the table, unregister it now if (fUniqueID > 0) { fEventThread->fRefTable.UnRegister(&fRef); #if !MACOSXEVENTQUEUE select_removeevent(fFileDesc);//The eventqueue / select shim requires this #ifdef __Win32__ err = ::closesocket(fFileDesc); #endif #else //On Linux (possibly other UNIX implementations) you MUST NOT close the fd before //removing the fd from the select mask, and having the select function wake up //to register this fact. If you close the fd first, bad things may happen, like //the socket not getting unbound from the port & IP addr. // //So, what we do is have the select thread itself call close. This is triggered //by calling removeevent. err = ::close(fFileDesc); #endif } else #ifdef __Win32__ err = ::closesocket(fFileDesc); #else err = ::close(fFileDesc); #endif } fFileDesc = kInvalidFileDesc; fUniqueID = 0; fWatchEventCalled = false; AssertV(err == 0, OSThread::GetErrno());//we don't really care if there was an error, but it's nice to know }
void TCPSocket::Set(int inSocket, struct sockaddr_in* remoteaddr) { fRemoteAddr = *remoteaddr; fFileDesc = inSocket; if ( inSocket != EventContext::kInvalidFileDesc ) { //make sure to find out what IP address this connection is actually occuring on. That //way, we can report correct information to clients asking what the connection's IP is #if __Win32__ || __osf__ || __sgi__ || __hpux__ int len = sizeof(fLocalAddr); #else socklen_t len = sizeof(fLocalAddr); #endif int err = ::getsockname(fFileDesc, (struct sockaddr*)&fLocalAddr, &len); AssertV(err == 0, OSThread::GetErrno()); fState |= kBound; fState |= kConnected; } else fState = 0; }
void TCPListenerSocket::ProcessEvent(int /*eventBits*/) { //we are executing on the same thread as every other //socket, so whatever you do here has to be fast. struct sockaddr_in addr; #if __Win32__ || __osf__ || __sgi__ || __hpux__ int size = sizeof(addr); #else socklen_t size = sizeof(addr); #endif Task* theTask = NULL; TCPSocket* theSocket = NULL; //fSocket data member of TCPSocket. int osSocket = accept(fFileDesc, (struct sockaddr*)&addr, &size); //test osSocket = -1; if (osSocket == -1) { //take a look at what this error is. int acceptError = OSThread::GetErrno(); if (acceptError == EAGAIN) { //If it's EAGAIN, there's nothing on the listen queue right now, //so modwatch and return this->RequestEvent(EV_RE); return; } //test acceptError = ENFILE; //test acceptError = EINTR; //test acceptError = ENOENT; //if these error gets returned, we're out of file desciptors, //the server is going to be failing on sockets, logs, qtgroups and qtuser auth file accesses and movie files. The server is not functional. if (acceptError == EMFILE || acceptError == ENFILE) { #ifndef __Win32__ QTSSModuleUtils::LogErrorStr(qtssFatalVerbosity, "Out of File Descriptors. Set max connections lower and check for competing usage from other processes. Exiting."); #endif exit (EXIT_FAILURE); } else { char errStr[256]; errStr[sizeof(errStr) -1] = 0; qtss_snprintf(errStr, sizeof(errStr) -1, "accept error = %d '%s' on socket. Clean up and continue.", acceptError, strerror(acceptError)); WarnV( (acceptError == 0), errStr); theTask = this->GetSessionTask(&theSocket); if (theTask == NULL) { close(osSocket); } else { theTask->Signal(Task::kKillEvent); // just clean up the task } if (theSocket) theSocket->fState &= ~kConnected; // turn off connected state return; } } theTask = this->GetSessionTask(&theSocket); if (theTask == NULL) { //this should be a disconnect. do an ioctl call? close(osSocket); if (theSocket) theSocket->fState &= ~kConnected; // turn off connected state } else { Assert(osSocket != EventContext::kInvalidFileDesc); //set options on the socket //we are a server, always disable nagle algorithm int one = 1; int err = ::setsockopt(osSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(int)); AssertV(err == 0, OSThread::GetErrno()); err = ::setsockopt(osSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(int)); AssertV(err == 0, OSThread::GetErrno()); int sndBufSize = 96L * 1024L; err = ::setsockopt(osSocket, SOL_SOCKET, SO_SNDBUF, (char*)&sndBufSize, sizeof(int)); AssertV(err == 0, OSThread::GetErrno()); //setup the socket. When there is data on the socket, //theTask will get an kReadEvent event theSocket->Set(osSocket, &addr); theSocket->InitNonBlocking(osSocket); theSocket->SetTask(theTask); theSocket->RequestEvent(EV_RE); theTask->SetThreadPicker(Task::GetBlockingTaskThreadPicker()); //The RTSP Task processing threads } if (fSleepBetweenAccepts) { // We are at our maximum supported sockets // slow down so we have time to process the active ones (we will respond with errors or service). // wake up and execute again after sleeping. The timer must be reset each time through //qtss_printf("TCPListenerSocket slowing down\n"); this->SetIdleTimer(kTimeBetweenAcceptsInMsec); //sleep 1 second } else { // sleep until there is a read event outstanding (another client wants to connect) //qtss_printf("TCPListenerSocket normal speed\n"); this->RequestEvent(EV_RE); } fOutOfDescriptors = false; // always false for now we don't properly handle this elsewhere in the code }
void Socket::SetSocketBufSize(UInt32 inNewSize) { int bufSize = inNewSize; int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (char*)&bufSize, sizeof(int)); AssertV(err == 0, OSThread::GetErrno()); }
void EventThread::Entry() { struct eventreq theCurrentEvent; ::memset( &theCurrentEvent, '\0', sizeof(theCurrentEvent) ); while (!IsStopRequested()) { int theErrno = EINTR; while (theErrno == EINTR) { #if MACOSXEVENTQUEUE int theReturnValue = waitevent(&theCurrentEvent, NULL); #else int theReturnValue = select_waitevent(&theCurrentEvent, NULL); #endif //Sort of a hack. In the POSIX version of the server, waitevent can return //an actual POSIX errorcode. if (theReturnValue >= 0) theErrno = theReturnValue; else theErrno = OSThread::GetErrno(); if(IsStopRequested()) break; } if (IsStopRequested()) break; AssertV(theErrno == 0, theErrno); //ok, there's data waiting on this socket. Send a wakeup. if (theCurrentEvent.er_data != NULL) { //The cookie in this event is an ObjectID. Resolve that objectID into //a pointer. StrPtrLen idStr((char*)&theCurrentEvent.er_data, sizeof(theCurrentEvent.er_data)); OSRef* ref = fRefTable.Resolve(&idStr); if (ref != NULL) { EventContext* theContext = (EventContext*)ref->GetObject(); #if DEBUG theContext->fModwatched = false; #endif theContext->ProcessEvent(theCurrentEvent.er_eventbits); fRefTable.Release(ref); } } #if EVENT_CONTEXT_DEBUG SInt64 yieldStart = OS::Milliseconds(); #endif this->ThreadYield(); #if EVENT_CONTEXT_DEBUG SInt64 yieldDur = OS::Milliseconds() - yieldStart; static SInt64 numZeroYields; if ( yieldDur > 1 ) { qtss_printf( "EventThread time in OSTHread::Yield %i, numZeroYields %i\n", (SInt32)yieldDur, (SInt32)numZeroYields ); numZeroYields = 0; } else numZeroYields++; #endif } }
void EventContext::RequestEvent(int theMask) { #if DEBUG fModwatched = true; #endif // // The first time this function gets called, we're supposed to // call watchevent. Each subsequent time, call modwatch. That's // the way the MacOS X event queue works. if (fWatchEventCalled) { fEventReq.er_eventbits = theMask; #if MACOSXEVENTQUEUE if (modwatch(&fEventReq, theMask) != 0) #else if (select_modwatch(&fEventReq, theMask) != 0) #endif AssertV(false, OSThread::GetErrno()); } else { //allocate a Unique ID for this socket, and add it to the ref table #ifdef __Win32__ // // Kind of a hack. On Win32, the way that we pass around the unique ID is // by making it the message ID of our Win32 message (see win32ev.cpp). // Messages must be >= WM_USER. Hence this code to restrict the numberspace // of our UniqueIDs. if (!compare_and_store(8192, WM_USER, &sUniqueID)) // Fix 2466667: message IDs above a fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1); // level are ignored, so wrap at 8192 else fUniqueID = (PointerSizedInt)WM_USER; #else if (!compare_and_store(10000000, 1, &sUniqueID)) fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1); else fUniqueID = 1; #endif fRef.Set(fUniqueIDStr, this); Assert(fEventThread->fRefTable.Register(&fRef) == OS_NoErr); //fill out the eventreq data structure ::memset( &fEventReq, '\0', sizeof(fEventReq)); fEventReq.er_type = EV_FD; fEventReq.er_handle = fFileDesc; fEventReq.er_eventbits = theMask; fEventReq.er_data = (void*)fUniqueID; fWatchEventCalled = true; #if MACOSXEVENTQUEUE if (watchevent(&fEventReq, theMask) != 0) #else if (select_watchevent(&fEventReq, theMask) != 0) #endif //this should never fail, but if it does, cleanup. AssertV(false, OSThread::GetErrno()); } }
/* 在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 }