⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 server.c

📁 经典远程控制代码
💻 C
📖 第 1 页 / 共 3 页
字号:
//服务器端:发送本机桌面到连接的客户端
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <winuser.h>
#include "Server.h"
#include "Command.h"
#include "Gdi.h"
#include "HuffCompress.h"
#include "RLE.h"
//默认端口
#define DEFAULT_PORT        5150
#define SETFLAGS			XP1_GUARANTEED_DELIVERY|XP1_GUARANTEED_ORDER
#define NOTSETFLAGS			XP1_CONNECTIONLESS
#define	LPBMIH				LPBITMAPINFOHEADER

UINT    gPort  = DEFAULT_PORT;
int		nGridX = 8;
int		nGridY = 2;
DWORD	dwLen,dwCompress,dwSendLen;
int		iCompressionLevel = 10;
HWND	hServerWnd;
HDC		hDDC = NULL,hMemDC,hNullDC;
BOOL	fChange = FALSE;
SOCKET	Socket;
SOCKET	Listen;
int		iWidth,iHeight;
WORD	bmBitsPixel = 4;
// 指向全局位图
BOOL	fDIBitmap = FALSE;

// GDI区域结构
struct	GdiList	GdiStart;
struct	GdiList	*pGdiNode;

// 这个结构用来在LPARAM参数中传递信息到客户端线程
struct	myStruct
{
	SOCKET	Socket;
	HWND	hWnd;
};

int SelectProtocols(DWORD dwSetFlags,DWORD dwNotSetFlags,LPWSAPROTOCOL_INFO lpProtocolBuffer,LPDWORD lpdwBufferLength,WSAPROTOCOL_INFO *pProtocol);

//LoadWinsock用来装载和初始化Winsock,绑定本地地址,创建监听socket,等候客户端连接
DWORD WINAPI LoadWinsock(LPVOID lpParam)
{
	// 协议变量
	LPBYTE				pBuf;
	WSAPROTOCOL_INFO	Protocol;
	int					nRet;
	int					nZero = 0;

	int					iAddrSize;
	HANDLE				hThread;
	DWORD				dwThreadId;
	char				szClientIP[81];
	char				szString[255];
	struct	sockaddr_in	local,client;
	// 这个结构用来在LPARAM参数中传递信息到客户端线程
	struct	myStruct	myStructure;

	// 为协议的选择和所有协议的变量决定需要的缓冲区的大小
	dwLen = 0;
	nRet = WSAEnumProtocols(NULL,NULL,&dwLen);
	if (nRet == SOCKET_ERROR)
	{
		if (WSAGetLastError() != WSAENOBUFS)
			return 1;
	}
	pBuf = malloc(dwLen);
	// 为WSASocketGet()得到协议
	nRet = SelectProtocols(SETFLAGS,NOTSETFLAGS,(LPWSAPROTOCOL_INFO)pBuf,&dwLen,&Protocol);

	// 创建我们的监听socket
	Listen = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_IP,NULL,0,SOCK_STREAM);
	if (Listen == SOCKET_ERROR)
	{
		sprintf(szString,"socket() failed: %d",WSAGetLastError());
		MessageBox(NULL,szString,"Remote Server",MB_OK);
		return 1;
	}

	// 设置server端信息
	local.sin_addr.s_addr = htonl(INADDR_ANY);
	local.sin_family = AF_INET;
	local.sin_port = htons(gPort);

	// 绑定到socket
	if (bind(Listen,(struct sockaddr *)&local,sizeof(local)) == SOCKET_ERROR)
	{
		sprintf(szString,"bind() failed: %d\n", WSAGetLastError());
		MessageBox(NULL,szString,"Remote Server",MB_OK);
		return 1;
	}

	//为了减小CPU的利用率,禁止在socket上将数据发送到缓冲。设置SO_SNDBUF为0,
	//从而使winsock直接发送数据到客户端,而不是将数据缓冲才发送。
	nZero = 0;
	setsockopt(Listen,SOL_SOCKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));
	//开始监听
	listen(Listen,SOMAXCONN);

	iAddrSize = sizeof(client);
	while (TRUE)
	{
		// 阻塞方式的接收客户端的连接,但因为这是一个线程函数,所以用户不会感到阻塞
		Socket = accept(Listen,(struct sockaddr *)&client,&iAddrSize);
		if (Socket != INVALID_SOCKET)
		{
			// 设置传到客户端线程的信息的数据结构
			myStructure.Socket = Socket;
			myStructure.hWnd = hServerWnd;
			//找出客户端的IP地址
			memset(szClientIP,'\0',sizeof(szClientIP));
			sprintf(szClientIP,"%s",inet_ntoa(client.sin_addr));
			// 为每一个客户端创建一个线程
			hThread = CreateThread(NULL,0,ClientThread,(LPVOID)&myStructure,0,&dwThreadId);
			if (hThread)
			{
				//关闭线程句柄
				CloseHandle(hThread);
			}
		}
		else
			return 1;
	}

	return 0;
}


