本文介绍如何利用 通过Windows Sockets (Winsock)以及多线程编程进行桌面电脑与Pocket PC(掌上电脑
简称
PPC)间的双向通信
并且通过当今流行平台编程语言Visaul Basic
Net
编写PC端和PPC端程序实现两者的信息交换
一概述
如今移动计算设备越来越普及而PPC与桌面电脑的数据交换更是其必不可少的功能笔者根据实际的无线PPC开发认为PPC程序与桌面程序进行通信除了可以通过SQL Server CE提供的RDA(远程数据存取)和Replication(复制)来完成与桌面SQL Server的数据存取外另外一种比较方便快捷的方法则是通过Socket与桌面电脑完成信息的交换
采用Visaul BasicNet 进行PPC和桌面电脑的程序编写已经完全不同于过去的VB它的诸多新特性以及依托Net平台强大的面向对象体系使VBNet焕然一新不仅编写桌面程序更加轻松快捷而且更可快速的可视化的开发以Pocket PC为平台的智能设备程序
Net框架是一种新的计算平台可以简单理解为组件库或者类库类似于MFC和VCL但是却比它们更加强大和易用NET框架具有两个主要组件公共语言运行库和 NET 框架类库公共语言运行库是 NET 框架的基础可以将运行库看作一个在执行时管理代码的代理它提供核心服务(如内存管理线程管理和远程处理等)以运行库为目标的代码称为托管代码而不以运行库为目标的代码称为非托管代码NET 框架类库是一个与公共语言运行库紧密集成的可重用的类型集合该类库是面向对象的NET 框架类库能够完成一系列常见编程任务包括诸如字符串管理数据收集数据库连接以及文件访问等任务
Net框架精简版是完整Net框架的子集它是对完整的 Net框架进行精简后得到的版本虽然其规模大大减小但多数功能仍然保持完整使用NET框架精简版可以针对Pocket PC和其他Windows CE NET设备进行开发和部署提高开发人员的工作效率 VBNet编译的桌面程序在目标机器上需要Net框架的支持才能运行而编译过的PPC程序需要在目标PPC上装载Net框架精简版后方可运行
二设计思路
在Net框架中SystemNetSockets命名空间为需要严密控制网络访问的开发提供了 Windows Sockets (Winsock) 接口的托管实现该命名空间中包含了与Socket相关的类接口和枚举主要包括实现 Berkeley 套接字接口的Socket类用于 TCP 网络客户端侦听连接的TcpListener类为 TCP 网络服务提供客户端连接的TcpClient类以及提供无连接的发送和接收用户数据文报 (UDP) 的网络服务等该命名空间下的大部分类都受Net框架精简版的支持也就是说实现这些对象的代码完全可以在装载有Net框架精简版的PPC上运行
可以把PPC当作局域网中的一个终端来与桌面电脑连接它们之间有多种物理连接方式通常可以使用USB线缆配合ActiveSync直接与桌面电脑连接当然若PPC和桌面电脑同时具备其它的连接方式比如无线WiFi(无线x)蓝牙等都可以组成相应的无线局域网络
在PPC上编写客户端程序并实现TcpClient类用于连接发送和接收流数据在桌面电脑上我们设计Socket服务端程序并实现TcpListener类和Socket类用于侦听和接受传入连接的请求Socket类为网络通信提供了一套非常丰富的方法和属性服务程序可以使用 TcpListenerListen 方法侦听连接Accept 方法处理任何传入的连接请求并返回可用于与远程主机进行数据通信的Socket如果当前使用的是无连接协议(如UDP)则根本不需要侦听连接实际上TcpClient类和TcpListener类为 Socket通信提供了比Socket类更简单对用户更友好的接口如果编写较简单的应用程序而且只需同步数据传输则可以考虑使用 TcpClientTcpListener 和 UdpClient
……
Listener = New TcpListener(SystemNetIPAddressParse())
ListenerStart()
LabelText = 正在 : 处侦听
……
三程序的具体实现
创建PPC客户端程序
启动Visual Studio Net 新建Visaul Basic项目并选择智能设备应用程序选择Pocket PC平台的Windows 应用程序并创建项目Pocket PC平台是Windows CE 平台的子集Windows CE系统的应用则更加广泛项目创建完后Visaul StudioNet会创建一个默认的主窗体Form在窗体上分别放置两个Panel控件四个Label控件三个TextBox控件和两个Button控件
PPC客户端程序界面
设计PPC程序的Form窗体与桌面程序没有太多差别只是少了一些PPC上所不支持的属性但是属性的名称并没有变化在PPC上没有窗口最小化的实际意义所以主窗体的MinimizeBox属性必须设置成False否则当需要退出程序点击标题栏最右侧的关闭按钮时程序并没有真正退出而是隐藏在后台程序需要重新激活才能回到前台或者可以向一个Button添加关闭主窗体的代码
Private Sub Button_Click(ByVal sender As SystemObject ByVal e As SystemEventArgs) Handles ButtonClick
MeClose()
End Sub
Me 为VBNet中的关键字可以引用当前在其中执行代码的类或结构的特定实例Me 的行为与引用当前实例的对象变量或结构变量类似此例Me代表主窗体的实例Form使用TcpClient向TcpListener或Socket 侦听器连接并交换数据可以使用下面两种方法之一连接到侦听器
()创建一个 TcpClient并调用三个可用的 Connect 方法之一
()使用远程主机的主机名和端口号创建 TcpClient此构造函数将自动尝试一个连接
TcpClient的Connect 方法使用指定的主机名和端口号将客户端连接到 TCP 主机重载函数原型为
使用指定的远程网络终结点将客户端连接到远程 TCP 主机
Overloads Public Sub Connect(IPEndPoint)
使用指定的 IP 地址和端口号将客户端连接到 TCP 主机
Overloads Public Sub Connect(IPAddress Integer)
将客户端连接到指定主机上的指定端口
Overloads Public Sub Connect(String Integer)
我们使用指定的本地主机IP地址和端口号将客户端连接到TCP主机在堆栈中创建TcpClient对象的实例当执行退出当前过程或函数时堆栈中的资源会自动清除保证内存空间被正确释放连接成功后要发送和接收数据使用 GetStream 方法来获取一个NetworkStreamNetworkStream为基础数据流调用 NetworkStream 的 Write 和 Read 方法与远程主机之间发送和接收数据当发送操作结束后使用 TcpClient的Close 方法断开连接关闭对象并释放与 TcpClient 关联的所有资源
双击连接按钮编写连接代码代码片段如下
创建TcpClient对象的实例
Client = New TcpClient
通过计算机名称和端口号连接到指定的计算机
ClientConnect()
SendData =由PPC传递过来的数据 – AuthorRisen
按一定的编码规则对要传递的数据进行编码 Buffer = EncodingUTFGetBytes(SendData)
向已连接的服务程序发送数据
ClientGetStream()Write(Buffer BufferLength)
DataAvailable 指示NetworkStream 上是否有可用的数据如果可以在流上读取数据则为 true
否则为 false只要有数据存在就等待传输完毕
While Not ClientGetStreamDataAvailable()
ApplicationDoEvents()
End While
接收由服务程序传递回客户端的数据并显示在对话框上
If ClientGetStreamDataAvailable() Then
ClientGetStream()Read(InBuff InBuffLength)
rtndata = 服务程序已经成功收到指令在 & SystemTextEncodingDefaultGetString(InBuff InBuffLength)
MsgBox(rtndata)
End If
断开连接关闭对象并释放与 TcpClient 关联的所有资源
ClientClose()
正确编译上述代码还需要在源文件的头部加入使用命名空间的引用才能正确使用命名空间中的所有枚举结构类或模块等
Imports Sockets <添加需要导入的命名空间
Imports SystemText <添加需要导入的命名空间
Public Class Form
……
跟蹤调试PPC程序需要在适当的模拟器下进行譬如系统默认的PocketPC 模拟器程序调试无误后可以连接到实际的PocketPC设备运行实际部署到PPC设备需要在连接的桌面电脑上安装Microsoft ActiveSync将程序传输到用户目录中
创建桌面服务端程序
启动Visual Studio Net 新建Visaul Basic项目并选择Windows应用程序创建项目项目创建完后Visaul StudioNet会创建一个默认的主窗体Form在窗体上分别放置三个Label控件两个TextBox控件一个列表框控件和一个Button控件
创建全局的TcpListener对象的实例来侦听特定的端口代码片段如下
创建在本机IP地址和端口侦听的TcpListener对象的实例
Listener = New TcpListener( )
开始侦听
ListenerStart()
侦听状态标志
Listening = True
LabelText = 正在 处侦听
……
通过计时器定时检测侦听器有无连接请求并启用多线程来处理接收到的数据代码片段如下 声明创建线程时使用 ThreadStart 委托作为其唯一参数的构造函数创建 Thread 类的新实例
Dim CurThreadStart As ThreadStart
Dim CurThread As Thread
检测侦听器是否有挂起的连接请求没有则退出计时器事件
If Not ListenerPending() Then
Exit Sub
End If
有连接的请求则将计时器暂停准备进行处理
tmProcessRequestEnabled = False
创建线程委托传递需要操作的过程的地址
CurThreadStart = New ThreadStart(AddressOf ProcessRequest)
CurThread = New Thread(CurThreadStart)
开始一个线程
CurThreadStart()
重新启动计时器
tmProcessRequestEnabled = True
将计时器触发事件的频率Interval(以毫秒为单位)适当调节可以改变服务程序响应连接请求的快慢
计时器不断循环一旦发现有连接的请求就会创建一个线程专门来处理这个连接创建线程时需要传递处理连接的过程或函数的地址以被线程调用
服务程序通过已连接的Socket获取数据接收到的数据按相应的编码规则进行解码并将信息添加到列表框中
……
CurThread = SystemThreadingThreadCurrentThread()
接受挂起的连接请求并返回可用来发送和接收数据的Socket
CurSocket = ListenerAcceptSocket
While Listening
If CurSocketAvailable > Then
接收来自绑定的Socket的数据
Bytes = CurSocketReceive(Buffer BufferLength )
将接收到的数据转化成相应编码的字符串
ReceivedData= EncodingUTFGetString(Buffer)
SyncLock CurThread
lbReceivedItemsAdd(ReceivedData)
End SyncLock
……
TCPListenerAcceptSocket接受挂起的连接请求并且返回用于发送和接收数据的 Socket此 Socket 继续用于和新连接上的客户端通信
SyncLock 语句确保多个线程不会同时执行向列表框添加数据的操作当线程到达 SyncLock 块时将计算表达式并保持此互斥性直到在表达式返回的对象上有锁为止这防止了表达式在多个线程运行期间更改值从而避免代码产生意外的结果
向客户端返回服务端处理后的消息代码片段如下
格式化返回消息 这是服务器端处理后的结果并返回到客户端
BacktoClientData = SystemDateTimeNow 向客户端传递服务端程序处理时的时间
Buffer = EncodingUTFGetBytes(BacktoClientDataToCharArray)
通过打开的套接字将结果发送回客户端应用程序然后关闭该套接字
CurSocketSend(Buffer BufferLength )
CurSocketClose()
编译桌面服务端程序运行效果如下图所示
当服务程序处理完一个连接后就向相应的PPC客户端返回一条成功信息如下图所示
至此PPC客户端程序和桌面服务端程序编写完毕该系统在安装WindowsXP SP的桌面电脑和Dell Xi 的PPC上测试通过
四结论
本程序简单介绍了PC与PPC之间Socket通信方法在实际应用中还有很多细节要做并且还有可以拓展的地方该模型为实现PC与PPC间复杂的信息交互创建复杂的无线信息系统提供了一定技术基础