大鳥的學(xué)習(xí)樂園
          路漫漫其修遠(yuǎn)兮,吾將上下而求索
          posts - 26,comments - 27,trackbacks - 0
          用VB5中WinSock控件編寫網(wǎng)上聊天程序



          Sockets是在Unix系統(tǒng)上提出來的,一開始主要是用于本地通訊,但很快就應(yīng)用到C/S體系上。MicroSoft公司在此基礎(chǔ)上創(chuàng)建了 WinSock控件,專門用于Windows接口,與Sockets完全兼容。Winsock控件對用戶來說是不可見的,它提供了訪問 TCP 和 UDP 網(wǎng)絡(luò)服務(wù)的方便途徑。Microsoft Access、Visual Basic、Visual C++ 或 Visual FoxPro 的開發(fā)人員都可使用它。為編寫客戶或服務(wù)器應(yīng)用程序,不必了解 TCP 的細(xì)節(jié)或調(diào)用低級的 Winsock APIs。通過設(shè)置控件的屬性并調(diào)用其方法就可輕易連接到一臺遠(yuǎn)程機(jī)器上去,并且還可雙向交換數(shù)據(jù)。下面就利用VB5中的WinSock控件編寫一個網(wǎng)上 聊天程序。


          一)網(wǎng)絡(luò)通信協(xié)議的基礎(chǔ)和選擇


          1.1 TCP(數(shù)據(jù)傳輸協(xié)議)基礎(chǔ)

          數(shù)據(jù)傳輸協(xié)議允許創(chuàng)建和維護(hù)與遠(yuǎn)程計算機(jī)的連接。連接兩臺計算機(jī)就可彼此進(jìn)行數(shù)據(jù)傳輸。
          如果創(chuàng)建客戶應(yīng)用程序,就必須知道服務(wù)器計算機(jī)名或者 IP 地址(RemoteHost 屬性),還要知道進(jìn)行“偵聽”的端口(RemotePort 屬性),然后調(diào)用 Connect 方法。
          如果創(chuàng)建服務(wù)器應(yīng)用程序,就應(yīng)設(shè)置一個收聽端口(LocalPort 屬性)并調(diào)用 Listen 方法。當(dāng)客戶計算機(jī)需要連接時就會發(fā)生 ConnectionRequest 事件。為了完成連接,可調(diào)用 ConnectionRequest 事件內(nèi)的 Accept 方法。
          建立連接后,任何一方計算機(jī)都可以收發(fā)數(shù)據(jù)。為了發(fā)送數(shù)據(jù),可調(diào)用 SendData 方法。當(dāng)接收數(shù)據(jù)時會發(fā)生 DataArrival 事件。調(diào)用 DataArrival 事件內(nèi)的 GetData 方法就可獲取數(shù)據(jù)。

          1.2 UDP(用戶數(shù)據(jù)文報協(xié)議)基礎(chǔ)

          用戶數(shù)據(jù)文報協(xié)議 (UDP) 是一個無連接協(xié)議。跟 TCP 的操作不同,計算機(jī)并不建立連接。另外 UDP 應(yīng)用程序可以是客戶機(jī),也可以是服務(wù)器。
          為了傳輸數(shù)據(jù),首先要設(shè)置客戶計算機(jī)的 LocalPort 屬性。然后,服務(wù)器計算機(jī)只需將 RemoteHost 設(shè)置為客戶計算機(jī)的 Internet 地址,并將 RemotePort 屬性設(shè)置為跟客戶計算機(jī)的 LocalPort 屬性相同的端口,并調(diào)用 SendData 方法來著手發(fā)送信息。于是,客戶計算機(jī)使用 DataArrival 事件內(nèi)的 GetData 方法來獲取已發(fā)送的信息。

          1.3 選擇通訊協(xié)議

          在使用 WinSock 控件時,首先需要考慮使用什么協(xié)議。可以使用的協(xié)議包括 TCP 和 UDP。兩種協(xié)議之間的重要區(qū)別在于它們的連接狀態(tài):
          TCP 協(xié)議是有連接的協(xié)議,可以將它同電話系統(tǒng)相比。在開始數(shù)據(jù)傳輸之前,用戶必須先建立連接。
          UDP 協(xié)議是一種無連接協(xié)議,兩臺計算機(jī)之間的傳輸類似于傳遞郵件:消息從一臺計算機(jī)發(fā)送到另一臺計算機(jī),但是兩者之間沒有明確的連接。另外,單次傳輸?shù)淖畲髷?shù)據(jù)量取決于具體的網(wǎng)絡(luò)。

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

          1. 在收發(fā)數(shù)據(jù)的時候,應(yīng)用程序是否需要得到客戶端或者服務(wù)器的確認(rèn)信息?如果需要,使用 TCP 協(xié)議,在收發(fā)數(shù)據(jù)之前先建立明確的連接。

          2. 數(shù)據(jù)量是否特別大(例如圖象與聲音文件)?在連接建立之后,TCP 協(xié)議將維護(hù)連接并確保數(shù)據(jù)的完整性。不過,這種連接需要更多的計算資源,因而是比較“昂貴”的。

          3. 數(shù)據(jù)發(fā)送是間歇的,還是在一個會話內(nèi)?例如,如果應(yīng)用程序在某個任務(wù)完成的時候需要通知某個計算機(jī),UDP 協(xié)議是更適宜的。UDP 協(xié)議適合發(fā)送少量的數(shù)據(jù)。

          通訊協(xié)議的選擇是通過設(shè)置WinSock的Protocol屬性來實(shí)現(xiàn)的。下面選擇TCP通訊協(xié)議編寫網(wǎng)上聊天程序,在此之前必須知道一個極其重要的參數(shù)---服務(wù)器端的IP地址或計算機(jī)名。

          二)確定計算機(jī)的名字

          1. 在計算機(jī)的桌面上,右鍵單擊“網(wǎng)上鄰居”。

          2. 選擇“屬性”。

          3. 單擊“標(biāo)識”選項卡。

          4. 在“計算機(jī)名稱”框中可以找到計算機(jī)的名稱。

          確定計算機(jī)的 IP地址

          1. 單擊“任務(wù)條”上的“啟動”。

          2. 選擇“運(yùn)行”。

          3. 若服務(wù)器端操作系統(tǒng)為win95則在“打開”中填入“winipcfg”,若服務(wù)器端操作系統(tǒng)為winnt則在“打開”中填入“ipconfig”。

          4. 按下“確定”鍵。

          上面找到的計算機(jī)名稱或IP地址可以作為WinSock的RemoteHost 屬性的值。

          三) winsock控件的State屬性。




          state 屬性的設(shè)置值是: 常 數(shù)
          值 描 述

          sckclosed 0 缺省的。關(guān)閉
          sckopen 1 打開
          scklistening 2 偵聽
          sckconnectionpending 3 連接掛起
          sckresolvinghost 4 識別主機(jī)
          sckhostresolved 5 已識別主機(jī)
          sckconnecting 6 正在連接
          sckconnected 7 已連接
          sckclosing 8 同級人員正在關(guān)閉連接
          sckerror 9 錯誤


          下面主要要用到sckClosed.sckConnected兩個State屬性的值。

          四)網(wǎng)上聊天程序的編制

          4.1 程序中服務(wù)器端所起的作用。

          從圖示中可以看到服務(wù)器端的兩個winsock控件之間并不存在直接的通訊,同時sckServer1和sckClient2及 sckServer2和sckClient1之間是不能直接通訊的。這也即是說若sckClient1向sckClient2發(fā)出信息,信息首先被 sckServer1接受,sckServer1再將信息傳給程序的信息處理部分,信息處理部分再將處理好的信息傳給sckServer2,再由 sckServer2傳給sckClient2。反之亦然。那么服務(wù)器端的信息處理部分又進(jìn)行什么工作呢?

          1. 對通訊的通道數(shù)作一些限制。

          2. 對使用后已關(guān)閉的通道,必須能夠重新使用以節(jié)省資源。

          3. 必須對所傳遞的數(shù)據(jù)包信息作甑別,從而作出不同的處理。

          通過解開數(shù)據(jù)的包頭就可區(qū)分不同的信息。

          網(wǎng)上聊天有兩種方式:第一種,以廣播方式;第二種,以點(diǎn)對點(diǎn)的方式。廣播方式即所有客戶都能收到某一客戶發(fā)出的信息。點(diǎn)對點(diǎn)的方式即想說“悄悄話 ”的一對客戶專門開辟了一間談話的“小屋”,別的客戶不能“聽”到他們的談話。在下面的程序中將看到如何利用數(shù)據(jù)的不同包頭來區(qū)分用戶是想以廣播方式還是 以點(diǎn)對點(diǎn)的方式進(jìn)行談話的(點(diǎn)對點(diǎn)方式數(shù)據(jù)的包頭為“PT”,廣播方式則無包頭)。

          4.2 客戶端的程序

          1. 在客戶端創(chuàng)建一個新的工程將其命名為“ClientPrj”

          2. 將缺省窗體命名為 frmClient。

          3. 將窗體的標(biāo)題改為“Client”。

          4. 在窗體中添加一個 WinSock 控件,并將其命名為 tcpClient。

          5. 在 frmClient 中添加一個ListBox 控件。將其命名為lstReceive。

          6. 在 frmClient 中添加一個 TextBox 控件。將其命名為 txtSend。

          7. 在窗體上放兩個 CommandButton 控件,并將其命名為 cmdConnect和cmdSent。

          8. 將cmdConnect控件的標(biāo)題改為 Connect, 將cmdSent控件的標(biāo)題改為 Sent。

          9. 在窗體中添加如下的代碼。

          Private Sub cmdConnect_Click()

          On Error GoTo ErrorPro

          sckClient.Connect

          Exit Sub

          ErrorPro:

          MsgBox "服務(wù)器未開或網(wǎng)絡(luò)出錯!"

          End

          End Sub

          Private Sub cmdSent_Click()

          sckClient.SendData txtSent.Text

          End Sub

          Private Sub Form_Load()

          ' RemoteComputerName為服務(wù)器端的計算機(jī)名或IP地址。

          sckClient.RemoteHost = "RemoteComputerName"

          sckClient.RemotePort = 1000

          End Sub



          Private Sub sckClient_Close()

          MsgBox "服務(wù)器通道已關(guān)閉!"

          End

          End Sub



          Private Sub sckClient_Connect()

          MsgBox "連接成功!"

          cmdConnect.Enabled = False

          End Sub



          Private Sub sckClient_DataArrival(ByVal bytesTotal As Long)

          Dim s As String

          sckClient.GetData s

          lstReceive.AddItem s

          End Sub



          Private Sub sckClient_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, _ ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)

          sckClient.Close

          cmdConnect.Enabled = True

          End Sub

          4.3 服務(wù)器端的程序

          1. 在服務(wù)器端創(chuàng)建一個新的工程將其命名為“ServerPrj”。

          2. 將缺省窗體命名為“frmServer”。

          3. 在窗體中添加一個ListBox控件,將其命名為“lstReceive”。

          4. 在窗體中添加三個WinSock控件,將其分別命名為“sckListen”,sckBusy和“sckServer”并將“sckServer”的“Index”屬性設(shè)置為0。

          5. 在窗體中添加如下代碼.。

          '最大通道數(shù)

          Private MaxChan As Integer



          Private Sub Form_Load()

          Dim i As Integer

          MaxChan = 10

          For i = 1 To MaxChan - 1

          Load sckServer(i)

          Next i

          sckListen.LocalPort = 1000

          sckListen.Listen

          End Sub



          Private Sub sckBusy_Close()

          sckBusy.Close

          End Sub



          Private Sub sckBusy_DataArrival(ByVal bytesTotal As Long)

          sckBusy.SendData "服務(wù)器忙,請稍后再連接!"

          DoEvents

          End Sub



          Private Sub sckListen_ConnectionRequest(ByVal requestID As Long)

          Dim i As Integer

          '決定由哪一Winsock接受請求

          For i = 0 To MaxChan - 1

          If sckServer(i).State = 0 Then

          Exit For

          End If

          Next i

          If sckServer(i).State = 0 Then

          sckServer(i).Accept requestID

          Exit Sub

          End If

          '如果所有Winsock都用完則由專門的“忙”Winsock接受請求,以免用戶要求得不到響應(yīng)

          sckBusy.Close

          sckBusy.Accept requestID

          End Sub



          Private Sub sckListen_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, _ ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)

          sckListen.Close

          sckListen.LocalPort = 1000

          sckListen.Listen

          End Sub



          Private Sub sckServer_Close(Index As Integer)

          sckServer(Index).Close

          End Sub



          Private Sub sckServer_DataArrival(Index As Integer, ByVal bytesTotal As Long)

          Dim s As String

          Dim i As Integer

          sckServer(Index).GetData s

          If UCase(Left(Trim(s), 2)) = "PT" Then '判斷是否為悄悄話,點(diǎn)對點(diǎn)方式

          If IsNumeric(Mid(Trim(s), 3, 1)) Then

          i = Mid(Trim(s), 3, 1)

          sckServer(i).SendData "Channel " & Index & " " & Right(Trim(s), Len(Trim(s)) - 3)

          DoEvents

          End If

          Else '廣播方式

          For i = 0 To MaxChan - 1

          '利用winsock的State屬性給所有連接在服務(wù)器上的客戶發(fā)消息

          If sckServer(i).State = 7 Then

          sckServer(i).SendData "Channel " & Index & " " & Trim(s)

          DoEvents

          End If

          Next i

          End If

          lstReceive.AddItem "Channel " & Index & " " & Trim(s)

          End Sub



          Private Sub sckServer_Error(Index As Integer, ByVal Number As Integer, Description As String, _

          ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As _

          Long, CancelDisplay As Boolean)

          sckServer(Index).Close

          End Sub

          從程序中可以看到:第一,程序中限制了通道數(shù)(10路)。第二,通過判斷WinSock控件的State屬性是否為0(關(guān)閉狀態(tài)),來重新使用已 關(guān)閉的WinSock控件。第三,通過給WinSock控件傳遞的信息加上包頭,來對信息進(jìn)行不同的處理(程序中若信息前加上了“PT"(Private Talk)+"通道數(shù)”的包頭,由此就知道客戶想要同擁有此“通道數(shù)”的另一客戶進(jìn)行“悄悄話”,否則就以廣播方式將信息發(fā)給所有客戶)。


          五) 結(jié)束語

          WinSock控件不僅僅是用來編制網(wǎng)上聊天程序,而且可以用來編制各種網(wǎng)絡(luò)游戲或網(wǎng)絡(luò)通信程序。實(shí)際上WinSock控件是編制各種C/S程序 的利器。在實(shí)際使用中通常是將WinSock控件封裝在Activex DLL(進(jìn)程內(nèi))、Activex EXE(進(jìn)程外)部件的類中(類中引用)來使用的。通過區(qū)分所傳信息前的不同的包頭,用RaiseEvent命令引發(fā)不同 的事件,再對事件分別進(jìn)行處理。這樣不僅增加了程序的可調(diào)試性和安全性,而且更符合事件驅(qū)動編程方法的特點(diǎn)。
          posted on 2009-08-21 13:54 大鳥 閱讀(1976) 評論(0)  編輯  收藏 所屬分類: VBA
          主站蜘蛛池模板: 西林县| 津市市| 北流市| 永清县| 凭祥市| 商都县| 云龙县| 黔南| 洞头县| 英吉沙县| 香格里拉县| 寻甸| 宜兰市| 桐城市| 遵义市| 澜沧| 扎囊县| 德钦县| 兴安县| 汶川县| 盐边县| 民勤县| 白水县| 信阳市| 玉环县| 格尔木市| 吴旗县| 穆棱市| 尼玛县| 福建省| 巴中市| 乌拉特中旗| 喀喇| 政和县| 武威市| 孟连| 五河县| 蓬安县| 伊吾县| 同仁县| 闻喜县|