Windows MFC远程监测系统
设计思路
- 根据服务器IP和端口建立连接
- 服务器接收客户端连接请求后,将客户端IP显示在树形节点上,并将客户端socket保存在节点的数据结构中,用于用户通信
- 双击客户端IP节点,弹出新对话框用于显示监控页面
- 双击事件中,服务器先向客户端发送位图结构信息的请求,用于获取客户端当前屏幕截图的位图结构信息
- 客户端接收请求后,对当前屏幕进行截屏,并拷贝到位图结构中,先将位图结构信息返回给服务器
- 服务器接收到结构信息,判断无误后,才再次发送位图数据的请求;
- 客户端接收到位图数据请求后,才将上次截屏的位图数据返回给服务器;由于位图数据可能较大,不能一次发送,所以需要循环发送,直至全部发送位置
- 服务器也是循环接收位图数据,若接收成功,则刷新界面,重新绘制位图;
9 定时器控制请求位图信息的命令间隔一段时间发一次,保证监控界面实时更新,达到监控客户端桌面的效果

图1:远程监控系统设计流程
具体实现
服务器
- 初始化主界面,创建监听套接字
BOOL CMFCServerDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// TODO: 在此添加额外的初始化代码SetWindowTextW(L"远程监控系统");m_tree.InsertItem(L"所有用户");//创建监听套接字m_pListenSocket = new CListenSocket(this);CString strMsg;if(NULL == m_pListenSocket){strMsg.Format(L"初始化套接字失败,错误码:%d", GetLastError());MessageBox(strMsg, L"温馨提示, MB_OK|MB_ICONERROR");//关闭对话框EndDialog(IDOK);return TRUE;;}if(FALSE == m_pListenSocket->Create(PORT, SOCK_STREAM))//tcp,建立连接,udp,不建立连接SOCK_DGRAM{strMsg.Format(L"创建套接字失败,错误码:%d", GetLastError());MessageBox(strMsg, L"温馨提示, MB_OK|MB_ICONERROR");//关闭对话框EndDialog(IDOK);return TRUE;;}//监将套接字设置为监听模式套if(FALSE == m_pListenSocket->Listen()){strMsg.Format(L"监听套接字设置失败,错误码:%d", GetLastError());MessageBox(strMsg, L"温馨提示, MB_OK|MB_ICONERROR");//关闭对话框EndDialog(IDOK);return TRUE;;}return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
- 接收客户端连接请求,并将客户端socket保存在树形节点中,便于后面通信
void CMFCServerDlg::OnAccept()
{CClientSocket* pClientSocket = new CClientSocket;if(FALSE == m_pListenSocket->Accept(*pClientSocket)){delete pClientSocket;pClientSocket = NULL;}else{//将客户端的IP显示在列表上CString strIp;UINT port;pClientSocket->GetPeerName(strIp,port);//将IP添加到树形结构的根节点下//先获取根节点HTREEITEM hRootItem = m_tree.GetRootItem();HTREEITEM hItem = m_tree.InsertItem(strIp, hRootItem);//将客户端套接字存储在树节点数据中m_tree.SetItemData(hItem, (DWORD_PTR)pClientSocket);}
}
- 双击客户端IP节点,弹出监控页面
//用于处理双击客户端IP事件
void CMFCServerDlg::OnDblclkTree1(NMHDR *pNMHDR, LRESULT *pResult)
{// TODO: 在此添加控件通知处理程序代码//排除根节点的双击HTREEITEM hRoot = m_tree.GetRootItem();HTREEITEM hCurItem =m_tree.GetSelectedItem();if(hRoot == hCurItem){return;}//获取选中节点中存储的客户端套接字CClientSocket* pClienSocket = (CClientSocket*)m_tree.GetItemData(hCurItem);CShowDlg* pShowDlg = new CShowDlg(pClienSocket);CString strMsg;if(FALSE == pShowDlg->Create(IDD_SHOW_DLG)){delete pShowDlg;pShowDlg = NULL;strMsg.Format(L"创建对话框失败,错误码%d",GetLastError());MessageBox(strMsg, L"温馨提示", MB_OK | MB_ICONERROR);EndDialog(IDOK);}pShowDlg->ShowWindow(SW_SHOW);//MessageBox(L"双击树形控件");*pResult = 0;
}
- 监控界面初始化,设置定时器,定时向客户端发送客户端界面的位图请求
BOOL CShowDlg::OnInitDialog()
{CDialogEx::OnInitDialog();//设置定时器,定时向客户端发送请求页面的消息SetTimer(TIME_ID, m_iTime, NULL);return TRUE;
}//nIDEvent: 定时器ID
void CShowDlg::OnTimer(UINT_PTR nIDEvent)
{//每隔m_iTime毫秒处理一次switch(nIDEvent){case TIME_ID://获取客户端的屏幕(屏幕的位图)OnGetClientScreen();break; default:break;}CDialogEx::OnTimer(nIDEvent);
}void CShowDlg::OnGetClientScreen()
{//自定义协议//协议包括:1. 消息类型int iMsgType = BITMAP_STRUCT;//发送消息给客户端if(SOCKET_ERROR == m_pClientSocket->Send(&iMsgType, sizeof(iMsgType), 0)){return ;}//接收客户端返回的位图结构//Receive返回接收的字节数int iRet = m_pClientSocket->Receive(&m_logBmp, sizeof(BITMAP));if(iRet != sizeof(BITMAP)){MessageBox(TEXT("接收位图结构错误!"));EndDialog(IDOK);return;}//再次向客户端发送位图数据的请求iMsgType = BITMAP_DATA;//发送消息给客户端if(SOCKET_ERROR == m_pClientSocket->Send(&iMsgType, sizeof(iMsgType), 0)){return ;}//分配内存用于存储接收的位图数据m_iBmpSize = m_logBmp.bmWidthBytes * m_logBmp.bmHeight;if(NULL != m_lpszBmpData){delete m_lpszBmpData;m_lpszBmpData = NULL;}m_lpszBmpData = new char[m_iBmpSize];int iRecDataSize = 0;//已经接收的字节数char* p = m_lpszBmpData;int iCurRecDataSize = 0;//循环接收位图数据do{iCurRecDataSize = m_pClientSocket->Receive(p , m_iBmpSize - iRecDataSize);iRecDataSize += iCurRecDataSize;p += iCurRecDataSize;}while(iRecDataSize < m_iBmpSize);if(iRecDataSize != m_iBmpSize){MessageBox(TEXT("接收为徒失败"), TEXT("提示"), MB_OK);return;}//刷新界面Invalidate(FALSE);}
- 接收到位图后,刷新监控界面
//绘制位图
void CShowDlg::OnPaint()
{CPaintDC dc(this); // device context for painting// TODO: 在此处添加消息处理程序代码// 不为绘图消息调用 CDialogEx::OnPaint()if(m_lpszBmpData == NULL){return;}//还原位图//位图头BITMAPINFOHEADER bih;bih.biBitCount = m_logBmp.bmBitsPixel;//每个像素占多少位bih.biClrImportant = 0; //显示位图所需要的颜色索引数,0表示需要很多种颜色bih.biClrUsed = 0; //颜色表中实际有位图所使用的的颜色数量;默认为0bih.biCompression = 0;//压缩类型bih.biHeight = m_logBmp.bmHeight;//位图的高度bih.biPlanes = 1;//必须为1,表示目标设备的平面数量bih.biSize = sizeof(BITMAPINFOHEADER);//位图头的大小bih.biSizeImage =m_logBmp.bmWidthBytes*m_logBmp.bmHeight;//图像的大小bih.biWidth = m_logBmp.bmWidth;//图像的宽度bih.biXPelsPerMeter = 0;//水平分辨率bih.biYPelsPerMeter = 0;//竖直分辨率//构造位图CBitmap bmp;if(FALSE == bmp.CreateBitmapIndirect(&m_logBmp)){return;}//若位图句柄为空if(bmp.m_hObject == NULL){return;}//创建内存DCCDC memDc;memDc.CreateCompatibleDC(&dc);SetDIBits(memDc.m_hDC, bmp, 0, m_logBmp.bmHeight, m_lpszBmpData, (BITMAPINFO*)&bih, DIB_RGB_COLORS);//获取客户区大小CRect rect;GetClientRect(&rect);memDc.SelectObject(&bmp);//拉伸dc.StretchBlt(0, 0, rect.Width(), rect.Height(), &memDc, 0, 0, m_logBmp.bmWidth, m_logBmp.bmHeight, SRCCOPY);}
客户端
- 向服务器发送连接请求
//点击,则连接服务器
void CMFCClientDlg::OnBnClickedBtnConnect()
{// TODO: 在此添加控件通知处理程序代码//获取控件中的值UpdateData(TRUE);CString strIp;m_serverIpAddress.GetWindowText(strIp);if(strIp.IsEmpty() || TEXT("0.0.0.0") == strIp){MessageBox(L"请输入IP地址",L"温馨提示",MB_OK|MB_ICONERROR);}CString strMsg;m_pClientSocket = new CClientSocket(this);if(NULL == m_pClientSocket){strMsg.Format(L"创建套接字失败,错误码为%d", GetLastError());MessageBox(strMsg, L"温馨提示",MB_OK | MB_ICONERROR);//关闭对话框EndDialog(IDOK);return;}//创建套接字if(FALSE == m_pClientSocket->Create()){strMsg.Format(L"创建套接字失败,错误码为%d", GetLastError());MessageBox(strMsg, L"温馨提示",MB_OK | MB_ICONERROR);//关闭对话框EndDialog(IDOK); return;}//连接服务器if(FALSE == m_pClientSocket->Connect(strIp, m_uPort)){strMsg.Format(L"连接服务器失败失败,错误码为%d", GetLastError());MessageBox(strMsg, L"温馨提示",MB_OK | MB_ICONERROR);//关闭对话框EndDialog(IDOK); return;}
}
- 接收服务器发送过来的位图结构信息请求,并截取当前屏幕,拷贝到位图结构中
//接收服务器发送过来的消息
void CClientSocket::OnReceive(int nErrorCode)
{int iMsgType;Receive(&iMsgType, sizeof(iMsgType));switch(iMsgType){case BITMAP_STRUCT://发送一个位图结构给服务器m_pClientDlg->SendBitMapStruct();break;case BITMAP_DATA:m_pClientDlg->SendBitMapData();break;default:break;};CSocket::OnReceive(nErrorCode);
}void CMFCClientDlg::SendBitMapStruct()
{//开始采集客户端当前屏幕的位图CatchScreen();//发送位图至服务器m_pClientSocket->Send(&m_bLogBmp, sizeof(BITMAP));
}//捕获屏幕
void CMFCClientDlg::CatchScreen()
{CDC dc;dc.CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);//创建位图CBitmap bmp;bmp.CreateCompatibleBitmap(&dc, m_iWindowWidth, m_iWindowHeight);//创建内存DCCDC tmpMemDC;//拷贝tmpMemDC.CreateCompatibleDC(&dc);tmpMemDC.SelectObject(&bmp);//关联位图//复制屏幕至位图tmpMemDC.BitBlt(0, 0, m_iWindowWidth, m_iWindowHeight, &dc, 0, 0, SRCCOPY);//获取位图结构bmp.GetBitmap(&m_bLogBmp);//获取位图数据//位图大小m_iBmpSize = m_bLogBmp.bmWidthBytes * m_bLogBmp.bmHeight;//分配内存if(NULL != m_lpszBmpData){delete[] m_lpszBmpData;m_lpszBmpData = NULL;}m_lpszBmpData = new char[m_iBmpSize];//位图头BITMAPINFOHEADER bih;bih.biBitCount = m_bLogBmp.bmBitsPixel;//每个像素占多少位bih.biClrImportant = 0; //显示位图所需要的颜色索引数,0表示需要很多种颜色bih.biClrUsed = 0; //颜色表中实际有位图所使用的的颜色数量;默认为0bih.biCompression = 0;//压缩类型bih.biHeight = m_bLogBmp.bmHeight;//位图的高度bih.biPlanes = 1;//必须为1,表示目标设备的平面数量bih.biSize = sizeof(BITMAPINFOHEADER);//位图头的大小bih.biSizeImage = m_iBmpSize;//图像的大小bih.biWidth = m_bLogBmp.bmWidth;//图像的宽度bih.biXPelsPerMeter = 0;//水平分辨率bih.biYPelsPerMeter = 0;//竖直分辨率//将位图数据复制到分配的内存空间中GetDIBits(dc, bmp, 0, bih.biHeight, m_lpszBmpData, (BITMAPINFO*)&bih, DIB_RGB_COLORS);//释放DCtmpMemDC.DeleteDC();bmp.DeleteObject();dc.DeleteDC();}
- 接收服务器发送过来的位图数据信息请求,将截取当前屏幕保存的位图数据发送给服务器
void CMFCClientDlg::SendBitMapData()
{if(NULL == m_lpszBmpData){return;}int iSendDataSize = 0;//已经发送的字节数char* p = m_lpszBmpData;int iCurSendDataSize = 0;//循环发送位图数据do{iCurSendDataSize = m_pClientSocket->Send(p , m_iBmpSize - iSendDataSize);iSendDataSize += iCurSendDataSize;p += iCurSendDataSize;}while(iSendDataSize < m_iBmpSize);//释放内存delete [] m_lpszBmpData;m_lpszBmpData = NULL;m_iBmpSize = 0;}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!