Remote monitoring system

Windows MFC remote monitoring system

Design ideas

  1. Establish a connection based on the server IP and port
  2. After the server receives the client connection request, it displays the client IP on the tree node and saves the client socket in the data structure of the node for user communication
  3. Double-click the client IP node, and a new dialog box will pop up to display the monitoring page
  4. In the double-click event, the server first sends a request for bitmap structure information to the client to obtain the bitmap structure information of the current screenshot of the client
  5. After receiving the request, the client takes a screenshot of the current screen and copies it to the bitmap structure, and first returns the bitmap structure information to the server
  6. After the server receives the structure information and judges that it is correct, it sends the request for bitmap data again;
  7. After the client receives the bitmap data request, it returns the bitmap data of the last screenshot to the server; because the bitmap data may be large and cannot be sent at one time, it needs to be sent cyclically until all the locations are sent.
  8. The server also receives bitmap data cyclically. If the reception is successful, the interface is refreshed and the bitmap is redrawn;
    9 The timer controls the command to request bitmap information and is sent once a period of time to ensure that the monitoring interface is updated in real time to achieve the effect of monitoring the client desktop
    Figure 1: Design process of remote monitoring system
    Figure 1: Design process of remote monitoring system

Implementation

server

  1. Initialize the main interface and create a listening socket
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
}
  1. Receive the client connection request and save the client socket in the tree node to facilitate subsequent communication
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);
	
	}
}
  1. Double-click the client IP node, and the monitoring page will pop up
//用于处理双击客户端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;
}
  1. Initialize the monitoring interface, set the timer, and send the bitmap request of the client interface to the client regularly
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);

}
  1. After receiving the bitmap, refresh the monitoring interface
//绘制位图
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; //颜色表中实际有位图所使用的的颜色数量;默认为0
	bih.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;
	}

	//创建内存DC
	CDC 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);


}

Client

  1. Send a connection request to the server
//点击,则连接服务器
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;
	}
}
  1. Receive the bitmap structure information request sent by the server, intercept the current screen, and copy it into the bitmap structure
//接收服务器发送过来的消息
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);

	//创建内存DC
	CDC 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; //颜色表中实际有位图所使用的的颜色数量;默认为0
	bih.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);

	//释放DC
	tmpMemDC.DeleteDC();
	bmp.DeleteObject();
	dc.DeleteDC();

}
  1. Receive the bitmap data information request sent by the server, and send the bitmap data saved on the current screen to the server
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;

}

Guess you like

Origin blog.csdn.net/tianzhiyi1989sq/article/details/95001409