作者 : 蔡宗成 (2005-01-26); 推薦 : 吳昌暉 徐業良 (2005-01-26); 最近更新 : 徐業良 (2005-02-27) PIC_SERVER 教材 (9) 以 VB 設計 TCP/UDP 之網路傳輸應用 本文首先介紹使用 Visual Basic 所提供的 Microsoft Winsock Control 控制元件 (MSWINSCK.OCX), 設計 UDP 與 TCP 兩種通訊協定的 Client 與 Server 應用程式, 達成訊息傳遞功能 了解 Visual Basic 的 UDP/TCP 的基本功能後, 就可自行開發 PIC_SERVER 的 UDP/TCP 應用程式 1. Microsoft Winsock Control 控制元件簡介 Microsoft Winsock Control 控制元件支援 UDP(User Datagram Protocol) 與 TCP(Transmission Control Protocol) 兩種通訊協定 UDP 是非連結式通訊協定, 不須建立特定的網路連結, 只要設定電腦間的 IP 位址與使用相同的 Port, 即可互相傳遞訊息 TCP 是連結式通訊協定, 用戶端電腦與伺服端電腦必須先建立網路連結, 便可在此連結下, 互相傳遞資料與訊息 使用 UDP 通訊協定發送訊息相當於寄普通信件, 郵局不會告知寄件人是否將信件送達至收件人 ; 使用 TCP 通訊協定則相當於寄雙掛號信件, 郵局會告知寄件人是否已將信件送達收件人 ( 若未完成則會有逾時 timeout 信號 ) UDP 可說是發了就不管,TCP 則會在時限之內, 若沒有回音就不斷嘗試重發, 因此在網路吵雜的環境裡, TCP 成功完成傳信任務的機率較高, 但效率也較低且耗時, 且成功完成傳信任務並不保證信中的內容無誤 Visual Basic 提供一項 Microsoft Winsock Control 控制元件 (MSWINSCK.OCX), 作為程式設計者開發應用程式與 UDP TCP 通訊協定之間的橋樑, 不須深入了解網路傳輸的理論, 只要設定此控制元件所提供的屬性 (Properties), 應用其事件 (Events) 1
及方法 (Methods), 即可開發 Client( 用戶端 : 主動先開口尋求連結 ) 與 Server( 伺服端 : 被動答話接受連結 ) 應用程式 由於 Microsoft Winsock Control 控制元件並不是 VB 預設的控制元件, 所以必須自行引用至程式專案中 如圖 1, 在 VB 程式專案中, 選擇專案 設定使用元件 選擇 Microsoft Winsock Control 6.0, 雙擊 Microsoft Winsock Control 6.0 控制元件後, 便會出現在程式專案裡 圖 1(a). 設定使用元件 2
圖 1(b). 選擇 Microsoft Winsock Control 6.0 3
圖 1(c). 引用至程式裡 Client/Server 架構中 請求 - 回應 " 過程之執行流程如圖 2, 敘述如下 : (1) 如圖 2(a),Server 端必須先建立可以提供 Client 端連結的功能, 成為 Listen 狀態, 等待 Client 端連接, 而 Client 端則在 Connect 狀態, 嘗試與 Server 端建立連結 (2) 當 Server 端接收到來自 Client 端的 連結請求 (Connection Request),Server 端裡的 Microsoft Winsock Control 控制元件產生 ConnectionRequest 事件, 在此事件中, 利用 Accept 方法接收來自 Client 端的請求, 如圖 2(b) (3) 如圖 2(c),Client 端與 Server 端建立連結後,Client 端開始對 Server 端傳送資料 (SendData),Server 端的 Microsoft Winsock Control 控制元件同時產生 DataArrival 事件, 在此事件中, 利用 GetData 方法接收 Client 端所傳送來之資料 (4) 如圖 2(d),Server 端也可以傳送資料給 Client 端, 稱為 回應 (Response), 程序同步驟 (3) 4
用戶端 Client Connect 連結請求 伺服端 Server Accept 圖 2(a). 嘗試建立連結 用戶端 Client Connect 允許建立連結 伺服端 Server Accept 圖 2(b). 建立連結 用戶端 Client SendData DataArrival 伺服端 Server GetData 圖 2(c). Client 端傳送資料至 Server 端 用戶端 Client GetData DataArrival 伺服端 Server SendData 圖 2(d). 回應資料 2. 以 VB 建立 UDP 對話視窗範例程式 UDP 是非連結式的通訊協定, 主要目的在高速傳遞資料 UDP 不須先建立通訊連結, 只要設定電腦間的 IP 位置與使用相同的 Port, 節省建立連結時間 ( 所以才能高速傳遞資料 ), 但 UDP 不提供資料錯誤的偵測及資料重送等機制, 無法確保資料能完整送達 本節介紹以 VB 建立 UDP 對話視窗之範例程式 Client 端範例程式收錄在資料夾 C:\YZ_PIC\Samples\pic_SERVER\VB\TCP_UDP\UDP_Client\UDP_Client.vbp, Server 端為 C:\YZ_PIC\Samples\pic_SERVER\VB\TCP_UDP\UDP_Client\UDP_Server.vbp 5
2.1 Client 端範例程式 Client 端範例程式外觀如圖 3, 設計 Host_IP Port Msg 欄提供使用者輸入數值, 一空白視窗將 Server 端傳回的訊息顯示出來,Send 按鈕作為連接和傳輸 表 1 為 Client 端範例程式所引用的基本控制元件與其屬性值, 注意對應前述 4 個欄位的 4 個 設定值分別為 remote_udp_server_ip remote_port Msg 和 Server_reply, Command_button 的設定為 Send_button 此外 Winsock 命名為 WskClient, 採用之 Protocal 為 1-sckUDPProtocol 圖 3. Client 端範例程式外觀 6
表 1. UDP_Client 表單之元件與屬性 元件 屬性 設定值 Form frmclient UDP_Client Frame Frame1 1 Host_IP 2 Port 3 Msg Msg Text Hi, there. remote_port Text 1055 remote_udp_server_ip Text 140.138.139.41 Server_reply Text CommandButton Send_button Send Winsock WskClient Protocol 1-sckUDPProtocol 圖 4 為 Client 端範例程式程式碼 當使用者按下 Send_button 按鈕後 (Send_button_Click()), 設定 WskClient.RemoteHost(Winsock Client 端欲連結遠端的主機名稱或 IP 位址 ) 為 remote_udp_server_ip(remote_udp_server_ip 文字框裡的 IP),WskClient.RemotePort(Winsock Client 端欲連結遠端的主機使用的 Port) 為 remote_port(remote_port 文字框裡的 Port), 並使用 SendData 方法傳送 Msg 文字框裡的內容 當 UDP_Client 收到遠方資料傳送過來時, 啟動 Microsoft Winsock Control 控制元 件的 DataArrival 事件 (WskClient_DataArrival()), 定義 strdata 為一字串變數, 使用 GetData 方法接收資料, 並將 strdata 資料顯示在 Server_reply 文字框裡 7
圖 4. UDP_Client 之程式碼 2.2 Server 端範例程式 Server 端範例程式外觀如圖 5, 設計 Local_IP 欄顯示 Server 端的 IP 位址,Port 欄提供使用者設定本 Server 連接 Port,Msg 欄將 Client 端傳來的訊息顯示出來, debug_info 欄作為顯示速度測試訊息之用, 並設計 Update 按鈕作為更新訊息 表 2 為 Server 端範例程式所引用的基本控制元件與其屬性值, 注意對應前述 4 個欄位的 4 個 設定值分別為 ServerIP ServerPort Msg 和 debug_info, Command_button 的設定為 Update_button 此外 Winsock 命名為 WskServer, 採用之 Protocal 為 1-sckUDPProtocol 圖 5. Server 端範例程式外觀 8
表 2. UDP_Server 表單之元件與屬性 元件 屬性 設定值 Form frmserver UDP_Server Frame Frame1 1 Local_IP 2 Port 3 Msg debug_info Text Msg Text ServerIP Text 192.11.79.158 ServerPort Text 1055 CommandButton Update_button Update Winsock WskServer Protocol 1-sckUDPProtocol 圖 6 為 UDP_Server 之程式碼 在副程式 From_Load 裡, 首先顯示 Server 端的 IP 位址, 並呼叫 Update_button_Click 副程式 在副程式 Update_button_Click 裡, 使用者每按下 Update_button 一次, 先關掉 Microsoft Winsock Control 控制元件, 並使用 Bind 方法設定 Server 端的連接 Port, 接 著清空 Msg 文字框與 debug_info 文字框裡的內容,msg_count 變數歸零 當收到 Client 端送來資料時, 啟動 Microsoft Winsock Control 控制元件的 DataArrival 事件 (WskServer_DataArrival()), 定義 strdata 為一字串變數, 使用 GetData 方法接收資料, 並將資料顯示在 Msg 文字框中, 同時將收到時間 訊息長度 收到訊息次數顯示在 debug_info 文字框中 接著再將收到通知 收到時間 Client 端位址 Port 等相關資訊, 利用 SendData 方法自動回傳資料至 Client 端 9
圖 6. UDP_Server 之程式碼 2.3 UDP 程式測試 啟動資料夾 C:\YZ_PIC\Samples\pic_SERVER\VB\TCP_UDP 裡的 UDP_Client.exe 與 UDP_Server.exe 範例程式執行檔, 在同一部電腦 (IP:140.138.*.*) 中進行測試, 圖 7(a) 為執行結果 如圖 7(b), 在 UDP_Client, 欲連結端 Host_IP 必須設定為 (140.138.*.*), Port 設定為 1055,Msg 文字框裡寫入 Hi, there., 按下 Send 按鈕, 便將資料傳送至指定 IP 與 Port 的 Server 端 UDP_Server 執行時, 會讀取 Server 端的 IP 位址, 顯示在 Local_IP 文字框,Port 設定為 1055,UDP_Server 接收到 UDP_Client 傳來的資料, 並將資料顯示在 Msg 文字框內, 再自動回傳資料給 UDP_Client, 在 UDP_Server, 按下 Updata 按鈕可清空 Msg 文字框與 debug_info 文字框內容 UDP_Server 每接收到一次資料時,msg_count 便會累計加 1 10
圖 7(a). 啟動 UDP_Client.exe 與 UDP_Server.exe 範例程式執行檔 圖 7(b). UDP_Client 與 UDP_Server 傳送與接收過程 3. 以 VB 建立 TCP 對話視窗範例程式 TCP 是連結式通訊協定, 主要目的在提供大量資料的傳遞並確保其傳遞資料無誤, 有提供錯誤偵測 資料復原及資料重送等機制 TCP 在傳送資料前, 會在 Server 端與 Client 端間建立通訊連結, 再互相傳送資料 11
本節介紹以 VB 建立 TCP 對話視窗之範例程式 Client 端範例程式收錄在資料夾 C:\YZ_PIC\Samples\pic_SERVER\VB\TCP_UDP\TCP_Client\TCP_Client.vbp, Server 端為 C:\YZ_PIC\Samples\pic_SERVER\VB\TCP_UDP\TCP_Server\TCP_Server.vbp 3.1 Client 端範例程式 Client 端範例程式外觀如圖 8, 同樣設計 Host_IP Port Msg 欄提供使用者輸入數值, 一空白視窗將 Server 端傳回的訊息顯示出來,Send 按鈕作為連接和傳輸, 並增加一 Status 欄位, 顯示目前連結狀況 引用元件除 Microsoft Winsock Control 6.0 控制元件外, 還增加了一個 Timer 作為連線實際時之用 表 3 為 Client 端範例程式所引用的基本控制元件與其屬性值, 注意對應前述 4 個欄位的 4 個 設定值分別為 remote_ip remote_port Msg 和 Message_from_Server,Command_button 的設定為 Send_button 此外 Winsock 命名為 WskClient, 採用之 Protocal 為 0-sckTCPProtocol,Timer 則命名為 Timer1 圖 8. Client 端範例程式外觀 12
表 3. TCP_Client 表單之元件與屬性 元件 屬性 設定值 Form frmclient TCP_Client Frame Frame1 ClientStatus Not Connected 1 Host_IP 2 Port 3 Status : 4 Msg 5 Message_from_Server Text Msg Text Anybody home? remote_ip Text 140.138.139.41 remote_port Text 83 CommandButton Send_button Send Timer Timer1 Enable False Winsock WskClient Protocol 0-sckTCPProtocol 圖 9 為 TCP_Client 之程式碼 當按下 Send_button 時, 啟動 Timer1, 並設定 Timer1 的時間週期為 3000ms, 清空 Message_from_Server 文字框,WskClient 的 State 值為 6 時, 代表正在嘗試與 Server 端連線中, 如果不等於 6, 設定欲連結 Server 端的 IP 位址與 Port, 並執行 WskClient_Connect 副程式, 代表已連線成功 否則 ClientStatus 顯示 Connecting 13
在 WskClient_Connect 副程式中, 當連線成功時,ClientStatus 顯示 Connected., 並將 Msg 文字框裡的內容藉由 Microsoft Winsock Control 控制元件的 SendData 方法傳送出去 當 Client 端接收到資料時, 啟動 DataArrival 事件, 定義 strdata 為一字串變數, 使用 GetData 方法接收此 strdata 資料, 將所接收到的資料顯示在 Message_from_Server 文字框, 接著結束連線,ClientStatus 顯示 Disconnected., 關閉 Timer1 在 Timer1_Timer 副程式裡, 當 Timer1 啟動後經過 3000ms 還無法與 Server 端建立連線, 便結束連線,ClientStatus 顯示 Not Connected., 並出現訊息提示框 (MsgBox) 告知 Failed to send message in 3 sec. 圖 9. TCP_Client 之程式碼 3.2 Server 端範例程式 Client 端範例程式外觀如圖 10, 同樣設計 Local_IP 欄顯示 Server 端的 IP 位址, Port 欄提供使用者設定本 Server 連接 Port,Msg 欄將 Client 端傳來的訊息顯示出來, 14
debug_info 欄作為顯示速度測試訊息之用,Update 按鈕作為更新訊息, 並增加一 Status 欄位, 顯示目前連結狀況 引用元件除 Microsoft Winsock Control 6.0 控制元件外, 還增加了一個 Timer 作為連線實際時之用 表 4 為 Server 端範例程式所引用的基本控制元件與其屬性值, 注意對應前述 4 個欄位的 4 個 設定值分別為 ServerIP ServerPort Msg 和 debug_info,command_button 的設定為 Update_button 此外 Winsock 命名為 WskServer, 採用之 Protocal 為 0-sckTCPProtocol,Timer 則命名為 Timer1 圖 10. Server 端範例程式外觀 15
表 4. TCP_Server 表單之元件與屬性 元件 屬性 設定值 Form frmserver TCP_Server Frame Frame1 1 Local_IP 2 Port 3 Status : 4 Msg ServerStatus Server is Idle debug_info Text Msg Text ServerIP Text 192.11.79.158 ServerPort Text 83 CommandButton Update_button Update Timer Timer1 Enable True Winsock WskServer Protocol 0-sckTCPProtocol 圖 11 為 TCP_Client 之程式碼 首先顯示 Server 端的 IP 位址, 並呼叫 WskServer_Listen() 副程式 在 WskServer_Listen() 副程式裡, 關閉 Timer1, 使用 Close 方法關閉連線, 設定 Winsock 的 LocalPort 屬性為 ServerPort 文字框裡的 IP 位址, 再使用 Listen 方法等待 Client 端之連線請求,ServerStatus 顯示 Listening... 16
當 Client 提出連線需求時, 引發 Winsock 的 ConnectionRequest 事件, 開始判斷 Winsock 的 State 為何 如果 State 為 sckconnected, 表示 Server 已被其他 Client 連結, 出現提示框, 內容為 Server already connected!, 如果不是, 先使用 Close 方法關閉連線, 再使用 Accept 方法取得 Client 端的 ID,ServerStatus 顯示 connected, 啟動 Timer1, 設定 Timer1 的週期為 10000ms, 清空 debug_info 文字框 Timer1 開始計時 10000ms 後, 呼叫 WskServer_Listen() 副程式, 回復到等待連線 (listen) 狀態 當 Client 傳送資料過來時, 引發 Winsock 的 DataArrival 事件, 定義 strdata 為一字串變數, 使用 GetData 接收此 strdata 資料, 並將資料顯示在 Msg 文字框裡,, 同時將收到時間 訊息長度 收到訊息次數顯示在 debug_info 文字框中 接著再將收到通知 收到時間 Client 端位址 Port 等相關資訊使用 SendData 方法回傳至 Client 端 當使用者按下 Update 按鈕時, 清空 Msg 文字框內容, 清空 debug_info 文字框內容, 呼叫 WskServer_Listen() 副程式, 回復到等待連線 (listen) 狀態 17
圖 11. TCP_Server 程式碼 3.3 TCP 程式測試 啟動資料夾 C:\YZ_PIC\Samples\pic_SERVER\VB\TCP_UDP 裡的 TCP_Client.exe 與 TCP_Server.exe 範例程式執行檔, 在同一部電腦 (IP:140.138.*.*) 中進行測試, 圖 12(a) 18
為執行結果,TCP_Client 端的 Status 顯示 Not Connected,TCP_Server 端的 Status 顯示 Listening 如圖 12(b), 當 Client 端設定 Server 端的 IP 位址與 Port, 按下 Send 按鈕, 將 Msg 文字框裡的內容傳送到 Server 端 TCP_Server 執行時, 會讀取 Server 端的 IP 位址, 顯示在 Local_IP 文字框,Port 設定為 83,TCP_Server 接收到 TCP_Client 傳來的資料, 並將資料顯示在 Msg 文字框內, 再自動回傳資料給 TCP_Client, 在 TCP_Server, 按下 Updata 按鈕可清空 Msg 文字框與 debug_info 文字框內容 TCP_Server 每接收到一次資料時,msg_count 便會累計加 1 圖 12(c) 為 TCP_Client 端所欲連結的 IP 位址與 TCP_Server 端不同時, 經過 3 秒後所出現的訊息提示框 圖 12(a). 啟動 TCP_Client.exe 與 TCP_Server.exe 範例程式執行檔 19
圖 12(b). TCP_Client 與 TCP_Server 傳送與接收過程 圖 12(c). 無法連結時出現提示框 4. 使用 TCP 與 PIC_SERVER 傳遞訊息在 client 端或 Server 端以 PIC_SERVER 取代 PC, 使用 UDP 或 TCP 傳遞訊息, 需要使用資料夾 C:\YZ_PIC\Samples\pic_SERVER\Beginner\ 中的範例程式 20
ex_4_udp_client.c ex_5_udp_server.c ex_6_tcp_server.c 及 ex_8_tcp_client.c 取代前述 VB 程式 以下敘述以 PIC_SERVER 為 Server 端 PC 為 client 端, 使用 TCP 與 PIC_SERVER 傳遞訊息的設定方式 將範例程式 C:\YZ_PIC\Samples\pic_SERVER\Beginner\ex_6_tcp_server.c 載入 PIC_SERVER, 在 ICP 中設定 local TCP server port 的欄位 ( 例如 3423), 不可與 HTTP port 相同 ( 例如 80), 如圖 13 所示 Client 端的 PC 則仍執行前述 VB 程式 TCPclient.exe, 將其 Host_IP 與 Port 設定為 pic_server 的 IP 和 local TCP server port, 就可以傳訊給 PIC 了 例如在 Msg 文字框裡輸入字串訊息 I am superman, 按下 Send 按鈕傳遞,PIC_SERVER 接收到訊息後再回傳通知給 TCP_Client, 如圖 14 範例程式 ex_6_tcp_server.c 架構和先前教材中 PIC 端 C 程式十分類似, 且範例程式中有很清楚的註解說明, 此處不再一一詳加解釋 值得注意的是範例程式 ex_6_tcp_server.c 中 TCP 通訊部分, 主要差異在先前沒有用到的 call back 函式 callback_tcpclientdataarrival() 內, 即接獲 TCP client 傳來 data 後的處理動作, 這個 callback 副程式與 VB 範例 TCP_Server.vbp 裡面的副程式 Private Sub WskServer_DataArrival(ByVal bytestotal As Long) 的功能與角色完全相同, 只是命名的概念有些差異罷了 此外,VB 用 WskServer.GetData strdata 以及 WskServer.SendData (reply_msg) 等兩個指令實現接收字串與傳送字串的功能,pic_SERVER 則使用 BIOS 指令 nic_getc 與 nic_putc 這兩個指令實現接收一個字元 (byte) 與傳送一個字元的功能, 用 printf( nic_putc, ) 的方式實現傳送字串的功能 總之, 要做的事情都一樣, 不同的程式語言採用不同的指令與函數實現這些功能 其他的 call back 函式如 callback_tcpserverdataarrival() callback_udpserverdataarrival() callback_udpclientdataarrival() 也都可以在 VB 範例中找到相對應的副程式,VB 有的通訊功能 PIC_SERVER 也都有, 瞭解 VB 的 UDP/TCP 就更能瞭解 PIC_SERVER 了 21
圖 13. PIC_SERVER 通訊協定設定 22
圖 14. TCP_Client 與 PIC_SERVER 傳遞訊息 將 Port 改為 80, 並在 Msg 文字框裡, 輸入 GET/index.htm,PIC_SERVER 收到 此指令後將指定網頁內容回傳給 TCP_Client, 如圖 15 圖 15. TCP_Client 傳遞指令索取網頁內容 23
實驗 嘗試以以下 6 種方式執行本教材中提到範例程式執行檔 UDP_Client.exe UDP_Server.exe TCP_Client.exe TCP_Server.exe: (1) PC 對 PC,UDP 通訊, 只用一台 PC, 自己發自己收 (2) PC 對 PC,UDP 通訊, 用兩台 PC, 一個發一個收 (3) PC 對 PC,TCP 通訊, 只用一台 PC, 自己發自己收 (4) PC 對 PC,TCP 通訊, 用兩台 PC, 一個發一個收 (5) 稍微修改 VB 範例程式, 用三台 PC 互相通訊, 第一台 PC 用 TCP_client 發信給第二台 PC 的 TCP_server, 第二台 PC 收到第一台 PC 送來的信之後, 就用 UDP 把信轉寄給第三台 PC 當然, 這個實驗用兩台 PC 也可以玩 (6) PC 對 PIC_SERVER, 使用 UDP 與 TCP 兩種模式傳送訊息, 並在下週課堂上實際展示 參考資料 黃嘉輝,2002,Visual Basic 網際網路程式設計 -TCP/IP 與 Internet Programming 篇最新版, 文魁資訊股份有限公司 24