电竞比分网-中国电竞赛事及体育赛事平台

分享

VB Winsock 控件TCP與UDP連接實(shí)例

 玉雪龍山999 2012-01-31

VB Winsock 控件TCP與UDP連接實(shí)例

減小字體 增大字體 作者:佚名  來(lái)源:本站整理  發(fā)布時(shí)間:2010-06-19 03:42:15

利用 WinSock 控件可以與遠(yuǎn)程計(jì)算機(jī)建立連接,并通過(guò)用戶數(shù)據(jù)文報(bào)協(xié)議 (UDP)或者傳輸控制協(xié)議 (TCP)進(jìn)行數(shù)據(jù)交換。這兩種協(xié)議都可以用來(lái)創(chuàng)建客戶與服務(wù)器應(yīng)用程序。與 Timer 控件類似,WinSock 控件在運(yùn)行時(shí)是不可見的。

可能的用途

  • 創(chuàng)建收集用戶信息的客戶端應(yīng)用程序,并將收集的信息發(fā)送到某中央服務(wù)器。
     
  • 創(chuàng)建一個(gè)服務(wù)器應(yīng)用程序,作為多個(gè)用戶的數(shù)據(jù)的匯入點(diǎn)。
     
  • 創(chuàng)建“聊天”應(yīng)用程序。

選擇通訊協(xié)議

在使用 WinSock 控件時(shí),首先需要考慮使用什么協(xié)議??梢允褂玫膮f(xié)議包括 TCP 和 UDP。兩種協(xié)議之間的重要區(qū)別在于它們的連接狀態(tài):

  • TCP 協(xié)議控件是基于連接的協(xié)議,可以將它同電話系統(tǒng)相比。在開始數(shù)據(jù)傳輸之前,用戶必須先建立連接。
     
  • UDP 協(xié)議是一種無(wú)連接協(xié)議,兩臺(tái)計(jì)算機(jī)之間的傳輸類似于傳遞郵件:消息從一臺(tái)計(jì)算機(jī)發(fā)送到另一臺(tái)計(jì)算機(jī),但是兩者之間沒(méi)有明確的連接。另外,單次傳輸?shù)淖畲髷?shù)據(jù)量取決于具體的網(wǎng)絡(luò)。

到底選擇哪一種協(xié)議通常是由需要?jiǎng)?chuàng)建的應(yīng)用程序決定的。下面的幾個(gè)問(wèn)題將有助于選擇適宜的協(xié)議:

  1. 在收發(fā)數(shù)據(jù)的時(shí)候,應(yīng)用程序是否需要得到客戶端或者服務(wù)器的確認(rèn)信息?如果需要,使用 TCP 協(xié)議,在收發(fā)數(shù)據(jù)之前先建立明確的連接。
     
  2. 數(shù)據(jù)量是否特別大(例如圖象與聲音文件)?在連接建立之后,TCP 協(xié)議將維護(hù)連接并確保數(shù)據(jù)的完整性。不過(guò),這種連接需要更多的計(jì)算資源,因而是比較“昂貴”的。
     
  3. 數(shù)據(jù)發(fā)送是間歇的,還是在一個(gè)會(huì)話內(nèi)?例如,如果應(yīng)用程序在某個(gè)任務(wù)完成的時(shí)候需要通知某個(gè)計(jì)算機(jī),UDP 協(xié)議是更適宜的。UDP 協(xié)議適合發(fā)送少量的數(shù)據(jù)。

協(xié)議的設(shè)置

在設(shè)計(jì)時(shí),可以按如下方式設(shè)置應(yīng)用程序使用的協(xié)議:在“屬性”窗口中單擊“協(xié)議”,然后選擇 sckTCPProtocol 或者 sckUDPProtocol。也可以使用程序代碼來(lái)設(shè)置 Protocol 屬性,如下所示:

Winsock1.Protocol = sckTCPProtocol

確定計(jì)算機(jī)的名稱

在與遠(yuǎn)程計(jì)算機(jī)相連接的時(shí)候,需要知道它的 IP 地址或者它的“好聽的名字”。IP 地址是一串?dāng)?shù)字,每三個(gè)數(shù)字為一組,中間用點(diǎn)隔開(形如 xxx.xxx.xxx.xxx)。通常,最易記住的是計(jì)算機(jī)的“好聽的名字”。

