远程共享显示及声音的实现

远程共享显示及声音的实现
马文骞
--------------------------------------------------------------------------------

局域网内共享调制解调器以及共享打印机都是我们非常熟悉的,而对于显示器和声卡的共享一般比较陌生。

  当你在进行教学、演示或展示时,也许你希望主控电脑上的画面同时也出现在其它电脑上;当你在跟踪调试程序时,你也许梦想过两台显示器能够同步,以便观看源代码时不破坏运行程序画面;还有,出于管理的目的,或许你需要远程监控其它电脑的运行状况,等等。上述所有情况都是远程共享显示的例子。

  共享声卡的需求也不少:首先这可以降低硬件投资,让没有装或者没法装声卡的电脑(如某些笔记本电脑)也有了“喉舌”;其次,即使每台机器都有声卡也不无用武之地:至少你的 mp3背景音乐与你的英语有声软件不再经常发生冲突了;还有,共享声卡使得“远程有声通知”成为可能,其作用类似于立即型声音邮件,在日常工作中有广泛的应用。

  共享显示器或声卡的硬件(一般在扩展槽内插一块卡)在市场上不难找到。但扩充本文的示例程序即可以用软件轻松实现这些功能。

  一、共享显示

  我们通过一个完整的示例程序(VB 6.0)来说明。在例子中,被共享端(即服务器端)的任务是:自动捕获本机的当前显示画面,并将之传给共享端(即客户端)。

  1. 服务器端程序(frmServer.frm)

  这里有三点需要重点说明:一是自动捕获画面问题。为了模拟“捕获屏幕键”被按下的动作,程序里使用了 API调用 keybd_event。虽然VB的SendKeys语句也有类似功能,但它不如 keybd_event稳定和可靠。二是画面粘贴和压缩存储问题。为了能把已经被捕获到系统剪贴板中的图像粘贴下来并存储到文件,程序里使用了 ImgEdit控件。该控件强大的功能远非PictureBox控件或 Image控件可比。ImgEdit 不仅支持多种压缩图像格式(如 JPG、TIFF等),而且它对画面进行操纵和编辑的功能也非常强大(如图像旋转、缩放、嵌入等)。ImgEdit 还能对剪贴板进行Copy、Cut、Paste等操作。使用 Ctrl+T 或菜单(工程->部件)来添加 Windows标准的 ImgEdit控件(参见图一)。三是文件传输问题。用 ImgEdit存储的压缩文件一般只有 40 KB左右,使用 Winsock控件可以一次传输出去。但由于接收方的 Winsock控件一般是4K至8K调用一次 DataArrival子程,故程序使用主动分块进行传输,接收方确认后再发下一块。

服务器端源程序如下:

注释:====================== frmServer.frm

Option Explicit
Const FileName = "C:sys1.tmp", BlockSize = 3072 注释: 传送包大小
Private Declare Sub keybd_event Lib "user32" _
(ByVal bVk As Byte, ByVal bScan As Byte, _
ByVal dwFlags As Long, ByVal dwExtraInfo As Long)


Private Sub Form_Load()
tcpServer.LocalPort = 1001 注释: 设置监听端口号
tcpServer.Listen 注释: 开始监听
End Sub


Private Sub tcpServer_ConnectionRequest(ByVal requestID As Long)
If tcpServer.State <> sckClosed Then tcpServer.Close
tcpServer.Accept requestID
tcpServer.SendData "SH" 注释: 成功连接后,发送“握手”信息
End Sub


Private Sub tcpServer_DataArrival(ByVal bytesTotal As Long)
Static FileID As Integer, Cur_Pos As Long, FileLen As Long
Dim strData As String, j
Dim Buf() As Byte 注释: 定义一个可变大小的数组,用于传送二进制图像包
tcpServer.GetData strData
Select Case strData
Case "Close" 注释: 接到“Disconnect”命令后,关闭当前连接,并继续监听
tcpServer.Close
ImgEdit1.ClearDisplay
tcpServer.LocalPort = 1001
tcpServer.Listen
Case "Save Picture"
Call keybd_event(vbKeySnapshot, 1, 0, 0) 注释: 模拟按键操作
j = DoEvents()
If Dir$(FileName) <> "" Then Kill FileName
If ImgEdit1.IsClipboardDataAvailable Then 注释: 当剪贴板上有数据时
ImgEdit1.ClearDisplay
ImgEdit1.DisplayBlankImage Screen.Width / _
Screen.TwipsPerPixelX, Screen.Height / _
Screen.TwipsPerPixelY, , , 6
ImgEdit1.ClipboardPaste 注释: 从剪贴板粘贴图像
ImgEdit1.BurnInAnnotations 0, 2
ImgEdit1.SaveAs FileName, 1, 6, 6, 256 注释: 另存图像。参数说明如下:
注释: “FileName”:文件名
注释: 参数“1”:TIFF 型文件;
注释: 第一个“6”:RGB24类型;
注释: 第二个“6”:JPEG压缩类型
注释: 参数“256”:最大压缩比
Clipboard.Clear
tcpServer.SendData "PS" 注释: 发送“图像文件就绪”信息
End If
Case "Get Picture"
If Dir$(FileName) <> "" Then
FileID = FreeFile
Open FileName For Binary As #FileID 注释: 打开文件并发送第一块数据
FileLen = LOF(FileID)
ReDim Buf(1 To BlockSize) As Byte
Get #FileID, , Buf
tcpServer.SendData Buf
Cur_Pos = BlockSize
End If
Case "Next Block"
If Cur_Pos = FileLen Then
tcpServer.SendData "EF" 注释: 文件传送完毕后,发送“完成”信息
Close FileID
Exit Sub
End If
j = Cur_Pos + BlockSize
If j > FileLen Then
j = FileLen - Cur_Pos
Else
j = BlockSize
End If
ReDim Buf(1 To j) As Byte 注释: 动态确定数组大小
Get #FileID, , Buf
tcpServer.SendData Buf 注释: 发送后续包
Cur_Pos = Cur_Pos + j
End Select
End Sub

2. 客户端程序(frmClient.frm)

  在窗体上建六个控件:一个名为 tcpClient的 Winsock控件用于通讯;一个名为txtIP 的 TextBox控件用于填写服务器的IP地址;一个名为ImgEdit1的 ImgEdit控件用于显示服务器传来的图像;三个CommandButton控件( cmdConnect、cmdGet_Pic和cmdDisconnect) 分别用于执行连接、取回图像和断开连接(见图二)。

客户端源代码如下:

注释:====================== frmClient.frm

Option Explicit
Const FileName = "C:sys1.tmp"
Private Sub cmdConnect_Click()
If tcpClient.State <> sckClosed Then tcpClient.Close
tcpClient.RemoteHost = txtIP.Text
tcpClient.RemotePort = 1001
tcpClient.Connect 注释: 进行连接
End Sub


Private Sub cmdDisconnect_Click()
tcpClient.SendData "Close" 注释: 断开连接
cmdConnect.Enabled = True
cmdGet_Pic.Enabled = False
cmdDisconnect.Enabled = False
End Sub


Private Sub cmdGet_Pic_Click()
tcpClient.SendData "Save Picture" 注释: 请求图像返回
frmClient.MousePointer = 11
End Sub


Private Sub Form_Resize() 注释: 使 ImgEdit1 的大小随窗体的变化而变化
ImgEdit1.Height = frmClient.Height - 825
ImgEdit1.Width = frmClient.Width - 225
End Sub


Private Sub tcpClient_DataArrival(ByVal bytesTotal As Long)
Static FileID As Integer, FileLen As Long
Dim Buf() As Byte
Dim j As Integer
ReDim Buf(bytesTotal) As Byte 注释: 根据到达数据的字节数确定接收数组的大小
tcpClient.GetData Buf
注释: 收到连接完成的“握手”信息
If bytesTotal = 2 And Chr(Buf(0)) = "S" And Chr(Buf(1)) = "H" Then
cmdConnect.Enabled = False
cmdGet_Pic.Enabled = True
cmdDisconnect.Enabled = True
Exit Sub
End If
注释: 收到图像就绪的信息
If bytesTotal = 2 And Chr(Buf(0)) = "P" And Chr(Buf(1)) = "S" Then
If Dir$(FileName) <> "" Then Kill FileName
FileID = FreeFile
Open FileName For Binary As #FileID 注释: 打开文件,准备存储图像
FileLen = 0
tcpClient.SendData "Get Picture"
Exit Sub
End If
注释: 收到图像发送完毕的信息
If bytesTotal = 2 And Chr(Buf(0)) = "E" And Chr(Buf(1)) = "F" Then
Close #FileID 注释: 关闭文件
j = DoEvents()
ImgEdit1.Image = FileName
ImgEdit1.Display 注释: 显示收到的图像
ImgEdit1.BurnInAnnotations 0, 2
frmClient.MousePointer = 0
Exit Sub
End If
注释: 收到一块二进制图像信息
Put #FileID, , Buf 注释: 将当前数据块存盘
tcpClient.SendData "Next Block" 注释: 申请下一块
FileLen = FileLen + bytesTotal
frmClient.Caption = "TCP Client " + Trim(Str(FileLen)) + _
" Bytes Received." 注释: 显示当前收到的字节数
End Sub


客户端成功共享服务器端显示画面后的外观如图三所示。


  二、共享声音

  共享声音与共享显示的思想是一致的,只是这时是客户端向服务器端发送声音文件,以便共享服务器的声卡。服务器端应使用微软的多媒体控件(MMControl) 进行声音播放(使用 Ctrl+T 或菜单“工程->部件”来添加)。用该控件播放声音不仅是简单的,而且功能强大。

  由于关键模块与共享显示一致,故此处略去源代码。
posted @ 2007-01-12 15:01  TangHuawei  阅读(792)  评论(0编辑  收藏  举报