#ifndef MULTIPLEX_SOCKET__H_ #define MULTIPLEX_SOCKET__H_ #ifdef _WIN32 #pragma once #pragma warning(disable:4244) #pragma warning(disable:4267) #endif #include class HMpsDispatcher; class HMultiplexSocket : public HSocket { public: enum { MPS_DISABLE = 1, MPS_READY, MPS_CONNECTING, MPS_LISTENING, MPS_CLOSING, MPS_DELETE }; enum { MPS_MAX_SOCKET_BUFFER = 16384 }; HMultiplexSocket(); HMultiplexSocket(SOCKET sock, struct sockaddr_in * psaiRemote, HMpsDispatcher * pDispatcher); ~HMultiplexSocket(); inline int GetState() { return m_nState; } inline void SetState(int nState) { m_nState = nState; } inline unsigned int GetLastReceivedTime() { return m_nLastReceivedTime; } inline bool IsSendRetry() { return m_bSendRetry; } HMpsDispatcher * GetDispatcher() { return m_pDispatcher; } void SetDispatcher(HMpsDispatcher * pDispatcher) { m_pDispatcher = pDispatcher; } virtual bool Close(); virtual bool Connect(unsigned nConnectTime = 60000); // msec bool IsConnectTimeout(DWORD nCurrTime); bool Bind(struct sockaddr_in * psaiLocal); bool Bind(const char * pszAddress, u_short nPort); struct sockaddr_in * GetRemoteAddress() { return &m_saiRemote; } bool GetRemoteAddress(char * pBuf, u_short * pnPort); bool SetRemoteAddress(struct sockaddr_in * psaiLocal); bool SetRemoteAddress(const char * pszAddress, u_short nPort); bool SetRemoteAddressHostByName(const char * pszHostName, u_short nPort); virtual int Send(const char * pData, int nSize) = 0; virtual int Receive(char * pBuffer, int nSize) = 0; virtual void FinishTcp() {} protected: virtual void OnClose() {} virtual void OnConnect(bool bIsSuccess); // only called for client socket virtual void OnReceive(); virtual int OnSendRetry(); virtual void OnAccepted() {} // only called for child socket public: virtual HMultiplexSocket * OnAccept(SOCKET /* sock */, struct sockaddr_in * /* psaiRemote */) { return NULL; } protected: int m_nState; bool m_bSendRetry; unsigned int m_nLastReceivedTime; struct sockaddr_in m_saiRemote; HMpsDispatcher * m_pDispatcher; private: unsigned long m_nConnectTime; unsigned long m_nLastTryConnectTime; friend class HMpsDispatcher; template friend class HMultiplexListenThread; }; class HMultiplexTcpSocket : public HMultiplexSocket { public: HMultiplexTcpSocket() {} HMultiplexTcpSocket(SOCKET sock, struct sockaddr_in * psaiRemote, HMpsDispatcher * pDispatcher) : HMultiplexSocket(sock, psaiRemote, pDispatcher) {} ~HMultiplexTcpSocket() {} bool Create(bool bIsTcpNoDelay = false); virtual bool IsSslSocket() { return false; } virtual int Send(const char * pData, int nSize); virtual int Receive(char * pBuffer, int nSize); virtual void FinishTcp(); // remain send and shutdown }; class HMultiplexUdpSocket : public HMultiplexSocket { public: HMultiplexUdpSocket() {} ~HMultiplexUdpSocket() {} virtual bool Create(); virtual bool SetRemoteAddress(struct sockaddr_in * psaiLocal); virtual bool SetRemoteAddress(const char * pszAddress, u_short nPort); virtual bool Bind(struct sockaddr_in * psaiLocal); virtual inline int Send(const char * pData, int nSize) { return sendto(m_fdSocket, pData, nSize, 0, (struct sockaddr *)&m_saiRemote, sizeof(struct sockaddr)); } virtual inline int Receive(char * pBuffer, int nSize) { return recvfrom(m_fdSocket, pBuffer, nSize, 0, NULL, 0); } virtual inline int Receive(char * pBuffer, int nSize, struct sockaddr_in * psaiUDPFrom) { socklen_t len = sizeof(struct sockaddr); return recvfrom(m_fdSocket, pBuffer, nSize, 0, (struct sockaddr *)psaiUDPFrom, &len); } }; class HMultiplexListenSocket : public HMultiplexTcpSocket { public: HMultiplexListenSocket() {} ~HMultiplexListenSocket() {} bool Listen(const char * pszAddress, u_short nPort, int nBackLog = 10) { if (Create() == false) return false; #ifdef REUSEADDR int nOptVal = 1; SetSockOpt(SOL_SOCKET, SO_REUSEADDR, (const char *)&nOptVal, sizeof(nOptVal)); // for multiple ip in UNIX #endif if (Bind(pszAddress, nPort) == false) { HLOGF(HLOG_ERROR, "[ERROR], MpLstnSock, Failed to bind, (%s:%d), error = %d\n", pszAddress, nPort, SockErrorNo()); return false; } if (listen(m_fdSocket, nBackLog) == SOCKET_ERROR) { HLOGF(HLOG_ERROR, "[ERROR], MpLstnSock, Failed to listen, (%s:%d), error = %d\n", pszAddress, nPort, SockErrorNo()); return false; } m_nState = MPS_LISTENING; HLOGF(HLOG_INFO1, "INFO, MpLstnSock, Succeed to listen TCP, (%s:%d), socket = %d\n", pszAddress, nPort, m_fdSocket); return true; } private: virtual HMultiplexTcpSocket * OnAccept(SOCKET sock, struct sockaddr_in * psaiRemote) = 0; }; typedef std::list LIST_MULTIPLEX_SOCKET; class HMpsDispatcher { public: HMpsDispatcher(); virtual ~HMpsDispatcher(); void Initialize(unsigned long nLoopInterval) { m_nLoopInterval = nLoopInterval; } // msec, for connect timeout void SetTerminateFlag() { m_bTerminate = true; } LIST_MULTIPLEX_SOCKET * GetSocketList() { return &m_listSocket; } bool Attach(HMultiplexSocket * pSocket); bool AttachWithLock(HMultiplexSocket * pSocket); bool Detach(HMultiplexSocket * pSocket); void RemoveAllSocket(); #ifndef SINGLE_THREADED_MODEL inline bool IsProcessing() { return m_bProecessing; } #endif bool Dispatch(); //private: public: LIST_MULTIPLEX_SOCKET m_listSocket; unsigned long m_nLoopInterval; volatile bool m_bTerminate; public: //...TempCode #ifndef SINGLE_THREADED_MODEL HSyncObject m_syncObject; volatile bool m_bProecessing; #endif }; class HMpsDispatcherList : public std::list { public: HMpsDispatcherList() { m_nLazyIndex = 0; } ~HMpsDispatcherList() {} HMpsDispatcher * GetLazyDispatcher() { iterator iter; int i = 0; for (iter = begin(); iter != end(); iter++) { if ((*iter)->IsProcessing() == false) i++; } //HLOGF(HLOG_WARNING, "[TRACE], DispatcherList_0x%x, lazy count = %d\n", this, i); i = 0; for (iter = begin(); iter != end(); iter++) { if (i == m_nLazyIndex) { m_nLazyIndex++; if (m_nLazyIndex >= (int)size()) m_nLazyIndex = 0; return *iter; } i++; } return NULL; } public: volatile int m_nLazyIndex; }; /////////////////////////////////////// class HMultiplexSocketDispatchThread : public HThread { public: HMultiplexSocketDispatchThread() : HThread(true, 1024 * 1024 * 4) // stack size : 4MB { m_pMpsDispatcher = new HMpsDispatcher; } ~HMultiplexSocketDispatchThread() { delete m_pMpsDispatcher; } inline HMpsDispatcher * GetDispatcher() { return m_pMpsDispatcher; } inline void DispatchLock() { m_pMpsDispatcher->m_syncObject.Lock(); } inline void DispatchUnlock() { m_pMpsDispatcher->m_syncObject.Unlock(); } inline unsigned long BeginDispatch(unsigned long nLoopInterval = 50) { m_pMpsDispatcher->Initialize(nLoopInterval); return HThread::Begin(); } void Terminate() { m_pMpsDispatcher->SetTerminateFlag(); } protected: void Main() { m_pMpsDispatcher->Dispatch(); } private: inline unsigned long Begin() { return 0; } // do not use private: HMpsDispatcher * m_pMpsDispatcher; }; /////////////////////////////////////// template class HMultiplexListenThread : public HThread { public: HMultiplexListenThread() : HThread(true, 0) { m_sockListen = INVALID_SOCKET; m_bTerminate = false; } ~HMultiplexListenThread() {} inline void SetDispatcherList(HMpsDispatcherList * pList) { m_pListDispatcher = pList; } bool Listen(const char * pszAddress, u_short nPort, int nBackLog = 10) { if (m_sockListen != INVALID_SOCKET) { HLOGF(HLOG_ERROR, "[ERROR], MpLstnThr, This socket aleady created. socket = %d\n", m_sockListen); return false; } if ((m_sockListen = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { HLOGF(HLOG_ERROR, "[ERROR], MpLstnThr, Don't create socket. Error = %d\n", SockErrorNo()); return false; } // Set non blocking mode #ifdef _WIN32 ULONG nNonBlock = 1; if (ioctlsocket(m_sockListen, FIONBIO, &nNonBlock) == SOCKET_ERROR) { m_sockListen = INVALID_SOCKET; HLOGF(HLOG_ERROR, "[ERROR], MpLstnThrd, Failed to ioctlsocket, socket = %d, error = %d\n", m_sockListen, SockErrorNo()); return false; } #else int nFlags = fcntl(m_sockListen, F_GETFL, 0); fcntl(m_sockListen, F_SETFL, nFlags | O_NONBLOCK); #endif struct sockaddr_in sai; memset(&sai, 0, sizeof(struct sockaddr_in)); if (pszAddress == NULL || pszAddress[0] == '\0') sai.sin_addr.s_addr = htonl(INADDR_ANY); else sai.sin_addr.s_addr = inet_addr(pszAddress); sai.sin_family = AF_INET; sai.sin_port = htons(nPort); #ifdef REUSEADDR int nOptVal = 1; setsockopt(m_sockListen, SOL_SOCKET, SO_REUSEADDR, (const char *)&nOptVal, sizeof(nOptVal)); // for multiple ip in UNIX #endif if (bind(m_sockListen, (struct sockaddr *)&sai, sizeof(struct sockaddr)) < 0) { HLOGF(HLOG_ERROR, "[ERROR], MpLstnThrd, Failed to bind, (%s:%d), socket = %d, error = %d\n", pszAddress, nPort, m_sockListen, SockErrorNo()); return false; } if (listen(m_sockListen, nBackLog) == SOCKET_ERROR) { HLOGF(HLOG_ERROR, "[ERROR], MpLstnThrd, Failed to listen, (%s:%d), socket = %d, error = %d\n", pszAddress, nPort, m_sockListen, SockErrorNo()); return false; } HLOGF(HLOG_INFO1, "INFO, MpLstnThrd, Succeed to listen TCP, (%s:%d), socket = %d\n", pszAddress, nPort, m_sockListen); m_strListenIp = pszAddress; m_nListenPort = nPort; Begin(); return true; } void Terminate() { closesocket(m_sockListen); for (HMpsDispatcherList::iterator iter = m_pListDispatcher->begin(); iter != m_pListDispatcher->end(); iter++) { (*iter)->SetTerminateFlag(); // auto delete } m_bTerminate = true; } protected: void Main() { HLOGF(HLOG_INFO5, "INFO, MpLstnThrd, listen thread begins, (%s:%d)\n", m_strListenIp.psz(), m_nListenPort); fd_set fdSetRead;//, fdSetError; int nSelectResult; SOCKET sockClient = INVALID_SOCKET; socklen_t len = sizeof(struct sockaddr); struct sockaddr_in sai; FD_ZERO(&fdSetRead); //FD_ZERO(&fdSetError); while (m_bTerminate == false) { FD_SET(m_sockListen, &fdSetRead); //FD_SET(m_sockListen, &fdSetError); if ((nSelectResult = select(m_sockListen + 1, &fdSetRead, NULL, NULL, NULL)) == SOCKET_ERROR) { Sleep(10); continue; } if ((sockClient = accept(m_sockListen, (struct sockaddr *)&sai, &len)) == INVALID_SOCKET) { HLOGF(HLOG_WARNING, "[WARN], MpLstnThrd, Failed to accept, (%s:%d), socket = %d, error = %d\n", m_strListenIp.psz(), m_nListenPort, m_sockListen, SockErrorNo()); Sleep(50); } else { HLOGF(HLOG_INFO4, "INFO, MpLstnThrd, Succeeded to accept, (%s:%d), (%s:%d), socket = %d\n", m_strListenIp.psz(), m_nListenPort, inet_ntoa(sai.sin_addr), ntohs(sai.sin_port), sockClient); // Set non blocking mode #ifdef _WIN32 ULONG nNonBlock = 1; if (ioctlsocket(sockClient, FIONBIO, &nNonBlock) == SOCKET_ERROR) HLOGF(HLOG_ERROR, "[ERROR], MpLstnThrd, Failed to ioctlsocket, socket = %d, error = %d\n", sockClient, SockErrorNo()); #else int nFlags = fcntl(sockClient, F_GETFL, 0); fcntl(sockClient, F_SETFL, nFlags | O_NONBLOCK); #endif HMultiplexTcpSocket * pAcceptedSocket = OnAccept(sockClient, &sai); if (pAcceptedSocket) { pAcceptedSocket->m_nLastReceivedTime = HTIMER()->GetTickCount(); pAcceptedSocket->OnAccepted(); } else closesocket(sockClient); } } } private: virtual HMultiplexTcpSocket * OnAccept(SOCKET sock, struct sockaddr_in * psaiRemote) { #if 1 HMpsDispatcher * pDispatcher = m_pListDispatcher->GetLazyDispatcher(); HMultiplexTcpSocket * pMpSocket = new _Ty(sock, psaiRemote, pDispatcher); pDispatcher->Attach(pMpSocket); return pMpSocket; #else HMpsDispatcherList::iterator iter; int i = 0; for (iter = m_pListDispatcher->begin(); iter != m_pListDispatcher->end(); iter++) { if ((*iter)->IsProcessing() == false) i++; } //HLOGF(HLOG_WARNING, "[TRACE], MplxLstnThr_0x%x, lazy dispatcher count = %d\n", this, i); for (;;) { for (iter = m_pListDispatcher->begin(); iter != m_pListDispatcher->end(); iter++) { if ((*iter)->IsProcessing() == false) { HMultiplexTcpSocket * pMpSocket = new _Ty(sock, psaiRemote, *iter); (*iter)->Attach(pMpSocket); return pMpSocket; } } Sleep(10); } #endif } protected: HMpsDispatcherList * m_pListDispatcher; SOCKET m_sockListen; HString m_strListenIp; u_short m_nListenPort; volatile bool m_bTerminate; }; #endif // MULTIPLEX_SOCKET__H_