要確定計(jì)算機(jī)的名字,請(qǐng)按照以下步驟執(zhí)行:

  1. 在計(jì)算機(jī)的“任務(wù)欄”上,單擊“啟動(dòng)”。
     
  2. 在“設(shè)置”項(xiàng)中,單擊“控制面板”。
     
  3. 雙擊“網(wǎng)絡(luò)”圖標(biāo)。
     
  4. 單擊“標(biāo)識(shí)”選項(xiàng)卡。
     
  5. 在“計(jì)算機(jī)名稱”框中可以找到計(jì)算機(jī)的名稱。

上面找到的計(jì)算機(jī)名稱可以作為 RemoteHost 屬性的值。

TCP 連接初步

如果應(yīng)用程序要使用 TCP 協(xié)議,那么首先必須決定應(yīng)用程序是服務(wù)器還是客戶端。如果要?jiǎng)?chuàng)建一個(gè)服務(wù)器端,那么應(yīng)用程序需要“監(jiān)聽”指定的端口。當(dāng)客戶端提出連接請(qǐng)求時(shí),服務(wù)器端能夠接受請(qǐng)求并建立連接。在連接建立之后,客戶端與服務(wù)器端可以自由地互相通訊。

下列步驟創(chuàng)建一個(gè)非常簡(jiǎn)單的服務(wù)器:

要?jiǎng)?chuàng)建一個(gè) TCP 服務(wù)器,請(qǐng)按照以下步驟執(zhí)行:

  1. 創(chuàng)建新的 Standard EXE 工程。
     
  2. 將缺省窗體的名稱改為 frmServer。
     
  3. 將窗體的標(biāo)題改為“TCP 服務(wù)器”。
     
  4. 在窗體中放入一個(gè) Winsock 控件,并將它的名字改為 tcpServer。
     
  5. 在窗體上添加兩個(gè) TextBox 控件。將第一個(gè)命名為 txtSendData,第二個(gè)為 txtOutput。
     
  6. 為窗體添加如下的代碼。
VBScript code復(fù)制代碼
Private Sub Form_Load()
    '將 LocalPort 屬性設(shè)置為一個(gè)整數(shù)。
    '然后調(diào)用 Listen 方法。
    tcpServer.LocalPort = 1001
    tcpServer.Listen
    frmClient.Show '顯示客戶端的窗體。
End Sub

Private Sub tcpServer_ConnectionRequest _
        (ByVal requestID As Long)
    '檢查控件的 State 屬性是否為關(guān)閉的。
    '如果不是,
    '在接受新的連接之前先關(guān)閉此連接。
    If tcpServer.State <> sckClosed Then _
        tcpServer.Close
    '接受具有 requestID 參數(shù)的
    '連接。
    tcpServer.Accept requestID
End Sub

Private Sub txtSendData_Change()
    '名為 txtSendData 的 TextBox 控件中
    '包含了要發(fā)送的數(shù)據(jù)。當(dāng)用戶往文本框中
    '鍵入數(shù)據(jù)時(shí),使用 SendData 方法
    '發(fā)送輸入的字符串。
    tcpServer.SendData txtSendData.Text
End Sub

Private Sub tcpServer_DataArrival _
        (ByVal bytesTotal As Long)
    '為進(jìn)入的數(shù)據(jù)聲明一個(gè)變量。
    '調(diào)用 GetData 方法,并將數(shù)據(jù)賦予名為 txtOutput
    '的 TextBox 的 Text 屬性。
    Dim strData As String
    tcpServer.GetData strData
    txtOutput.Text = strData
End Sub

上面的步驟創(chuàng)建了一個(gè)簡(jiǎn)單的服務(wù)器應(yīng)用程序。為了使它能夠工作,還必須為它創(chuàng)建一個(gè)客戶端的應(yīng)用程序。

要?jiǎng)?chuàng)建 TCP 客戶端,請(qǐng)按照以下步驟執(zhí)行:

  1. 在工程中添加一個(gè)新的窗體,將其命名為 frmClient。
     
  2. 將窗體的標(biāo)題改為“TCP Client”。
     
  3. 在窗體中添加一個(gè) Winsock 控件,并將其命名為 tcpClient。
     
  4. 在 frmClient 中添加兩個(gè) TextBox 控件。將第一個(gè)命名為 txtSend,第二個(gè)為 txtOutput。
     
  5. 在窗體上放一個(gè) CommandButton 控件,并將其命名為 cmdConnect。
     
  6. 將 CommandButton 控件的標(biāo)題改為 Connect。
     
  7. 在窗體中添加如下的代碼。

重點(diǎn)   必須將 RemoteHost 屬性值修改為您的計(jì)算機(jī)的名字。

VBScript code復(fù)制代碼
Private Sub Form_Load()
    'Winsock 控件的名字為 tcpClient。
    '注意:要指定遠(yuǎn)程主機(jī),可以使用
    ' IP 地址(例如:"121.111.1.1"),也可以使用
    '計(jì)算機(jī)的“好聽的名字”如下所示。
    tcpClient.RemoteHost = "RemoteComputerName"
    tcpClient.RemotePort = 1001
End Sub

Private Sub cmdConnect_Click()
    '調(diào)用 Connect 方法,初始化連接。
    tcpClient.Connect
End Sub

Private Sub txtSendData_Change()
    tcpClient.SendData txtSend.Text
End Sub

Private Sub tcpClient_DataArrival _
        (ByVal bytesTotal As Long)
    Dim strData As String
    tcpClient.GetData strData
    txtOutput.Text = strData
End Sub

上面的代碼創(chuàng)建了一個(gè)簡(jiǎn)單的客戶/服務(wù)器模式的應(yīng)用程序。我們可以將兩者都運(yùn)行起來(lái):運(yùn)行工程,然后單擊“連接”。在兩個(gè)窗體之一的 txtSendData 文本框中鍵入文本,可以看到同樣的文字將出現(xiàn)在另一個(gè)窗體的 txtOutput 文本框中。

接受多個(gè)連接請(qǐng)求

上面設(shè)計(jì)的基本服務(wù)器只能接受一個(gè)連接請(qǐng)求。通過(guò)創(chuàng)建控件數(shù)組,使用一個(gè)控件也可以同時(shí)接受多個(gè)連接請(qǐng)求。利用這種方法,不需要關(guān)閉連接,而只需創(chuàng)建新的控件實(shí)例(通過(guò)設(shè)置其索引屬性),然后在新的實(shí)例上調(diào)用 Accept 方法。

下面的代碼假定名為 sckServer 的窗體上有一個(gè) Winsock 控件,它的 Index 屬性被設(shè)置為 0;因此控件是控件數(shù)組的一部分。在聲明部分,聲明了一個(gè)模塊級(jí)的變量 intMax。在窗體的 Load 事件中,intMax 被設(shè)置為 0,數(shù)組中第一個(gè)控件的 LocalPort 屬性被設(shè)置為 1001。然后調(diào)用控件的 Listen 方法,使之成為“監(jiān)聽”控件。在連接請(qǐng)求到達(dá)時(shí),代碼將檢測(cè) Index 是否為 0(“監(jiān)聽”控件的值)。如果為 0,監(jiān)聽控件將增加 intMax 的值,并使用該號(hào)碼來(lái)創(chuàng)建新的控件實(shí)例。然后,使用新的控件實(shí)例接受連接請(qǐng)求。

VBScript code復(fù)制代碼
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 初步

創(chuàng)建 UDP 應(yīng)用程序比創(chuàng)建 TCP 應(yīng)用程序還要簡(jiǎn)單,因?yàn)?UDP 協(xié)議不需要顯式的連接。在上面的 TCP 應(yīng)用程序中,一個(gè) Winsock 控件必須顯式地進(jìn)行“監(jiān)聽”,另一個(gè)必須使用 Connect 方法初始化連接。

UDP 協(xié)議不需要顯式的連接。要在兩個(gè)控件中間發(fā)送數(shù)據(jù),需要完成以下的三步(在連接的雙方):

  1. 將 RemoteHost 屬性設(shè)置為另一臺(tái)計(jì)算機(jī)的名稱。

     
  2. 將 RemotePort 屬性設(shè)置為第二個(gè)控件的 LocalPort 屬性。

     
  3. 調(diào)用 Bind 方法,指定使用的 LocalPort。(下面將詳細(xì)地討論該方法。)

因?yàn)閮膳_(tái)計(jì)算機(jī)的地位可以看成“平等的”,這種應(yīng)用程序也被稱為點(diǎn)到點(diǎn)的。為了具體說(shuō)明這個(gè)問(wèn)題,下面將創(chuàng)建一個(gè)“聊天”應(yīng)用程序,兩個(gè)人可以通過(guò)它進(jìn)行實(shí)時(shí)的交談。

要?jiǎng)?chuàng)建一個(gè) UDP 伙伴,請(qǐng)按照以下步驟執(zhí)行:

  1. 創(chuàng)建一個(gè)新的 Standard EXE 工程。
     
  2. 將缺省的窗體的名稱修改為 frmPeerA。
     
  3. 將窗體的標(biāo)題修改為“Peer A”。
     
  4. 在窗體中放入一個(gè) Winsock 控件,并將其命名為 udpPeerA。
     
  5. 在“屬性”頁(yè)上,單擊“協(xié)議”并將協(xié)議修改為 UDPProtocol。
     
  6. 在窗體中添加兩個(gè) TextBox 控件。將第一個(gè)命名為 txtSend,第二個(gè)命名為 txtOutput。
     
  7. 為窗體增加如下的代碼。
VBScript code復(fù)制代碼
Private Sub Form_Load()
    '控件的名字為 udpPeerA
    With udpPeerA
        '重點(diǎn):必須將 RemoteHost 的值
        '修改為計(jì)算機(jī)的名字。
        .RemoteHost = "PeerB"
        .RemotePort = 1001 '連接的端口號(hào)。
        .Bind 1002 '綁定到本地的端口。
    End With
    frmPeerB.Show '顯示第二個(gè)窗體。
End Sub

Private Sub txtSend_Change()
    '在鍵入文本時(shí),立即將其發(fā)送出去。
    udpPeerA.SendData txtSend.Text
End Sub

Private Sub udpPeerA_DataArrival _
        (ByVal bytesTotal As Long)
    Dim strData As String
    udpPeerA.GetData strData
    txtOutput.Text = strData
End Sub

要?jiǎng)?chuàng)建第二個(gè) UDP 伙伴,請(qǐng)按照以下步驟執(zhí)行:

  1. 在工程中添加一個(gè)標(biāo)準(zhǔn)窗體。
     
  2. 將窗體的名字修改為 frmPeerB。
     
  3. 將窗體的標(biāo)題修改為“Peer B”。
     
  4. 在窗體中放入一個(gè) Winsock 控件,并將其命名為 udpPeerB。
     
  5. 在“屬性”頁(yè)上,單擊“協(xié)議”并將協(xié)議修改為“UDPProtocol”。
     
  6. 在窗體上添加兩個(gè) TextBox 控件。將第一個(gè)命名為 txtSend,第二個(gè)命名為 txtOutput。
     
  7. 在窗體中添加如下的代碼。
VBScript code復(fù)制代碼
Private Sub Form_Load()
    '控件的名字為 udpPeerB。
    With udpPeerB
        '重點(diǎn):必須將 RemoteHost 的值改為
        '計(jì)算機(jī)的名字。
        .RemoteHost = "PeerA"
        .RemotePort = 1002 '要連接的端口。
        .Bind 1001 '綁定到本地的端口上。
    End With
End Sub

Private Sub txtSend_Change()
    '在鍵入后立即發(fā)送文本。
    udpPeerB.SendData txtSend.Text
End Sub

Private Sub udpPeerB_DataArrival _
        (ByVal bytesTotal As Long)
    Dim strData As String
    udpPeerB.GetData strData
    txtOutput.Text = strData
End Sub

如果要試用上面的例子,按 F5 鍵運(yùn)行工程,然后在兩個(gè)窗體的 txtSend TextBox 中分別鍵入一些文本。鍵入的文字將出現(xiàn)在另一個(gè)窗體的 txtOutput TextBox 中。

關(guān)于 Bind 方法

在上面的代碼中,在創(chuàng)建 UDP 應(yīng)用程序時(shí)調(diào)用了 Bind 方法,這是必須的。Bind 方法的作用是為控件“保留”一個(gè)本地端口。例如,如果將控件綁定到 1001 號(hào)端口,那么其它應(yīng)用程序?qū)⒉荒苁褂迷摱丝谶M(jìn)行“監(jiān)聽”。該方法阻止其它應(yīng)用程序使用同樣的端口。

Bind 方法的第二個(gè)參數(shù)是任選的。如果計(jì)算機(jī)上存在多個(gè)網(wǎng)絡(luò)適配器,可以用 LocalIP 參數(shù)來(lái)指定使用哪一個(gè)適配器。如果忽略該參數(shù),控件使用的將是計(jì)算機(jī)上“控制面板”設(shè)置中“網(wǎng)絡(luò)”控制面板對(duì)話框中列出的第一個(gè)適配器。

在使用 UDP 協(xié)議的時(shí)候,可以任意地改變 RemoteHost 和 RemotePort 屬性,同時(shí)始終保持綁定在同一個(gè) LocalPort 上。TCP 協(xié)議與此不同,在改變 RemoteHost 和 RemotePort 屬性之前,必須先關(guān)閉連接。

Tags:Winsock TCP UDP

作者:佚名

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多