本文介紹如何利用VB.Net 通過Windows Sockets (Winsock)以及多線程編程進行桌面電腦與Pocket PC(掌上電腦,簡稱:PPC)間的雙向通信,并且通過當今流行的.net平臺編程語言Visaul Basic.Net 2003編寫PC端和PPC端程序實現兩者的信息交換。
一、概述
如今移動計算設備越來越普及,而PPC與桌面電腦的數據交換更是其必不可少的功能。筆者根據實際的無線PPC開發認為,PPC程序與桌面程序進行通信,除了可以通過SQL Server CE提供的RDA(遠程數據存。┖蚏eplication(復制)來完成與桌面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框架精簡版后方可運行。
二、設計思路
在.Net框架中System.Net.Sockets命名空間為需要嚴密控制網絡訪問的開發提供了 Windows Sockets (Winsock) 接口的托管實現,該命名空間中包含了與Socket相關的類、接口和枚舉,主要包括實現 Berkeley 套接字接口的Socket類、用于 TCP 網絡客戶端偵聽連接的TcpListener類、為 TCP 網絡服務提供客戶端連接的TcpClient類以及提供無連接的發送和接收用戶數據文報 (UDP) 的網絡服務等。該命名空間下的大部分類都受.Net框架精簡版的支持,也就是說實現這些對象的代碼完全可以在裝載有.Net框架精簡版的PPC上運行。
可以把PPC當作局域網中的一個終端來與桌面電腦連接,它們之間有多種物理連接方式,通?梢允褂肬SB線纜配合ActiveSync直接與桌面電腦連接,當然,若PPC和桌面電腦同時具備其它的連接方式,比如無線WiFi(無線802.1x)、藍牙等都可以組成相應的無線局域網絡。
在PPC上編寫客戶端程序并實現TcpClient類,用于連接、發送和接收流數據。在桌面電腦上我們設計Socket服務端程序并實現TcpListener類和Socket類,用于偵聽和接受傳入連接的請求。Socket類為網絡通信提供了一套非常豐富的方法和屬性,服務程序可以使用 TcpListener.Listen 方法偵聽連接。Accept 方法處理任何傳入的連接請求,并返回可用于與遠程主機進行數據通信的Socket,如果當前使用的是無連接協議(如UDP),則根本不需要偵聽連接。實際上TcpClient類和TcpListener類為 Socket通信提供了比Socket類更簡單、對用戶更友好的接口,如果編寫較簡單的應用程序,而且只需同步數據傳輸,則可以考慮使用 TcpClient、TcpListener 和 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客戶端程序界面
![]() PPC客戶端程序界面 |
設計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。使用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) |
我們使用指定的本地主機IP地址和端口號將客戶端連接到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, 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 Form1 …… |
跟蹤調試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客戶端返回一條成功信息 |
至此,PPC客戶端程序和桌面服務端程序編寫完畢,該系統在安裝WindowsXP SP2的桌面電腦和Dell X3i 的PPC上測試通過。
四、結論
本程序簡單介紹了PC與PPC之間Socket通信方法,在實際應用中還有很多細節要做,并且還有可以拓展的地方。該模型為實現PC與PPC間復雜的信息交互,創建復雜的無線信息系統提供了一定技術基礎。