对等网络图像传输软件设计(四)
__asm
{
MOV ECX,dwLen1 //将位图数据长度赋给32位寄存器ECX
SUB ECX,dwBitMapHeader1 //减去位图头部数据长度
SHR ECX,2 //计数器除以4,判断时以双字为单位
MOV EDX,dwBitMapHeader1 //将位图头部数据长度赋给32位寄存器EDX
MOV ESI,pDIBitmap1 //将位图数据存放地址赋给源变址寄存器
ADD ESI,EDX //移动指针跳过位图头部
MOV EDI,pDIB1 //将位图数据存放地址赋给目的变址寄存器
ADD EDI,EDX //移动指针跳过位图头部
REP CMPSD //重复比较,每次比较一个双字的数据
JNZ SetFlagRegion1 //不为0时,跳转到SetFlagRegion1,设置变量
MOV fChange1,FALSE //给标志标量赋FALSE,表示未变化
JMP ExitRegion1 //跳转到ExitRegion1,执行退出
SetFlagRegion1:
MOV fChange1,TRUE //给标志标量赋TRUE,表示数据已变化
ExitRegion1:
}
//设置变化状态
pGdiNode->Gdi.fChange = fChange1; //设置位图改变标志
4.3.4 数据压缩模块设计
在软件的图像压缩模块设计时,基于屏幕图像的特征分析和压缩编码的特点,选取了霍夫曼压缩编码和游程编码两种编码方式,从而提高数据压缩的综合效果。RLE压缩编码适用于计算机生成的图像,对减少图像文件的存储空间非常有效。图像中具有相同颜色的图像块越大,图像块数目越少,获得的压缩比也就越高。而屏幕图像一般情况下,拥有大片相同的背景颜色,使用RLE非常合适,为了补救RLE编码的不足,需要和其他的压缩编码技术联合应用。因为串长度并非等概率分布,所以在实现数据压缩时,配合霍夫曼编码压缩方法。实现代码如下:
pTempDIB = (char *)malloc(pGdiNode->Gdi.dwCompress * 3 + 4);
pGdiNode->Gdi.pDIBCompress = (char *)malloc(pGdiNode->Gdi.dwCompress * 3 + 4);
// Run Length 编码图像
dwCompLen= RunLengthEncode(pGdiNode->Gdi.pDIBChange,
pGdiNode->Gdi.dwCompress,pTempDIB);
// 生成霍夫曼字节树的字典
wTreeSize=HuffmanDictionary(pTempDIB,dwCompLen,&dwByteTree[0],&dwCodes[0]);
// 使用霍夫曼压缩+Run Length编码的图像
dwSendLen=HuffmanCompress(pTempDIB,dwCompLen,wTreeSize,&dwByteTree[0],&dwCodes[0],pGdiNode->Gdi.pDIBCompress);
4.3.5 信息模块设计
消息处理模块是远程控制软件中的不可缺少的重要组成部分,实现软件的远程控制功能原理类似于Windows程序设计采用消息响应和处理的模式。
服务器与客户机的相互通信,是通过发送固定格式的信息完成的。客户端发送消息,服务器端接收并响应消息。服务器端在接收到了客户端发来的消息之后,对消息的格式进行判断,分析是常规消息还是鼠标键盘的消息,将消息解析后,发送到各自的消息队列中,等待响应。程序设计时服务器端有专门的解析函数来处理。常规的消息都带有固定的格式头,如刷新消息是以REFRESH 开头的字符串。断开连接消息是以DISCONNECT为固定的头部。在服务器端接收到这样的消息之后,可以立即判断并响应执行消息。还有一些特殊消息,如鼠标、键盘的消息,在实现对远程计算机的控制时,必不可少,其发送的数据量不是很大,但是发送和接收的频率较高。程序使用消息队列存储接收到的消息,另外除了鼠标、键盘消息的动作消息外还需传输一些其他信息,如鼠标当前的坐标位置信息以及键盘的特殊键被按下等信息等。它们都作为消息的重要参数进行传输,服务端通过调用鼠标处理事件和键盘处理事件执行。客户端发送的其他一些消息如表4.1所示。
表4.1 客户端发出的请求列表
请求 含义 请求 含义
CONNECT 建立双方通信连接 UNLOCK 对计算机解锁
DISCONNECT 断开双方通信连接 WM_KD 键盘按键被按下
RESOLUTION 发送当前分辨率 WM_KU 键盘按键被弹起
GIRD 发送网格划分方案 WM_MM 鼠标移动
REFERSH 请求刷新页面 WM_LBD 鼠标左键按下
MESSAGE 发送的文字消息 WM_LBU 鼠标左键弹起
VIEW 视图方式访问 WM_LBK 鼠标左键双击
REBOOT 重起计算机 WM_RBD 鼠标右键按下
LOGOFF 注销计算机 WM_RBU 鼠标右键弹起
SHUTDOWN 关闭计算机 WM_RBK 鼠标右键双击
LOCK 锁定计算机
4.4客户端模块设计
4.4.1 屏幕图像显示模块设计
客户端接收服务器端发来的图像数据,首先对图像进行解压缩,还原为原来的图像数据。在内存环境中创建兼容的设备描述表,将所有的位图数据在内存中整合为一个完整的图片,用于在客户端屏幕显示。屏幕图像显示流程如图4.5所示。
具体在软件实现中,循环遍历整个结点链表,在内存设备环境中将一个一个的网格图像数据拼合成完整的屏幕图像,实现图像的分割-传输-拼合-显示的过程。
while (pNode)
{
if (pNode->Gdi.fDIBitmap)
{ //将设备无关位图绘制到设备相关位图中(DIB->DDB),用于屏幕显示
StretchDIBits(m_hMemDC,pNode->Gdi.iWidth1,pNode->Gdi.iHeight1, pNode->Gdi.lpBitmapIH->biWidth,pNode->Gdi.lpBitmapIH->biHeight,0,0,
pNode->Gdi.lpBitmapIH->biWidth,pNode->Gdi.lpBitmapIH->biHeight, (LPBYTE)pNode->Gdi.lpBitmapIH+(pNode->Gdi.lpBitmapIH->biSize+(1<< pNode->Gdi.lpBitmapIH->biBitCount) * sizeof(RGBQUAD)), (LPBITMAPINFO)pNode->Gdi.lpBitmapIH,DIB_RGB_COLORS,SRCCOPY);
}
pNode = pNode->pNext;//移动指针到下一个结点位置
}
BitBlt(hDC,0,0,iScreenWidth,iScreenHeight,m_hMemDC,iHScrollPos,-iVScrollPos
,SRCCOPY); //将内存设备环境的位图拷贝到窗口,用于显示
4.4.2 通信模块设计
客户端的套接字的创建和连接相对于服务器端来说较为简单。如图4.3所示,只需要创建套接字,然后根据等到的IP地址连接目标计算机,从而请求连接该计算机。在服务器端接受了连接请求后,两台计算机之间便可以相互通信发送数据信息。创建Socket的过程以及连接目标计算机的代码如下:
//创建一个流式套接口
sClient = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_IP,NULL,0,SOCK_STREAM);
//初始化IP地址结构
server.sin_family = AF_INET;//定义协议簇
server.sin_port = htons(port);//使用约定的端口号
server.sin_addr.s_addr = inet_addr(szIP);//获取连接服务器的IP地址
//连接服务器端
if (connect(sClient,(struct sockaddr *)&server,sizeof(server)) == SOCKET_ERROR)
{
memset(szString,'\0',sizeof(szString));
sprintf(szString,"Connect() failed: %d",WSAGetLastError());
//显示最近的错误信息
MessageBox(NULL,szString,"Client Error",MB_OK);
return 1;
}
4.4.3 信息模块设计
当客户端程序与服务器端程序建立了连接后,得到服务器端计算机的当前屏幕,在本机程序窗口中显示。当鼠标在显示窗口出现时,客户端程序会根据当前鼠标的坐标位置换算出在远程计算机屏幕上的坐标,并将鼠标的一举一动记录下来,如鼠标单击、双击或者移动等动作,并把消息发送到远程计算机。服务器端程序根据接收来得鼠标位置信息,结合鼠标位置等参数信息,然后响应并执行消息,实现远程控制。举例实现代码如下。
//获取鼠标双击消息,并将鼠标消息、位置参数和标志信息一起发送出去
void CMainWnd::OnLButtonDblClk(HWND hWnd,BOOL fDoubleClick,int x,int y,UINT keyFlags)
{ if (fConnected)
{ x += iHScrollPos;
y += (-1 * iVScrollPos);
memset(szMouseMessage,'\0',sizeof(szMouseMessage));
sprintf(szMouseMessage,"WM_LBK;%d;%d;%d;0;\0",x,y,keyFlags);
SendCommand(m_hWnd,1,szMouseMessage);//发送消息函数
}
}
该段代码实现的功能是获取鼠标左键双击的消息,获取鼠标的当前位置以及是否有功能键。程序将信息以字符串"WM_LBK;%d;%d;%d;0;\0"的方式传输给服务器端来响应。鼠标其它功能如左键单击、右键单击和鼠标移动等动作,其实现原理也基本相同。
键盘的操作只有两种操作,即键盘按键被按下和按键弹起,实现代码如下:
// 处理键按下的消息
void CMainWnd::OnKeyDown(HWND hWnd,UINT vk,BOOL fDown,int cRepeat,UINT flags)
{ if (fConnected)
{ memset(szMouseMessage,'\0',sizeof(szMouseMessage));
sprintf(szMouseMessage,"WM_KD;%d;%d;%d;%d;\0",vk,fDown,cRepeat,flags);
SendCommand(m_hWnd,1,szMouseMessage);
}
}
// 处理键弹起的消息
void CMainWnd::OnKeyUp(HWND hWnd,UINT vk,BOOL fDown,int cRepeat,UINT flags)
{ if (fConnected)
{ memset(szMouseMessage,'\0',sizeof(szMouseMessage));
sprintf(szMouseMessage,"WM_KU;%d;%d;%d;%d;\0",vk,fDown,cRepeat,flags);
SendCommand(m_hWnd,1,szMouseMessage);
}
}
4.4.4 图像保存模块设计
对等网络图像传输软件设计(四)由毕业论文网(www.huoyuandh.com)会员上传。