利用VB.Net 通过Windows Sockets (Winsock)以及多线程编程进行桌面电脑与Pocket PC间的双向通信
利用VB.Net编程实现PC与掌上电脑PPC间的双向通信
[源文件下载]
http://www.cnblogs.com/Risen/category/110585.html
本文介绍如何利用VB.Net 通过Windows Sockets (Winsock)以及多线程编程进行桌面电脑与Pocket PC(掌上电脑,简称:PPC)间的双向通信,并且通过当今流行的.net平台编程语言Visaul Basic.Net 2003编写PC端和PPC端程序实现两者的信息交换。
@H_
502_12@
<!--[if !supportLists]-->
一、<!--[endif]-->概述
如今移动计算设备越来越普及,而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框架是一种新的计算平台,可以简单理解为组件库或者类库,类似于MFC和VCL,但是却比它们更加强大和易用。.NET框架具有两个主要组件:公共语言运行库和 .NET 框架类库。公共语言运行库是 .NET 框架的基础,可以将运行库看作一个在执行时管理
代码的代理,它提供核心服务(如内存管理、线程管理和远程处理等)。以运行库为目标的
代码称为托管
代码,而不以运行库为目标的
代码称为非托管
代码。.NET 框架类库是一个与公共语言运行库紧密集成的可重用的类型集合,该类库是面向对象的,.NET 框架类库能够完成一系列常见编程任务,
包括诸如字符串管理、数据收集、
数据库连接以及
文件访问等任务。
.Net框架精简版是完整.Net框架的子集。它是对完整的 .Net框架进行精简后得到的版本,虽然其规模大大减小,但多数
功能仍然保持完整。使用.NET框架精简版可以针对Pocket PC和其他Windows CE .NET设备进行开发和部署,提高开发人员的工作效率
VB.Net编译的桌面程序在目标机器上需要.Net框架的
支持才能运行,而编译过的PPC程序需要在目标PPC上装载.Net框架精简版后方可运行。
@H_
502_12@
<!--[if !supportLists]-->
二、<!--[endif]-->设计思路
在.Net框架中System.Net.Sockets命名空间为需要严密控制
网络访问的开发提供了 Windows Sockets (Winsock) 接口的托管实现,该命名空间中包含了与Socket相关的类、接口和枚举,主要
包括实现 Berkeley
套接字接口的Socket类、用于 TCP 网络客户端侦听连接的TcpListener类、为 TCP 网络服务提供客户端连接的TcpClient类以及提供无连接的发送和接收
用户数据文报 (UDP) 网络服务等。该命名空间下的大部
分类都受.Net框架精简版的
支持,也就是说实现这些对象的
代码完全可以在装载有.Net框架精简版的PPC上运行。
设计PPC程序的Form窗体与桌面程序没有太多差别,只是少了一些PPC上所
不支持的
属性,但是
属性的
名称并没有变化。在PPC上没有窗口最小化的实际意义,主窗体的Minimize
Box属性必须设置成False,否则,当需要
退出程序点击
标题栏最右侧的
关闭按钮时,程序并没有真正
退出而是隐藏在
后台,程序需要重新激活才能回到
前台。或者可以向一个Button
添加关闭主窗体的
代码:
Private
Sub
Button3_Click(
ByVal
sender
As
System.Object,
ByVal
e
As
System.EventArgs)
Handles
Button3.Click
Me
.Close()
EndSub
Me 为VB.Net中的关键字,可以引用当前在其中执行
代码的类或结构的特定实例。Me 的行为与引用当前实例的对象变量或结构变量类似。此例Me代表主窗体的实例Form1。使 用
TcpClient向TcpListener 或 Socket 侦听器连接并交换数据,可以使用下面两种方法之一连接到侦听器:
(
1)创建一个 TcpClient,并调用三个可用的 Connect 方法之一。
(
2)使用远程主机的主机名和端口号创建 TcpClient。此构造函数将自动尝试一个连接。
TcpClient的Connect
方法使用指定的主机名和端口号将客户端连接到 TCP 主机,重载
函数原型为:
使用指定的远程网络终结点将客户端连接到远程
TCP 主机。
Overloads Public Sub Connect(IPEndPoint)
使用指定的
IP 地址和端口号将客户端连接到 TCP 主机。
Overloads Public Sub Connect(IPAddress,Integer)
将客户端连接到指定主机上的指定端口。
Overloads Public Sub Connect(String,Integer)
我们使用指定的主机
名称和端口号将客户端连接到
TCP 主机,在堆栈中创建TcpClient对象的实例,当执行
退出当前过程或
函数时,堆栈中的资源会
自动清除,保证内存空间被正确释放。连接成功后要发送和接收数据,使用 GetStream
方法来
获取一个 NetworkStream,NetworkStream为基础数据流,
调用 NetworkStream 的 Write 和 Read
方法与远程主机之间发送和接收数据。当发送操作结束后使用 TcpClient的
Close 方法断开连接、关闭对象并释放与 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,InBuff.Length)
rtndata
=
"
服务程序已经成功收到指令,在
"
&
System.Text.Encoding.Default.GetString(InBuff,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 Stu
dio .Net 2003,新建Visaul Basic项目,并选择“Windows应用程序”创建项目。项目创建完后,Visaul Stu
dio.Net会创建一个默认的主窗体Form1,在窗体上分别放置三个Label控件、两个Text
Box控件、一个列表框控件和一个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
ExitSub
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,
0
)
CurSocket.Close()
当服务程序处理完一个连接后就向相应的PPC客户端返回一条成功信息,如下图所示:
至此,PPC客户端程序和桌面服务端程序编写完毕,该系统在安装WindowsXP SP2的桌面电脑和Dell X3i 的PPC上测试通过。
@H_502_12@
<!--[if !supportLists]-->
一、<!--[endif]-->结论
本程序简单介绍了
PC
与
PPC
之间
Socket
通信方法,在实际应用中还有很多细节要做,并且还有可以拓展的地方。该模型为实现
PC
与
PPC
间复杂的信息交互,创建复杂的无线信息系统提供了一定技术基础。