VB Socket編程(Winsock控件創建TCP/IP客戶機/服務器程序)
Winsock控件建立在TCP、UDP協議的基礎上,完成與遠程計算機的通信。即使對TCP/IP不太熟悉的用戶,使用該控件也可以在十幾分鍾內創建一 個簡單的客戶機/服務器程序。下面我們對Winsock控件的事件、方法、屬性按其在程序中出現的順序分別作詳細的介紹,以便更好地理解程序源代碼。
下面是Winsock控件的相關屬性,方法和事件。(略去一些暫用不到的)
*屬性
-------------------------------------------------------------------------
LocalHostName >
先在一台計算機上運行服務器程序,此時窗口上只有一個“退出”按鈕。 再在另一台計算機上運行客戶機程序,在“連接”按鈕右邊的文本框中輸入服務器的主機名后單擊“連接”按鈕。如果連接成功,則服務器和客戶機程序窗口都會出 現兩個文本框。這時,兩端都可以在上面的文本框中輸入文字,這些文字會立即在下面的文本框中出現。
服務器程序使用的控件如下:
(1)Command1:退出按鈕;
(2)textsend:發送數據文本框;
(3)Winsockserver: 服務器Winsock;
(4)textget :接收數據文本框。
服務器程序的界面如圖所示。
服務器程序的源代碼如下:
- textget.Visible = False
- Winsockserver.LocalPort = 1001
- Winsockserver.Listen
- End Sub
- Private Sub textsend_Change()
- Winsockserver.SendData textsend.Text
- End Sub
- Private Sub Winsockserver_Close()
- Winsockserver.Close
- End
- End Sub
- Private Sub Winsockserver_ConnectionRequest(ByVal requestID As Long)
- textsend.Visible = True
- textget.Visible = True
- If Winsockserver.State <> sckClosed Then Winsockserver.Close
- Winsockserver.Accept requestID
- End Sub
- Private Sub Winsockserver_DataArrival(ByVal bytesTotal As Long)
- Dim tmpstr As String
- Winsockserver.GetData tmpstr
- textget.Text = tmpstr
- End Sub
客戶機程序使用的控件如下:
(1)Command1:退出按鈕;
(2)Command2:連接按鈕;
(3)Winsockclient:客戶Winsock;
(4)Text1:主機名文本框;
(5)Textsend:發送數據文本框;
(6)Textget:接收數據文本框;
客戶機程序的源代碼如下:
- textget.Visible = False
- Winsockclient.RemotePort = 1001
- Winsockclient.RemoteHost = "sccdsz"
- End Sub
- Private Sub Text1_Change()
- Winsockclient.RemoteHost = Text1.Text
- End Sub
- Private Sub textsend_Change()
- Winsockclient.SendData textsend.Text
- End Sub
- Private Sub Winsockclient_Close()
- Winsockclient.Close
- End
- End Sub
- Private Sub winsockclient_Connect()
- textsend.Visible = True
- textget.Visible = True
- Command2.Visible = False
- End Sub
- Private Sub winsockclient_DataArrival(ByVal bytesTotal As Long)
- Dim tmpstr As String
- Winsockclient.GetData tmpstr
- textget.Text = tmpstr
- End Sub
Private intMax As Long Private Sub Form_Load() intMax = 0 sckServer(0).LocalPort = 1001 sckServer(0).Listen End Sub Private Sub sckServer_ConnectionRequest _ (Index As Integer, ByVal requestID As Long) If Index = 0 Then intMax = intMax + 1 Load sckServer(intMax) sckServer(intMax).LocalPort = 0 sckServer(intMax).Accept requestID Load txtData(intMax) End If End Sub
UDP 初步
創建 UDP 應用程序比創建 TCP 應用程序還要簡單,因為 UDP 協議不需要顯式的連接。在上面的 TCP 應用程序中,一個 Winsock 控件必須顯式地進行“監聽”,另一個必須使用 Connect 方法初始化連接。
UDP 協議不需要顯式的連接。要在兩個控件中間發送數據,需要完成以下的三步(在連接的雙方):
- 將 RemoteHost 屬性設置為另一台計算機的名稱。
- 將 RemotePort 屬性設置為第二個控件的 LocalPort 屬性。
- 調用 Bind 方法,指定使用的 LocalPort。(下面將詳細地討論該方法。)
因為兩台計算機的地位可以看成“平等的”,這種應用程序也被稱為點到點的。為了具體說明這個問題,下面將創建一個“聊天”應用程序,兩個人可以通過它進行實時的交談。
要創建一個 UDP 伙伴,請按照以下步驟執行:
- 創建一個新的 Standard EXE 工程。
- 將缺省的窗體的名稱修改為 frmPeerA。
- 將窗體的標題修改為“Peer A”。
- 在窗體中放入一個 Winsock 控件,並將其命名為 udpPeerA。
- 在“屬性”頁上,單擊“協議”並將協議修改為 UDPProtocol。
- 在窗體中添加兩個 TextBox 控件。將第一個命名為 txtSend,第二個命名為 txtOutput。
- 為窗體增加如下的代碼。
- VBScript code 復制代碼
-
Private Sub Form_Load() '控件的名字為 udpPeerA With udpPeerA '重點:必須將 RemoteHost 的值 '修改為計算機的名字。 .RemoteHost = "PeerB" .RemotePort = 1001 '連接的端口號。 .Bind 1002 '綁定到本地的端口。 End With frmPeerB.Show '顯示第二個窗體。 End Sub Private Sub txtSend_Change() '在鍵入文本時,立即將其發送出去。 >Text End Sub Private Sub udpPeerA_DataArrival _ (ByVal bytesTotal As Long) Dim strData As String >Text = strData End Sub
要創建第二個 UDP 伙伴,請按照以下步驟執行:
- 在工程中添加一個標准窗體。
- 將窗體的名字修改為 frmPeerB。
- 將窗體的標題修改為“Peer B”。
- 在窗體中放入一個 Winsock 控件,並將其命名為 udpPeerB。
- 在“屬性”頁上,單擊“協議”並將協議修改為“UDPProtocol”。
- 在窗體上添加兩個 TextBox 控件。將第一個命名為 txtSend,第二個命名為 txtOutput。
- 在窗體中添加如下的代碼。
- VBScript code 復制代碼
-
Private Sub Form_Load() '控件的名字為 udpPeerB。 With udpPeerB '重點:必須將 RemoteHost 的值改為 '計算機的名字。 .RemoteHost = "PeerA" .RemotePort = 1002 '要連接的端口。 .Bind 1001 '綁定到本地的端口上。 End With End Sub Private Sub txtSend_Change() '在鍵入后立即發送文本。 >Text End Sub Private Sub udpPeerB_DataArrival _ (ByVal bytesTotal As Long) Dim strData As String >Text = strData End Sub
如果要試用上面的例子,按 F5 鍵運行工程,然后在兩個窗體的 txtSend TextBox 中分別鍵入一些文本。鍵入的文字將出現在另一個窗體的 txtOutput TextBox 中。
關於 Bind 方法
在上面的代碼中,在創建 UDP 應用程序時調用了 Bind 方法,這是必須的。Bind 方法的作用是為控件“保留”一個本地端口。例如,如果將控件綁定到 1001 號端口,那么其它應用程序將不能使用該端口進行“監聽”。該方法阻止其它應用程序使用同樣的端口。
Bind 方法的第二個參數是任選的。如果計算機上存在多個網絡適配器,可以用 LocalIP 參數來指定使用哪一個適配器。如果忽略該參數,控件使用的將是計算機上“控制面板”設置中“網絡”控制面板對話框中列出的第一個適配器。
在使用 UDP 協議的時候,可以任意地改變 RemoteHost 和 RemotePort 屬性,同時始終保持綁定在同一個 LocalPort 上。TCP 協議與此不同,在改變 RemoteHost 和 RemotePort 屬性之前,必須先關閉連接。
- 1 通信程序通常都是采用Client/Server形式。這就要求作為服務器的主機可以同時處理多個客戶的請求。因此在編寫服務器程序時要添加多個Winsock控件。在開始我們先加入兩個Winsock控件。其中一個用來偵聽網上請求信號,取名為Listener;另外一個為初始的連接口,取名叫Sock(0)。注意,后一個控件要設為動態數組的形式,以后當客戶增多時,可在這個控件基礎上動態增加。由於受資源限制,我們在本例中設定最多可以同時接納15個客戶。客戶機一般只與一個主機相連,因此程序只須一個Winsock進行連接就足夠了。這個程序要用到的控件較少,除了Winsock和Form控件外,只須再添加Commmand控件即可。下面是具體程序和詳細注釋。
- 2 ******************************
- 3 '服務器程序
- 4 ******************************
- 5 Option Explicit
- 6 定義常量
- 7 Const BUSY As Boolean = False
- 8 Const FREE As Boolean = True
- 9 定義連接狀態
- 10 Dim ConnectState() As Boolean
- 11 Private Sub Form_Load()
- 12 ReDim Preserve ConnectState(0 To 1)
- 13 On Error Resume Next
- 14 ConnectState(0) = FREE
- 15 ConnectState(1) = FREE
- 16 '指定網絡端口號
- 17 Listener.LocalPort = 1011
- 18 '開始偵聽
- 19 Listener.Listen
- 20 End Sub
- 21 Private Sub Listener_ConnectionRequest(ByVal requestID As Long)
- 22 Dim SockIndex As Integer
- 23 Dim SockNum As Integer
- 24 On Error Resume Next
- 25 Form1.Print requestID & "連接請求"
- 26 '查找連接的用戶數
- 27 SockNum = UBound(ConnectState)
- 28 If SockNum > 14 Then
- 29 Form1.Print SockIndex & ""
- 30 Exit Sub
- 31 End If
- 32 '查找空閑的sock
- 33 SockIndex = FindFreeSocket()
- 34 '如果已有的sock都忙,而且sock數不超過15個,動態添加sock
- 35 If SockIndex > SockNum Then
- 36 Load Sock(SockIndex)
- 37 End If
- 38 ConnectState(SockIndex) = BUSY
- 39 Sock(SockIndex).Tag = SockIndex
- 40 '接受請求
- 41 Sock(SockIndex).Accept (requestID)
- 42 Form1.Print SockIndex & "接受請求"
- 43 End Sub
- 44
- 45 '客戶斷開,關閉相應的sock
- 46 Private Sub Sock_Close(Index As Integer)
- 47 If Sock(Index).State <> sckClosed Then
- 48 Sock(Index).Close
- 49 End If
- 50 ConnectState(Index) = FREE
- 51 Form1.Print Index & "close"
- 52 End Sub
- 53
- 54 '接收數據
- 55 Private Sub Sock_DataArrival(Index As Integer, ByVal bytesTotal As Long)
- 56 Dim dx As Double
- 57 Form1.Print "數據來自" & Index
- 58 Sock(Index).GetData dx, vbDouble
- 59 Form1.Print "dx=" & dx
- 60 End Sub
- 61
- 62 '尋找空閑的sock
- 63 Public Function FindFreeSocket()
- 64 Dim SockCount, i As Integer
- 65 SockCount = UBound(ConnectState)
- 66 For i = 0 To SockCount
- 67 If ConnectState(i) = FREE Then
- 68 FindFreeSocket = i
- 69 Exit Function
- 70 End Ifs
- 71 Next i
- 72 ReDim Preserve ConnectState(0 To SockCount + 1)
- 73 FindFreeSocket = UBound(ConnectState)
- 74 End Function
- 75
- 76 ***************************
- 77 '客戶程序
- 78 ’***************************
- 79 Option Explicit
- 80 '發送數據
- 81 Private Sub command1_Click()
- 82 Dim dx As Double
- 83 dx = 23.9
- 84 sock.SendData dx
- 85 MsgBox ("data sended")
- 86 End Sub
- 87
- 88 Private Sub Form_Load()
- 89 '遠程主機名
- 90 sock.RemoteHost = "media2"
- 91 '網絡端口
- 92 sock.RemotePort = 1011
- 93 '發出連接命令
- 94 sock.Connect
- 95 Command1.Enabled = False
- 96 End Sub
- 97
- 98 '服務器關閉
- 99 Private Sub sock_Close()
- 100 MsgBox ("socket closed")
- 101 End Sub
- 102
- 103 '連接成功
- 104 Private Sub sock_Connect()
- 105 MsgBox ("socket connected")
- 106 Command1.Enabled = True
- 107 End Sub
- 復制代碼