//客户端线程函数,这个函数等候从客户端程序发送过来的消息,
//如果这个消息是"REFRESH",那么它发送当前的桌面图片
//如果这个消息是"DISCONNECT",那么它结束和客户端的连接
//如果这个消息以"WM_"开头,那么它就根据消息类型,在服务器端执行该消息
DWORD WINAPI ClientThread(LPVOID lpParam)
{
	HWND	hWnd;
	SOCKET	MySocket;
	FD_SET	SocketSet;
	struct	timeval	timeout;
	char	szMessage[2049];
	DWORD	iRecv;
	struct	myStruct	*myStructure;
	DWORD	iLength;
	DWORD	iRet;
	int		iUpdates;

	// 分析参数
	myStructure = (struct myStruct *)lpParam;
	MySocket = myStructure->Socket;
	hWnd = myStructure->hWnd;

	// 设置超时值
	timeout.tv_sec = 0;		// 秒
	timeout.tv_usec = 0;	// 微秒

	// 设置Socket集合
	SocketSet.fd_count = 1;
	SocketSet.fd_array[1] = MySocket;

	// 轮询sockets
	while(TRUE)
	{
		// 等候发送过来的数据直到超时
		iRet = select(0,&SocketSet,NULL,NULL,&timeout);
		if (iRet != 0)
		{
 			//初始化缓冲
			memset(szMessage,'\0',sizeof(szMessage));
			// 阻塞方式调用recv()
			iRecv = recv(MySocket,szMessage,2048,0);
			szMessage[iRecv] = '\0';

			CHECK_MSG:
			// 是不是"REFRESH"消息
			if (strncmp(szMessage,"REFRESH",7) == 0)
			{
				// 捕获并且发送桌面的更新的区域
				iUpdates = SendRegionDisplay(hServerWnd,MySocket);
			}
			
			// 检查从客户端发送过来的Windows 命令消息,这是一个核心部分
			else if (strncmp(szMessage,"WM_",3) == 0)
			{
				// 解析从客户端发送过来的消息并发送到本机的消息队列
				DispatchWMMessage(szMessage);

				// 看看是否还有消息
				iLength = strlen(szMessage);
				if (iLength > 0)
					goto CHECK_MSG;
			}
			// 检查是否是查询消息
			else if (strncmp(szMessage,"RESOLUTION",10) == 0)
			{
				SendResolution(MySocket);
			}
			// 检查是否是DISCONNECT消息
			else if (strncmp(szMessage,"DISCONNECT",10) == 0)
			{
				fChange = FALSE;
				fDIBitmap = FALSE;

				pGdiNode = GdiStart.pNext;
				while (pGdiNode)
				{
					free(pGdiNode->Gdi.pDIBitmap);
					free(pGdiNode->Gdi.pDIBChangeStart);
					pGdiNode->Gdi.fDIBitmap = FALSE;
					pGdiNode->Gdi.fChange = FALSE;
					pGdiNode = pGdiNode->pNext;
				}
				// 停止查询,相当于结束该线程
				break;
			}
		}
	}
	closesocket(MySocket);
	return 0;
}

