欢迎光临
未来你我一起携手

Ghost远程管理IOCP模型剥离笔记 向服务端发送消息

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

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);
}

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

 

未经允许不得转载:卧栏听雨 » Ghost远程管理IOCP模型剥离笔记
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址