//服务器端:发送本机桌面到连接的客户端
#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(WMMouseM