// QQTX.cpp: implementation of the CQQTX class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "QQTX.h" /* #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif */ ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// #include #include #include "..\..\..\public\md53\\md5_L.h" CQQTX::CQQTX() { m_hSocket = INVALID_SOCKET; memset( m_szIP , 0 , sizeof(m_szIP) ); m_lPort = 0; m_bQuitThread = false; m_dwEndSendTime = 0; m_dwEndRecvTime = 0; m_dwTestCount = 0; m_bRecvFrame = false; m_lID = 1; m_pRecvProc = NULL; m_bConnect = false; m_bLoginQQTX= -1; m_bInitQQTX = false; memset(m_szQQTXIP ,0,sizeof(m_szQQTXIP)); memset(m_szQQTXUser ,0,sizeof(m_szQQTXUser)); memset(m_szQQTXPasswd,0,sizeof(m_szQQTXPasswd)); memset(m_szQQTXKey ,0,sizeof(m_szQQTXKey)); m_lConnectType = 2; //默认是接收与发送类型 WSADATA wsaData; //用于初始化socket的结构 //以下为初始化SOCKET环境 int iTemp=WSAStartup(0x0101,&wsaData); //初始化socket环境 InitializeCriticalSection(&m_CriDeliverList); InitializeCriticalSection(&m_CriErrMsg); InitializeCriticalSection(&m_CriSendFrame); //定义队列 m_lSendID = 1; m_pDeliverList = new QQTX_Deliver_List[QQTX_MAX_DELIVER]; m_lDeliverList=0; for ( int i=0 ; iStop(); //重新连接,先让原来的线程停止 strcpy( m_szIP,pAddr ); m_lPort = lPort; sockaddr_in sAddr; m_hSocket=socket(AF_INET,SOCK_STREAM,0); sAddr.sin_family=AF_INET; sAddr.sin_port=0; sAddr.sin_addr.s_addr=htonl(INADDR_ANY); if (bind(m_hSocket,(LPSOCKADDR)&sAddr,sizeof(sAddr))==SOCKET_ERROR) { return false; } sAddr.sin_port=htons((unsigned short)lPort); sAddr.sin_addr.s_addr=inet_addr(pAddr); if (sAddr.sin_addr.s_addr == INADDR_NONE) { LPHOSTENT lphost; lphost = gethostbyname(pAddr); if (lphost != NULL) sAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr; else { Close(); return false; } } if (connect(m_hSocket,(LPSOCKADDR)&sAddr,sizeof(sAddr))==SOCKET_ERROR) { Close(); return false; } //设置Socket u_long ulTemp=1; //将SOCKET设成非阻塞式的SOCKET //u_long ulTemp=0; //将SOCKET设成阻塞式的SOCKET ioctlsocket( m_hSocket,FIONBIO,&ulTemp ); ulTemp=1024000; setsockopt( m_hSocket , SOL_SOCKET, SO_SNDBUF ,(const char *)&ulTemp ,sizeof(ulTemp)); //设绶冲 ulTemp=1024000; setsockopt( m_hSocket , SOL_SOCKET, SO_RCVBUF ,(const char *)&ulTemp ,sizeof(ulTemp)); //设绶冲 m_bQuitThread = false; DWORD dwTemp; HANDLE m_hThread=CreateThread(NULL,0,SocketRecv,(LPVOID)this,0,&dwTemp); // if ( !::AfxBeginThread((AFX_THREADPROC)CNetSocket::SocketRecv,(LPVOID)this,THREAD_PRIORITY_LOWEST) ) if ( m_hThread == INVALID_HANDLE_VALUE ) { Close(); return false; } HANDLE m_hThread_Send=CreateThread(NULL,0,SocketSend,(LPVOID)this,0,&dwTemp); // if ( !::AfxBeginThread((AFX_THREADPROC)CNetSocket::SocketRecv,(LPVOID)this,THREAD_PRIORITY_LOWEST) ) if ( m_hThread_Send == INVALID_HANDLE_VALUE ) { Close(); return false; } //SetThreadPriority(m_hThread,THREAD_PRIORITY_LOWEST); //设置优先级 m_bConnect = true; return true; } BOOL CQQTX::Connect2() { sockaddr_in sAddr; m_hSocket=socket(AF_INET,SOCK_STREAM,0); sAddr.sin_family=AF_INET; sAddr.sin_port=0; sAddr.sin_addr.s_addr=htonl(INADDR_ANY); if (bind(m_hSocket,(LPSOCKADDR)&sAddr,sizeof(sAddr))==SOCKET_ERROR) { return false; } sAddr.sin_port=htons((unsigned short)m_lPort); sAddr.sin_addr.s_addr=inet_addr(m_szIP); if (sAddr.sin_addr.s_addr == INADDR_NONE) { LPHOSTENT lphost; lphost = gethostbyname(m_szIP); if (lphost != NULL) sAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr; else { Close(); return false; } } if (connect(m_hSocket,(LPSOCKADDR)&sAddr,sizeof(sAddr))==SOCKET_ERROR) { Close(); return false; } //设置Socket u_long ulTemp=1; //将SOCKET设成非阻塞式的SOCKET //u_long ulTemp=0; //将SOCKET设成阻塞式的SOCKET ioctlsocket( m_hSocket,FIONBIO,&ulTemp ); ulTemp=1024000; setsockopt( m_hSocket , SOL_SOCKET, SO_SNDBUF ,(const char *)&ulTemp ,sizeof(ulTemp)); //设绶冲 ulTemp=1024000; setsockopt( m_hSocket , SOL_SOCKET, SO_RCVBUF ,(const char *)&ulTemp ,sizeof(ulTemp)); //设绶冲 return true; } BOOL CQQTX::isConnect() { return m_bConnect; } void CQQTX::Stop() { m_bQuitThread = true; if (m_hThread != INVALID_HANDLE_VALUE && WaitForSingleObject(m_hThread,2000) == WAIT_TIMEOUT ) //等待5秒,让线程退出 { TerminateThread(m_hThread,0); //如果退出超时,强制结束线程 } m_hThread = INVALID_HANDLE_VALUE; if (m_hThread_Send != INVALID_HANDLE_VALUE && WaitForSingleObject(m_hThread_Send,2000) == WAIT_TIMEOUT ) //等待5秒,让线程退出 { TerminateThread(m_hThread_Send,0); //如果退出超时,强制结束线程 } m_hThread_Send = INVALID_HANDLE_VALUE; Close(); } BOOL CQQTX::isSocketClose(SOCKET s) { BOOL bConnDropped = FALSE; INT iRet = 0; BOOL bOK = TRUE; if (s == INVALID_SOCKET) return TRUE; struct timeval timeout = { 0, 0 }; fd_set readSocketSet; FD_ZERO(&readSocketSet); FD_SET(s, &readSocketSet); iRet = ::select(0, &readSocketSet, NULL, NULL, &timeout); bOK = (iRet > 0); if(bOK) { bOK = FD_ISSET(s, &readSocketSet); } if(bOK) { CHAR szBuffer[1] = ""; iRet = recv(s, szBuffer, 1, MSG_PEEK); bOK = (iRet > 0); if(!bOK) { INT iError = WSAGetLastError(); bConnDropped = (( iError == WSAENETRESET) || (iError == WSAECONNABORTED) || (iError == WSAECONNRESET) || (iError == WSAEINVAL) || (iRet == 0)); } } return(bConnDropped); } void CQQTX::Close() { if ( m_hSocket == INVALID_SOCKET ) return; if ( m_hSocket != INVALID_SOCKET ) closesocket(m_hSocket); m_hSocket = INVALID_SOCKET; m_bConnect = false; //m_dwEndSendTime=0; //m_dwEndRecvTime=0; } long CQQTX::Send(SOCKET sock,BYTE *pData, long lLen) { #define SOCKET_SEND_TIMEOUT 10000 //10秒发送超时 if ( sock == INVALID_SOCKET ) return 0; long lSended = 0; DWORD lTimeOut = ::GetTickCount(); while ( 1 ) { long lRet = send(sock,(char*)(pData+lSended), lLen-lSended,0); if ( lRet == SOCKET_ERROR ) //发送错误 { if ( GetLastError() != WSAEWOULDBLOCK ) //绶区已满 return SOCKET_ERROR; } if ( lRet > 0 ) { lSended += lRet; if ( lSended >= lLen ) break; } else { Sleep_Lu( 20 ); //绶冲区已满,稍等 } if ( ::GetTickCount() - lTimeOut > SOCKET_SEND_TIMEOUT ) break; } return lSended; } DWORD WINAPI CQQTX::SocketSend(LPVOID lParam) { CQQTX * pNet = (CQQTX *)lParam; QQTX_Data_Recv Drecv={0}; while ( !pNet->m_bQuitThread ) { //发送短信 Sleep_Lu(20); } return 1; } DWORD WINAPI CQQTX::SocketRecv(LPVOID lParam) { CQQTX * pNet = (CQQTX *)lParam; QQTX_Data_Recv Drecv={0}; pNet->m_dwEndRecvTime = ::GetTickCount(); //保存最后接收到帧的时间 DWORD dwEndSend=GetTickCount(); while ( !pNet->m_bQuitThread ) { BOOL bReConnect=false; if ( isSocketClose(pNet->m_hSocket) ) { pNet->Close(); //重新接收数据 if ( Drecv.pRecvFrame ) delete Drecv.pRecvFrame; memset(&Drecv,0,sizeof(Drecv)); if ( pNet->m_bInitQQTX ) //原已连接的,重新连接 { bReConnect = true; } else { pNet->m_bLoginQQTX = -4; //对端断开连接,登录失败 } } if ( ReadFrame(pNet->m_hSocket,&Drecv) ) //判断是否有接收到新的Frame { pNet->m_dwEndRecvTime = ::GetTickCount(); //保存最后接收到帧的时间 pNet->m_bRecvFrame = true; pNet->RecvFrame(Drecv); //处理接收到的帧 Drecv.lDataLen = 0; Drecv.lRecvType = 0; if ( Drecv.pRecvFrame ) { delete Drecv.pRecvFrame; Drecv.pRecvFrame = NULL; } } //判断接收是否超时 //if ( pNet->m_dwEndSendTime > 0 && !pNet->m_bRecvFrame) if ( pNet->m_dwEndSendTime>0 && ::GetTickCount() - pNet->m_dwEndRecvTime >(QQTX_ACTIVE_TESTTIME*QQTX_ACTIVE_COUNTOUT+QQTX_ACTIVE_TIMEOUT) ) //接收超时 { pNet->m_dwEndRecvTime = ::GetTickCount()-QQTX_ACTIVE_TIMEOUT; //如果接收超时,重新计数 pNet->Close(); //重新接收数据 if ( Drecv.pRecvFrame ) delete Drecv.pRecvFrame; memset(&Drecv,0,sizeof(Drecv)); bReConnect = true; } //判断是否应该发送Active_Test指令 if ( pNet->m_dwEndSendTime > 0 ) { if ( ::GetTickCount() - pNet->m_dwEndSendTime > QQTX_ACTIVE_TESTTIME ) //重新连接 { ULONG lSeq=0; pNet->SendFrame(pNet->m_hSocket,pNet->m_lID,pNet->m_dwEndSendTime, QQ_ACTIVE_TEST ,_T(""),lSeq ); } } //发送短信 //pNet->Back_Send(); if ( bReConnect ) { for ( int i=0 ; i<30 && !pNet->m_bQuitThread; i++ ) //延时5秒 { Sleep_Lu(100); } pNet->SetErrMsg("QQTX消息:正在重连网关..."); if ( !pNet->m_bQuitThread ) pNet->InitQQTX2(); //重新连接到服务器 } //Sleep_Lu(1); } if ( Drecv.pRecvFrame ) delete Drecv.pRecvFrame; pNet->Close(); return 1; } void CQQTX::SetRecvProc(QQTX_RECVPROC proc) { m_pRecvProc = proc; } BOOL CQQTX::ReadFrame(SOCKET s, QQTX_Data_Recv *pRecv) { ULONG nBytes=0; ioctlsocket(s,FIONREAD, &nBytes); if (nBytes <= 0 ) { Sleep_Lu(20); //没有数据,等待一段时间 return false; } BYTE bTemp=0; long lRet=0; switch( pRecv->lRecvType ) { case 0://接收头 lRet = recv(s, (char*)&bTemp,sizeof(bTemp),0); if ( lRet > 0 && bTemp==QQ_stx) { pRecv->lRecvType = 1; //开始接收长度 pRecv->lDataLen = 0; if ( pRecv->pRecvFrame ) { delete pRecv->pRecvFrame; pRecv->pRecvFrame = NULL; } } break; case 1: //接收长度 lRet = recv(s, (char*)&pRecv->lDataLen,sizeof(pRecv->lDataLen),0); if ( lRet > 0 ) { pRecv->lDataLen = ntohl(pRecv->lDataLen); if ( pRecv->lDataLen > 0 && pRecv->lDataLen<4096000) { pRecv->pRecvFrame = new BYTE[pRecv->lDataLen]; *((BYTE*)pRecv->pRecvFrame) = QQ_stx; //存好前4个字节 *((ULONG*)(pRecv->pRecvFrame+1)) = pRecv->lDataLen; //存好前4个字节 pRecv->lRecvLen = sizeof(pRecv->lDataLen)+1; pRecv->lRecvType = 2; //开始接收数据s } } break; case 2: //接收数据 { //计算需要接收的总字节数 ULONG lBeRecv = pRecv->lDataLen-pRecv->lRecvLen; lRet = recv(s,(char*)pRecv->pRecvFrame+pRecv->lRecvLen , lBeRecv ,0); if ( lRet > 0 ) pRecv->lRecvLen += lRet ; if ( pRecv->lRecvLen == pRecv->lDataLen ) //Frame 已收满 { pRecv->lDataLen = 0; //重新开始接收 return true; } } break; } /* if ( pRecv->lDataLen <= 0 && nBytes<= sizeof(pRecv->lDataLen) ) //未收够长度信息 return false; long lRet; if (pRecv->lDataLen <= 0 ) //未收到数据,现在开始接收 { lRet = recv(s, (char*)&pRecv->lDataLen,sizeof(pRecv->lDataLen),0); if ( lRet > 0 ) { pRecv->lDataLen = ntohl(pRecv->lDataLen); if ( pRecv->lDataLen > 0 ) { if ( pRecv->pRecvFrame ) { delete pRecv->pRecvFrame; pRecv->pRecvFrame = NULL; } pRecv->pRecvFrame = new BYTE[pRecv->lDataLen]; *((long*)pRecv->pRecvFrame) = pRecv->lDataLen; //存好前4个字节 pRecv->lRecvLen = sizeof(pRecv->lDataLen); } } } else { //计算需要接收的总字节数 ULONG lBeRecv = pRecv->lDataLen-pRecv->lRecvLen; lRet = recv(s,(char*)pRecv->pRecvFrame+pRecv->lRecvLen , lBeRecv ,0); if ( lRet > 0 ) pRecv->lRecvLen += lRet ; if ( pRecv->lRecvLen == pRecv->lDataLen ) //Frame 已收满 { pRecv->lDataLen = 0; //重新开始接收 return true; } } */ return false; } BOOL CQQTX::RecvFrame(QQTX_Data_Recv &Drecv) { QQTX_Head * pHead=(QQTX_Head *)Drecv.pRecvFrame; if ( pHead->Total_Length != Drecv.lRecvLen ) return false; pHead->Command_Id = ntohl(pHead->Command_Id); pHead->Sequence_Id= ntohl(pHead->Sequence_Id); BYTE * pData = (BYTE*)(Drecv.pRecvFrame+sizeof(QQTX_Head)); long lLen = pHead->Total_Length-sizeof(QQTX_Head)-1; CStringA strAnsA((char*)pData,lLen); CString strAns = CA2W(strAnsA,CP_UTF8); long lRet = 0; switch( pHead->Command_Id ) { case QQ_CONNECT_RESP: lRet = Process_Connect(*pHead,strAns); break; case QQ_ACTIVE_TEST_RESP: lRet = Process_Active(*pHead,strAns); break; default: lRet = Process_Other(*pHead,strAns); break; } return lRet; } long CQQTX::QQTX_Init(const TCHAR *pIP,USHORT lPort, const TCHAR *pUser, const TCHAR *pPasswd,const TCHAR * pKey,int lConnectType) { //记录信息 strcpy(m_szQQTXIP,CW2A(pIP)); _tcscpy(m_szQQTXUser,pUser); _tcscpy(m_szQQTXPasswd,pPasswd); _tcscpy(m_szQQTXKey,pKey); m_lConnectType = lConnectType; if ( !Connect(m_szQQTXIP,lPort) ) { return -1; //连接服务器失败 } CString strReq; CString strTemp=_T("")\ _T("")\ _T("%d")\ _T("%s")\ _T("%s")\ _T("%s")\ _T(""); CString strTimeStamp; SYSTEMTIME t;GetLocalTime(&t); strTimeStamp.Format(_T("%04d%02d%02d%02d%02d%02d"),t.wYear,t.wMonth,t.wDay,t.wHour,t.wMinute,t.wSecond); CString strUserName = m_szQQTXUser; CString strPasswd = m_szQQTXPasswd; CString strKey = m_szQQTXKey; CString strMd5=CSendPub::GetMD5(strUserName+strKey+strTimeStamp+strPasswd); strReq.Format(strTemp,m_lConnectType,strUserName,strTimeStamp,strMd5); m_bLoginQQTX = -1; ULONG lSeq=0; if ( !SendFrame(m_hSocket,m_lID,m_dwEndSendTime,QQ_CONNECT,strReq,lSeq) ) { m_bQuitThread = true; return -2; //发送数据失败 } long lTime = ::GetTickCount(); while ( m_bLoginQQTX == -1 ) { if ( ::GetTickCount()-lTime > 30000 ) //超时了 break; Sleep_Lu(20); } if ( m_bLoginQQTX==0 ) { m_bInitQQTX = true; //已初始化 return m_bLoginQQTX; //返回成功 } if ( m_bLoginQQTX == -1 ) { m_bQuitThread = true; return -3; //登录超时 } else { m_bQuitThread = true; return m_bLoginQQTX; //登录返回成功,具体请看错误码 } m_bQuitThread = true; return -4; //其它错误 } long CQQTX::SendFrame(SOCKET sock,ULONG &lID,DWORD &dwEndSendTime,ULONG lCommandID,CString strReq,ULONG & lSeq ) { EnterCriticalSection(&m_CriSendFrame); //防止冲突 CStringA strReqA = CW2A(strReq,CP_UTF8); long lDataLen = strReqA.GetLength(); QQTX_Head head={0}; long lAllLen; head.Total_Length = sizeof(head)+lDataLen+1; //总长度,最后还有个etx lAllLen = head.Total_Length; head.Command_Id = lCommandID; if ( lID >= 0xF000000 ) //重新开始序号 lID = 1; if ( lSeq > 0 ) head.Sequence_Id = lSeq; //直接使用相同序号的发送 else head.Sequence_Id = lID++; lSeq = head.Sequence_Id; //返回 head.stx = QQ_stx; head.Total_Length = htonl(head.Total_Length); head.Command_Id = htonl(head.Command_Id); head.Sequence_Id = htonl(head.Sequence_Id); dwEndSendTime = ::GetTickCount(); //记录最后发送时间 BYTE * pSData = new BYTE[lAllLen]; long lSetLen=0; memcpy(pSData,&head,sizeof(head));lSetLen+=sizeof(head); memcpy(pSData+lSetLen,strReqA.GetBuffer(),lDataLen);lSetLen+=lDataLen; pSData[lSetLen]=QQ_etx;lSetLen+=1; BOOL bOK = Send(sock,(BYTE*)pSData , lAllLen ); delete pSData; long lRet = 0; if ( bOK ) lRet = head.Sequence_Id; LeaveCriticalSection(&m_CriSendFrame); //防止冲突 return lRet; } long CQQTX::Process_Connect(QQTX_Head head,CString strAns) { CMarkup xml(strAns); CString strRetCode; CString strErrMsg; if ( xml.FindElem(_T("/Ans/RetCode")) ) strRetCode =xml.GetData(); if ( xml.FindElem(_T("/Ans/ErrMsg")) ) strErrMsg =xml.GetData(); BOOL bLoginRet = false; strRetCode.Trim(); if ( strRetCode == CString(_T("0")) ) { bLoginRet = true; } if ( m_bInitQQTX && !bLoginRet ) //需要重新登录 { //InitQQTX2(); //登录不成功,关闭连接 this->Close(); } CString str; str.Format(_T("QQTX消息:连接网关返回代码:%s,%s") , strRetCode,strErrMsg ); SetErrMsg(str); m_bLoginQQTX = _ttol(strRetCode); if (!bLoginRet ) m_bLoginQQTX = -100; return 1; } BOOL CQQTX::InitQQTX2() { m_bLoginQQTX = -1; if ( !Connect2() ) { return false; //连接服务器失败 } CString strReq; CString strTemp=_T("")\ _T("")\ _T("%d")\ _T("%s")\ _T("%s")\ _T("%s")\ _T(""); CString strTimeStamp; SYSTEMTIME t;GetLocalTime(&t); strTimeStamp.Format(_T("%04d%02d%02d%02d%02d%02d"),t.wYear,t.wMonth,t.wDay,t.wHour,t.wMinute,t.wSecond); CString strUserName = m_szQQTXUser; CString strPasswd = m_szQQTXPasswd; CString strKey = m_szQQTXKey; CString strMd5=CSendPub::GetMD5(strUserName+strKey+strTimeStamp+strPasswd); strReq.Format(strTemp,m_lConnectType,strUserName,strTimeStamp,strMd5); m_bLoginQQTX = -1; ULONG lSeq=0; if ( !SendFrame(m_hSocket,m_lID,m_dwEndSendTime,QQ_CONNECT,strReq,lSeq) ) return -2; //发送数据失败 //重新连接时,需要重发短信 // memset(m_pSendList,0,sizeof(QQTX_Send_List)*SMS_MAX_LIST); /* for ( int i=0 ; i< SMS_MAX_LIST ;i++ ) { if ( m_pSendList[i].lSendID > 0 ) { m_pSendList[i].lReSendCount = 0; m_pSendList[i].lSendTime = 0; m_pSendList[i].lSeq = 0; } } */ return -4; //其它错误 } long CQQTX::Process_Active(QQTX_Head head, CString strAns) { m_dwTestCount = 0; //收到active指令,检测次数为0 return 1; } long CQQTX::Process_Other(QQTX_Head head,CString strAns) { QQTX_Deliver_List Deliver; Deliver.lSendID = head.Sequence_Id; Deliver.head = head; Deliver.strAns = strAns; Deliver_Add(Deliver); //添加到列表 /* //发送返回 QQTX_Deliver_Resp resp={0}; resp.Msg_ID = pDeliver->Msg_ID; resp.Result = 0; ULONG lReq=head.Sequence_Id; SendFrame(m_hSocket,m_lID,m_dwEndSendTime,QQTX_DELIVER_RESP,(BYTE*)&resp,lReq); */ return true; } BOOL CQQTX::GetDeliver(QQTX_Deliver_List &deliver) { EnterCriticalSection(&m_CriDeliverList); //防止冲突 //从当前点到结尾 for ( int i=m_lDeliverList ; i< QQTX_MAX_DELIVER; i++ ) { if ( m_pDeliverList[i].lSendID != 0 ) //其等于0时,认为此位置为空 { m_lDeliverList = i+1; deliver = m_pDeliverList[i]; m_pDeliverList[i].lSendID = 0; LeaveCriticalSection(&m_CriDeliverList); //防止冲突 return true; } } //从开头到当前点 for ( i=0 ; i0 ) { strRet = m_strErrMsg; m_strErrMsg = CString(_T("")); } LeaveCriticalSection(&m_CriErrMsg); //防止冲突 return strRet; } void CQQTX::SetErrMsg(CString str) { EnterCriticalSection(&m_CriErrMsg); //防止冲突 m_strErrMsg = str; LeaveCriticalSection(&m_CriErrMsg); //防止冲突 } BOOL CQQTX::isConnectGateWay() { if ( m_bLoginQQTX == 0 ) { return true; } return false; }