// 解析从客户端发送过来的消息并发送到本机的消息队列
void DispatchWMMessage(char *szString)
{
	//鼠标消息
	struct {char *szWMMouseMsg;} 
	WMMouseMsg[] = {"WM_MM","WM_LBD","WM_LBU","WM_LBK",
					"WM_MBD","WM_MBU","WM_MBK",
					"WM_RBD","WM_RBU","WM_RBK"};

	// 键盘消息
	struct {char *szWMKeyBdMsg;}
	WMKeyBdMsg[] = {"WM_KD","WM_KU"};

	// 通用消息:色彩模式,网格数和压缩消息
	struct {char *szMsg;}
	Msg[] = {"WM_COMP","WM_GRID","WM_CMOD"};

	int		nWMMouseMsg;
	int		nWMKeyBdMsg;
	int		nMsg;

	struct	CommandList	CommandStart;
	struct	CommandList	*pCommandNode;
	struct	CommandDS	Command;
	char	*pDest;
	int		iLoc,nChar;
	int		iLoop,iParms;
	char	szString2[2049];

	// 分别得到鼠标,键盘,通用消息的数目
	nWMMouseMsg = (int)(sizeof(WMMouseMsg)/sizeof(WMMouseMsg[0]));
	nWMKeyBdMsg = (int)(sizeof(WMKeyBdMsg)/sizeof(WMKeyBdMsg[0]));
	nMsg = (int)(sizeof(Msg)/sizeof(Msg[0]));

	// 初始化command链表
	CommandStart.pNext = NULL;
	pCommandNode = &CommandStart;

	// 分析command命令,截获命令的参数
	iParms = 0;
	while (pDest = strchr(szString,';'))
	{
		iLoc = pDest - szString;
		nChar = iLoc;
		memset(Command.szElement,'\0',sizeof(Command.szElement));
		strncpy(Command.szElement,szString,nChar);
		// 发送到命令栈中
		pCommandNode = Add_Command(pCommandNode,Command);
		memset(szString2,'\0',sizeof(szString2));
		strcpy(szString2,&szString[iLoc + 1]);
		strcpy(szString,szString2);

		iParms++;
		if (iParms == 5) // 每条命令5个参数
			break;
	}

	// 处理命令
	pCommandNode = CommandStart.pNext;
	if (pCommandNode)
	{
		// 鼠标消息
		UINT	keyFlags;
		int		iMessage;
		int		fWMMouseMsg;
		double	iScaleX,iScaleY,iX,iY;
		DWORD	dwX,dwY;

		// 键盘消息
		int		fWMKeyBdMsg;
		UINT	vk;
		int		fDown;
		int		cRepeat;
		UINT	flags;

		// 判断是否有鼠标消息
		fWMMouseMsg = FALSE;
		for (iLoop = 0;iLoop < nWMMouseMsg;iLoop++)
		{
			if (strcmp(pCommandNode->Command.szElement,WMMouseMsg[iLoop].szWMMouseMsg) == 0)
			{
				// 设置鼠标消息的标志
				fWMMouseMsg = TRUE;
				// 具体的鼠标消息
				if (strcmp(WMMouseMsg[iLoop].szWMMouseMsg,"WM_MM\0") == 0)
					iMessage = 1;
				else if (strcmp(WMMouseMsg[iLoop].szWMMouseMsg,"WM_LBD\0") == 0)
					iMessage = 2;
				else if (strcmp(WMMouseMsg[iLoop].szWMMouseMsg,"WM_LBU\0") == 0)
					iMessage = 3;
				else if (strcmp(WMMouseMsg[iLoop].szWMMouseMsg,"WM_LBK\0") == 0)
					iMessage = 4;
				else if (strcmp(WMMouseMsg[iLoop].szWMMouseMsg,"WM_MBD\0") == 0)
					iMessage = 5;
				else if (strcmp(WMMouseMsg[iLoop].szWMMouseMsg,"WM_MBU\0") == 0)
					iMessage = 6;
				else if (strcmp(WMMouseMsg[iLoop].szWMMouseMsg,"WM_MBK\0") == 0)
					iMessage = 7;
				else if (strcmp(WMMouseMsg[iLoop].szWMMouseMsg,"WM_RBD\0") == 0)
					iMessage = 8;
				else if (strcmp(WMMouseMsg[iLoop].szWMMouseMsg,"WM_RBU\0") == 0)
					iMessage = 9;
				else if (strcmp(WMMouseMsg[iLoop].szWMMouseMsg,"WM_RBK\0") == 0)
					iMessage = 10;

				// 移动到参数栈的下一个节点,x坐标
				pCommandNode = pCommandNode->pNext;
				iX = atof(pCommandNode->Command.szElement);

				// 移动到参数栈的下一个节点,y坐标
				pCommandNode = pCommandNode->pNext;
				iY = atof(pCommandNode->Command.szElement);

				// 移动到参数栈的下一个节点,辅助键
				pCommandNode = pCommandNode->pNext;
				keyFlags = atoi(pCommandNode->Command.szElement);

				// 退出循环
				break;
			}
		}

		// 如果有鼠标消息则对鼠标消息进行处理
		if (fWMMouseMsg)
		{
			// 得到坐标的范围因子
			iScaleX = 65535.0 / (iWidth - 1);
			iScaleY = 65535.0 / (iHeight - 1);
			// 对坐标进行比例缩放
			iX *= iScaleX;
			iY *= iScaleY;
			// 转换成DWORDS
			dwX = (DWORD)iX;
			dwY = (DWORD)iY;

			// 处理鼠标消息
			if (iMessage == 1) 
			{
				mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_MOVE,dwX,dwY,0,0);
			}
			else if (iMessage == 2) 
			{
				mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_LEFTDOWN,dwX,dwY,0,0);
			}
			else if (iMessage == 3) 
			{
				mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_LEFTUP,dwX,dwY,0,0);
			}
			else if (iMessage == 4) 
			{
				mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_LEFTDOWN,dwX,dwY,0,0);
				mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_LEFTUP,dwX,dwY,0,0);
				mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_LEFTDOWN,dwX,dwY,0,0);
				mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_LEFTUP,dwX,dwY,0,0);
			}
			else if (iMessage == 5) 
			{
				mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_MIDDLEDOWN,dwX,dwY,0,0);
			}
			else if (iMessage == 6) 
			{
				mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_MIDDLEUP,dwX,dwY,0,0);
			}
			else if (iMessage == 7) 
			{
				mouse_event(MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_MIDDLEDOWN,dwX,dwY,0,0);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -