只要开始,永远不晚;只要前进,总有空间 - 草上爬的博客

.Net 编程技术学习与分享 http://www.Rithia.net
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

利用VB.Net编程实现PC与掌上电脑PPC间的双向通信

Posted on 2007-10-28 09:43  douruixin  阅读(2633)  评论(2编辑  收藏  举报
    [本文已于06-4-12在天极发表]

[源文件下载]

本文介绍如何利用VB.Net 通过Windows Sockets (Winsock)以及多线程编程进行桌面电脑与Pocket PC(掌上电脑,简称:PPC)间的双向通信,并且通过当今流行的.net平台编程语言Visaul Basic.Net 2003编写PC端和PPC端程序实现两者的信息交换。

一、概述

如今移动计算设备越来越普及,而PPC与桌面电脑的数据交换更是其必不可少的功能。笔者根据实际的无线PPC开发认为,PPC程序与桌面程序进行通信,除了可以通过SQL Server CE提供的RDA(远程数据存取)和Replication(复制)来完成与桌面SQL Server的数据存取外,另外一种比较方便快捷的方法则是通过Socket与桌面电脑完成信息的交换。

采用Visaul Basic.Net 2003进行PPC和桌面电脑的程序编写,VB.Net已经完全不同于过去的VB,它的诸多新特性以及依托.Net平台强大的面向对象体系使VB.Net焕然一新,不仅编写桌面程序更加轻松快捷而且更可快速的、可视化的开发以Pocket PC为平台的智能设备程序。

.Net框架是一种新的计算平台,可以简单理解为组件库或者类库,类似于MFCVCL,但是却比它们更加强大和易用。.NET框架具有两个主要组件:公共语言运行库和 .NET 框架类库。公共语言运行库是 .NET 框架的基础,可以将运行库看作一个在执行时管理代码的代理,它提供核心服务(如内存管理、线程管理和远程处理等)。以运行库为目标的代码称为托管代码,而不以运行库为目标的代码称为非托管代码。.NET 框架类库是一个与公共语言运行库紧密集成的可重用的类型集合,该类库是面向对象的,.NET 框架类库能够完成一系列常见编程任务,包括诸如字符串管理、数据收集、数据库连接以及文件访问等任务。

.Net框架精简版是完整.Net框架的子集。它是对完整的 .Net框架进行精简后得到的版本,虽然其规模大大减小,但多数功能仍然保持完整。使用.NET框架精简版可以针对Pocket PC和其他Windows CE .NET设备进行开发和部署,提高开发人员的工作效率

VB.Net编译的桌面程序在目标机器上需要.Net框架的支持才能运行,而编译过的PPC程序需要在目标PPC上装载.Net框架精简版后方可运行。

二、设计思路

.Net框架中System.Net.Sockets命名空间为需要严密控制网络访问的开发提供了 Windows Sockets (Winsock) 接口的托管实现,该命名空间中包含了与Socket相关的类、接口和枚举,主要包括实现 Berkeley 套接字接口的Socket类、用于 TCP 网络客户端侦听连接的TcpListener类、为 TCP 网络服务提供客户端连接的TcpClient类以及提供无连接的发送和接收用户数据文报 (UDP) 网络服务等。该命名空间下的大部分类都受.Net框架精简版的支持,也就是说实现这些对象的代码完全可以在装载有.Net框架精简版的PPC上运行。

    可以把PPC当作局域网中的一个终端来与桌面电脑连接,它们之间有多种物理连接方式,通常可以使用USB线缆配合ActiveSync直接与桌面电脑连接,当然,若PPC和桌面电脑同时具备其它的连接方式,比如无线WiFiIEEE 802.1x)、蓝牙等都可以组成相应的无线局域网络。

PPC上编写客户端程序并实现TcpClient类,用于连接、发送和接收流数据。在桌面电脑上我们设计Socket服务端程序并实现TcpListener类和Socket类,用于侦听和接受传入连接的请求。Socket类为网络通信提供了一套非常丰富的方法和属性,服务程序可以使用 Listen 方法侦听连接。Accept 方法处理任何传入的连接请求,并返回可用于与远程主机进行数据通信的 Socket如果当前使用的是无连接协议(如 UDP),则根本不需要侦听连接。实际上TcpClient类和TcpListener类为 Socket通信提供了比Socket类更简单、对用户更友好的接口,如果编写较简单的应用程序,而且只需同步数据传输,则可以考虑使用 TcpClientTcpListener UdpClient
Listener = New TcpListener(System.Net.IPAddress.Parse("192.200.0.100"),"10200")
Listener.Start()
Label1.Text 
= "正在 192.200.0.100:10200 处侦听.."

一、程序的具体实现

1、创建PPC客户端程序

启动Visual Studio .Net 2003,新建Visaul Basic项目,并选择“智能设备应用程序”,选择“Pocket PC”平台的“Windows 应用程序”并创建项目。Pocket PC平台是Windows CE 平台的子集,Windows CE系统的应用更加广泛。项目创建完后,Visaul Studio.Net会创建一个默认的主窗体Form1,在窗体上分别放置两个Panel控件、四个Label控件、三个TextBox控件和两个Button控件。



设计PPC程序的Form窗体与桌面程序没有太多差别,只是少了一些PPC上所不支持的属性,但是属性的名称并没有变化。在PPC上没有窗口最小化的实际意义,主窗体的MinimizeBox属性必须设置成False,否则,当需要退出程序点击标题栏最右侧的关闭按钮时,程序并没有真正退出而是隐藏在后台,程序需要重新激活才能回到前台。或者可以向一个Button添加关闭主窗体的代码:


Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles
 Button3.Click
        
Me.Close()
End Sub

Me VB.Net中的关键字,可以引用当前在其中执行代码的类或结构的特定实例。Me 的行为与引用当前实例的对象变量或结构变量类似。此例Me代表主窗体的实例Form1使 用TcpClientTcpListener Socket 侦听器连接并交换数据,可以使用下面两种方法之一连接到侦听器:

1)创建一个 TcpClient,并调用三个可用的 Connect 方法之一。

2)使用远程主机的主机名和端口号创建 TcpClient。此构造函数将自动尝试一个连接。

TcpClientConnect 方法使用指定的主机名和端口号将客户端连接到 TCP 主机,重载函数原型为:

使用指定的远程网络终结点将客户端连接到远程 TCP 主机。

Overloads Public Sub Connect(IPEndPoint)

使用指定的 IP 地址和端口号将客户端连接到 TCP 主机。

 

Overloads Public Sub Connect(IPAddress, Integer)

将客户端连接到指定主机上的指定端口。

 

Overloads Public Sub Connect(String, Integer)

我们使用指定的主机名称和端口号将客户端连接到 TCP 主机,在堆栈中创建TcpClient对象的实例,当执行退出当前过程或函数时,堆栈中的资源会自动清除,保证内存空间被正确释放。连接成功后要发送和接收数据,使用 GetStream 方法来获取一个 NetworkStreamNetworkStream为基础数据流,调用 NetworkStream Write Read 方法与远程主机之间发送和接收数据。当发送操作结束后使用 TcpClientClose 方法断开连接、关闭对象并释放与 TcpClient 关联的所有资源。

双击“连接”按钮,编写连接代码:
 ‘创建TcpClient对象的实例
  Client 
= New TcpClient
  ‘通过计算机名称和端口号连接到指定的计算机
  Client.Connect(“
192.200.0.100”,10200)
SendData 
=”由PPC传递过来的数据 – Author:Risen”
  ‘按一定的编码规则对要传递的数据进行编码
  Buffer 
= Encoding.UTF8.GetBytes(SendData)
  ‘向已连接的服务程序发送数据
  Client.GetStream().Write(Buffer, 
0, Buffer.Length)
‘DataAvailable 指示NetworkStream 上是否有可用的数据。如果可以在流上读取数据,则为 
true
‘否则为 
false,只要有数据存在就等待传输完毕
While Not Client.GetStream.DataAvailable()
    Application.DoEvents()
  
End While
  ‘接收由服务程序传递回客户端的数据并显示在对话框上
  
If Client.GetStream.DataAvailable() Then
     Client.GetStream().Read(InBuff, 
0, InBuff.Length)
     rtndata 
= "服务程序已经成功收到指令,在" & System.Text.Encoding.Default.GetString(InBuff, 0, InBuff.Length)
     
MsgBox(rtndata)
  
End If
  ‘断开连接、关闭对象并释放与 TcpClient 关联的所有资源
Client.Close()
正确编译上述代码还需要在源文件的头部加入使用命名空间的引用,才能正确使用命名空间中的所有枚举、结构、类或模块等。
Imports System.Net.Sockets ‘<-添加需要导入的命名空间
Imports System.Text  ‘<-添加需要导入的命名空间

Public Class Form

跟踪调试PPC程序需要在适当的模拟器下进行,譬如系统默认的PocketPC2002 模拟器,程序调试无误后可以连接到实际的PocketPC设备运行。实际部署到PPC设备需要在连接的桌面电脑上安装Microsoft ActiveSync将程序传输到用户目录中。

2、创建桌面服务端程序

启动Visual Studio .Net 2003,新建Visaul Basic项目,并选择“Windows应用程序”创建项目。项目创建完后,Visaul Studio.Net会创建一个默认的主窗体Form1,在窗体上分别放置三个Label控件、两个TextBox控件、一个列表框控件和一个Button控件。

创建全局的TcpListener对象的实例来侦听特定的端口,代码片段如下:
‘创建在本机IP地址和端口侦听的TcpListener对象的实例
Listener 
= New TcpListener(“192.200.0.100”, 10200)
'开始侦听
Listener.Start()
‘侦听状态标志
Listening 
= True
Label1.Text 
= "正在 192.200.0.100:10200 处侦听.."
通过计时器定时检测侦听器有无连接请求以及多线程来处理接收到的数据:
'声明创建线程时,使用 ThreadStart 委托作为其唯一参数的构造函数创建 Thread 类的新实例
Dim CurThreadStart As ThreadStart
Dim CurThread As Thread

'检测侦听器是否有挂起的连接请求,没有则退出计时器事件
If Not Listener.Pending() Then
  
Exit Sub
End If
'有连接的请求则将计时器暂停,准备进行处理
tmProcessRequest.Enabled = False
'创建线程委托,传递需要操作的过程的地址
CurThreadStart = New ThreadStart(AddressOf ProcessRequest)
CurThread 
= New Thread(CurThreadStart)
'开始一个线程
CurThread.Start()
'重新启动计时器
tmProcessRequest.Enabled = True

将计时器触发事件的频率Interval(以毫秒为单位)适当调节可以改变服务程序响应连接请求的快慢。

计时器不断循环,一旦发现有连接的请求就会创建一个线程专门来处理这个连接,创建线程时需要传递处理连接的过程或函数的地址以被线程调用。

服务程序通过已连接的Socket获取数据,接收到的数据按相应的编码规则进行解码并将信息添加到列表框中:
CurThread = System.Threading.Thread.CurrentThread()
‘接受挂起的连接请求并返回可用来发送和接收数据的Socket
CurSocket 
= Listener.AcceptSocket

While Listening
If CurSocket.Available > 0 Then
  ‘接收来自绑定的Socket的数据
Bytes 
= CurSocket.Receive(Buffer, Buffer.Length, 0)
‘将接收到的数据转化成相应编码的字符串
ReceivedData
= Encoding.UTF8.GetString(Buffer)
SyncLock CurThread
       lbReceived.Items.Add(ReceivedData)
End SyncLock

TCPListener.AcceptSocket接受挂起的连接请求,并且返回用于发送和接收数据的 Socket Socket 继续用于和新连接上的客户端通信。

SyncLock 语句确保多个线程不会同时执行向列表框添加数据的操作。当线程到达 SyncLock 块时,将计算表达式并保持此互斥性,直到在表达式返回的对象上有锁为止。这防止了表达式在多个线程运行期间更改值,从而避免代码产生意外的结果。

向客户端返回服务端处理后的消息,代码片段如下:
'格式化返回消息 - 这是服务器端处理后的结果并返回到客户端。
BacktoClientData = System.DateTime.Now ‘向客户端传递服务端程序处理时的时间
Buffer 
= Encoding.UTF8.GetBytes(BacktoClientData.ToCharArray)
'通过打开的套接字将结果发送回客户端应用程序,然后关闭该套接字。
CurSocket.Send(Buffer, Buffer.Length, 0)
CurSocket.Close()
编译桌面服务端程序运行效果如下图所示:


当服务程序处理完一个连接后就向相应的PPC客户端返回一条成功信息,如下图所示:

至此,PPC客户端程序和桌面服务端程序编写完毕,该系统在安装WindowsXP SP2的桌面电脑和Dell X3i PPC上测试通过。

一、结论

本程序简单介绍了PCPPC之间Socket通信方法,在实际应用中还有很多细节要做,并且还有可以拓展的地方。该模型为实现PCPPC间复杂的信息交互,创建复杂的无线信息系统提供了一定技术基础。

[源文件下载]