#include "StdAfx.h" #include "NetClient.h" #include "NetLinkMain.h" #include "PduEntity.h" #include "PduEntityHead.h" CNetClient::CNetClient(CNetLinkMain* pParent, PDU_LINK_TYPE a_nLinkType, bool IsAutoReconnect) : CSocketBase(pParent), m_Assistant(IsAutoReconnect) { m_Assistant.m_pParent = this; m_Assistant.linkType() = a_nLinkType; ZeroMemory(&m_Overlapped, sizeof(m_Overlapped)); m_WsaBuf.buf = NULL; m_WsaBuf.len = 0; __resetBuffer(); } CNetClient::~CNetClient(void) { TRACE(_T("@CNetClient::~CNetClient\r\n")); } /***************************************************************** **【函数名称】 __resetBuffer **【函数功能】 重置接收缓冲 **【参数】 **【返回值】 ****************************************************************/ void CNetClient::__resetBuffer( void ) { ZeroMemory(m_szBuffer, PDU_BUFFER_LEN); m_HasRead = 0; } /***************************************************************** **【函数名称】 __create **【函数功能】 创建 **【参数】 **【返回值】 ****************************************************************/ bool CNetClient::__create( void ) { ASSERT(m_Socket == INVALID_SOCKET); m_Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); if(m_Socket == INVALID_SOCKET) return false; else return true; } /***************************************************************** **【函数名称】 __connect **【函数功能】 连接服务器 **【参数】 **【返回值】 ****************************************************************/ bool CNetClient::__connect( LPCTSTR FarAddr, int FarPort ) { SOCKADDR_IN sockAddr; memset(&sockAddr,0,sizeof(sockAddr)); sockAddr.sin_family = AF_INET; sockAddr.sin_addr.s_addr = inet_addr(FarAddr); if (sockAddr.sin_addr.s_addr == INADDR_NONE) { LPHOSTENT lphost; lphost = gethostbyname(FarAddr); if (lphost != NULL) sockAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr; else return false; } sockAddr.sin_port = htons((u_short)FarPort); if(connect(m_Socket, (sockaddr*)&sockAddr, sizeof(sockAddr)) == 0) { m_pParent->onConnEstablished(this); return true; } else { m_pParent->onConnFailed(this); return false; } } /***************************************************************** **【函数名称】 __send **【函数功能】 发送数据 **【参数】 **【返回值】 ****************************************************************/ bool CNetClient::__send( UCHAR a_szBuf[], UINT a_BufLen ) { UINT nHasSend = 0; while(nHasSend < a_BufLen) { int nSend = send(m_Socket, (LPCTSTR)&a_szBuf[nHasSend], a_BufLen - nHasSend, 0); if(nSend > 0) { nHasSend += nSend; } else { if(WSAGetLastError() == WSAEWOULDBLOCK) // 阻塞 Sleep(10); else return false; } } // end while return true; } /***************************************************************** **【函数名称】 doJob **【函数功能】 开始解析接收的数据并投递接收请求 **【参数】 **【返回值】 ****************************************************************/ void CNetClient::doJob( OVERLAPPED* pOverLapped, DWORD BytesTransfered ) { TRACE(_T("recv: %d @CNetClient::doJob\r\n"), BytesTransfered); if(m_DropFlag) return; if(BytesTransfered == 0) { onDisconnect(); return; } // 对接收的数据进行处理 m_HasRead += BytesTransfered; // 如果已经接收到完整的包头 while(m_HasRead > PDU_HEAD_LEN) // 有可能一次接收到多个完整命令 { CPduEntityHead pduHead; // 解析PDU数据包头 if(!pduHead.Decode((UCHAR*)m_szBuffer)) { __resetBuffer(); // 如果解析失败,清除缓冲区 break; } // end if // 如果数据包完整(HEAD + DATA) if(m_HasRead >= (PDU_HEAD_LEN + pduHead.GetDataLen())) { // 拷贝当前要处理的数据到临时缓冲区 UCHAR szTmpBuf[PDU_MAX_DATA_LEN] = { 0 }; memcpy_s(szTmpBuf, PDU_MAX_DATA_LEN, m_szBuffer, PDU_HEAD_LEN + pduHead.GetDataLen()); // 清除已处理的数据(数据前移) m_HasRead = m_HasRead - (PDU_HEAD_LEN + pduHead.GetDataLen()); memmove_s(m_szBuffer, PDU_BUFFER_LEN, &m_szBuffer[PDU_HEAD_LEN + pduHead.GetDataLen()], m_HasRead); ZeroMemory(&m_szBuffer[m_HasRead], PDU_BUFFER_LEN - m_HasRead); // 对数据进行处理 CPduEntity pduEntity(&pduHead, &szTmpBuf[PDU_HEAD_LEN]); pduEntity.SetAssoSocket(m_Socket); m_pParent->onRecvCommand(&pduEntity); } else { break; // 如果当前数据不是一个完整数据包,退出循环 } // end if } // end while ready4Recv(); } /***************************************************************** **【函数名称】 isAlive **【函数功能】 客户端是否断链 **【参数】 **【返回值】 ****************************************************************/ bool CNetClient::isAlive( void ) { return monitor(); } /***************************************************************** **【函数名称】 __onDisconnect **【函数功能】 网络断连的处理函数 **【参数】 **【返回值】 ****************************************************************/ void CNetClient::onDisconnect( void ) { TRACE(_T("@CNetClient::onDisconnect\r\n")); if(m_DropFlag) return; _close(); __create(); __resetBuffer(); m_pParent->onConnFailed(this); } /***************************************************************** **【函数名称】 connect2Server **【函数功能】 连接到SERVER端 **【参数】 **【返回值】 ****************************************************************/ bool CNetClient::connect2Server( const CString& a_FarIp, int a_FarPort, PDU_DEV_TYPE a_FarType, int a_FarId ) { if(!__create()) return false; m_Assistant.setFarLinkInfo(a_FarIp, a_FarPort); m_Assistant.setFarDevInfo(a_FarType, a_FarId); if(__connect(a_FarIp, a_FarPort)) return true; else return false; } /***************************************************************** **【函数名称】 regist **【函数功能】 向服务器注册 **【参数】 **【返回值】 ****************************************************************/ bool CNetClient::regist( void ) { // 生成注册命令 CPduEntity reg(PDU_CMD_REG); PDU_DEV_TYPE DevType = PDU_DEV_TYPE_UNKNOWN; int DevId = -1; m_Assistant.getFarDevInfo(DevType, DevId); reg.SetPeerDevInfo(DevType, DevId); // 填充接收方信息 m_pParent->getLocalInfo(DevType, DevId); reg.SetLocalDevInfo(DevType, DevId); // 填充发送方信息 // 发送注册命令 return sendDirectly(®); } /***************************************************************** **【函数名称】 monitor **【函数功能】 发送心跳监听 **【参数】 **【返回值】 ****************************************************************/ bool CNetClient::monitor( void ) { CPduEntity Monitor(PDU_CMD_LISTEN); PDU_DEV_TYPE DevType = PDU_DEV_TYPE_UNKNOWN; int DevId = -1; m_Assistant.getFarDevInfo(DevType, DevId); Monitor.SetPeerDevInfo(DevType, DevId); // 填充接收方信息 m_pParent->getLocalInfo(DevType, DevId); Monitor.SetLocalDevInfo(DevType, DevId); // 填充发送方信息 // 发送心跳监听 return sendDirectly(&Monitor); } /***************************************************************** **【函数名称】 sendDirectly **【函数功能】 直接发送PDU命令 **【参数】 a_pCmd 要发送的命令实体 **【返回值】 ****************************************************************/ bool CNetClient::sendDirectly( CPduEntity* a_pCmd ) { ASSERT(m_Socket != INVALID_SOCKET); // 生成要发送的数据缓冲区 UCHAR szBuf[PDU_HEAD_LEN + PDU_MAX_DATA_LEN]; ZeroMemory(szBuf, PDU_HEAD_LEN + PDU_MAX_DATA_LEN); int nLen = a_pCmd->CreatePackge(szBuf); // 发送 return __send(szBuf, nLen); } /***************************************************************** **【函数名称】 sendPdu **【函数功能】 发送PDU命令 **【参数】 a_pCmd 要发送的命令实体 **【返回值】 ****************************************************************/ bool CNetClient::sendPdu( CPduEntity* a_pCmd ) { PDU_DEV_TYPE DevType; int DevId; m_Assistant.getFarDevInfo(DevType, DevId); a_pCmd->SetPeerDevInfo(DevType, DevId); return sendDirectly(a_pCmd); } /***************************************************************** **【函数名称】 ready4Recv **【函数功能】 准备接收数据 **【参数】 **【返回值】 ****************************************************************/ bool CNetClient::ready4Recv( void ) { // 初始化变量 DWORD dwFlags = MSG_PARTIAL; DWORD dwBytes = 0; m_WsaBuf.buf = &m_szBuffer[m_HasRead]; m_WsaBuf.len = PDU_BUFFER_LEN - m_HasRead; ZeroMemory(&m_Overlapped, sizeof(OVERLAPPED)); // 投递WSARecv请求 int nBytesRecv = WSARecv(m_Socket, &m_WsaBuf, 1, &dwBytes, &dwFlags, &m_Overlapped, NULL ); if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError())) { TRACE(_T("WSARecv error@ CNetClient::ready4Recv\r\n")); onDisconnect(); return false; } return true; } /***************************************************************** **【函数名称】 drop **【函数功能】 丢弃此客户端 **【参数】 **【返回值】 ****************************************************************/ void CNetClient::drop( void ) { m_DropFlag = true; _close(); } /***************************************************************** **【函数名称】 getLocalInfo **【函数功能】 获取本端设备类型及ID **【参数】 **【返回值】 ****************************************************************/ void CNetClient::getLocalInfo( PDU_DEV_TYPE& a_DevType, int& a_DevId ) const { m_pParent->getLocalInfo(a_DevType, a_DevId); }