Winsock控件,實現網絡點對點通信

網絡的階梯第二話:

使用Winsock控件,實現網絡點對點通信

  blog出現在CSDN,也就blog將不blog也。你會問爲什麼吧?無論你心裏有沒有這個問號,但在我心裏這是個句號!你又會問爲什麼嗎?好,不管你問不問。我決定,現在作答。但那種長篇大論的前因後果,請恕我無法一一羅列。我只能直接而又間接地說明,blog出現在CSDN,也就blog將不blog也。

  使用VB,寫過網絡程序,沒試過Winsock這個控件實在是遺憾(API高手除外)。我想沒有朋友有這種遺憾的...呵呵!因爲,通過Winsock控件,你可以把網絡通信簡化簡化再簡化。那是什麼程度?可能就是10行代碼以內就行了那種(IDE生成的隨外)!因爲那只是網絡通信,而通信,僅僅就發送一條信息,對方收到了,顯示出來。可以算了吧?來看看:

  首先,窗口加載過程,我們寫上:
            '設置了第一個Winsock控件進入等待
            Winsock1.LocalPort = 5052
            Winsock1.Listen
            '再來把第二個Winsock控件連向第一個
            Winsock2.Connect "127.0.0.1", 5052

  好了,這時Winsock1控件的ConnectionRequest事件觸發。我們寫上:
            If Winsock1.State <> sckClosed Then Winsock1.Close
            Winsock1.Accept requestID '接受連接

  就這樣就連上啦!簡單得很。再來:
            Winsock1.SendData Text1.Text '把Text1中的文本傳給對方

  當然了,你傳了數據給Winsock2,那它的DataArrival事件也觸發了。
            Dim strDat As String
            Winsock2.GetData strDat '取得數據
            Text2.Text = strDat '在Text2中顯示出來

  此,一個使用Winsock控件,實現網絡通信程序就完成了!哈哈哈,是不是覺得上當受騙了?這種點子,MSDN裏早有出賣了!蓋茲可真不是蓋的,竟然早早想到用MSDN斷我財路!唉.難怪他比我富!

  完成任務了?沒有,我要作的是《使用Winsock控件,實現網絡點對點通信》。通信實現了,也是一個點對另一個點。可是,在普遍認識裏,點對點並不是這麼解釋的。怎麼解?就是雙向通信吧!呵呵,但蓋茲始終比我富。這點子在MSDN裏又已經出賣了!

  耐煩了吧?但要寫程序,就不能怕煩。如果你決定走這條路,那還有更煩更煩的等着你。喂,煩人,你能看得下去嗎?哈哈~!既然好點子全給蓋茲拿去賣了,那還有什麼好說的?就如,比爾賣你一個空的數據表,可是不填上數據。它還是一個空的數據表,而不是數據表!但比爾是不能幫你填上適合你的數據的,所以,你要照着你現有的數據表,向空的數據表填上數據,再把源數據表刪了,那它就成了一個獨一無二的數據表了!

  看你真的不耐煩了...你餓嗎?把鼠標蒸熟吃了頂一會吧!
  ,現在就讓我說一說,點對點通信應用裏的一個重要程序...但這次,不會再像上面那樣,列出一行一行的代碼,然後告訴你添加到那裏,然後怎樣...現在我要就“授人以漁”的政策,堅持“編程重思想”的方針;向“由淺入深”的路線,從“理解到認識”的哲學角度出發。解釋一下,通過Winsock控件,在網絡上傳送文件的VB程序是怎麼寫的!

  如前面的例子一樣,用Winsock控件通信,得分開客戶端與服務器,即:Client、Server(C/S)。但大家都知道,這僅是使用TCP協議必需要上演的一幕。而在UDP裏,並沒有明確的把C/S分開!但這樣概念要變模糊了。這在以後的話題裏,我會再作解釋。今天我們的主角是TCP。
  例中,那個循序漸進的步驟,大家體會到嗎?沒關係,現在來回顧一下。

  • 服務器監聽5052端口。
  • 客端連接到服務器的5052端口,就是服務器程序監聽的端口了!
  • 由於有客戶連接,服務器程序那的Winsock控件就觸發ConnectionRequest事件(說明見MSDN)。
  • 服務器接受了連接,客端程序的Winsock控件會觸發Connect事件,以表示連接上(例省略)。
  • 連接上後,由服務器程序向客戶端程序發送數據。
  • 數據到達客端,Winsock控件觸發起客端程序的DataArrival事件提示,取得數據,顯示!

  個流程就這樣,顯然很簡單了,不對嗎?呵呵!然後,我們得到這麼一個流程表後。就要開始填空了。打開我們想發送的文件讀取數據給服務器發送。對方收到數據後,再寫回到文件!過程就這麼個樣了!具體實現起來,還得要思考思考。好,現在我們就從一個用戶的角度出發去想吧!

  個Winsock怎麼連上我就不說了,反正蓋茲都已經比我富!那我們要傳文件,先得讓用戶選擇一個文件吧。然後就開始傳囉!好傳,慢着!客端怎麼知道服務器要傳的是什麼類型的文件呢?好!有了這個想法,我們在用戶點擊服務器程序傳送文件按扭的時候,獲取剛纔用戶選擇的文件的名字吧。要傳過去了?還是不行,如果我們這個程序是聊天+傳文件的話,那不是很混亂了?嗯,給這個信息一個名字吧!就叫:“Send”怎樣?好,是個很有霸氣的名字~大家用過QQ之類的聊天軟件傳文件都知道,QQ會先詢問接收方是否接收XXXX.XXX的文件!那我們就模仿一下吧,把傳送命令和文件名設置成:"Send 文件名",這樣的一個格式。然後用Winsock的SendData方法傳給對方。

  就去了客戶端編寫代碼囉:
  ,當收到這條命令,我們從前4個字得出,對方要傳文件過來了!也沒什麼好寫的,就彈個Msgbox出來說,對方要傳XXXX.XXX給你,你願意接收嗎?好,當用戶把保存路徑確定下來了,然後在這樣的路徑裏,創建一個這樣的文件,以備接收到數據時寫入。我們現在就得返回一條信息給服務器那,就叫:"ILiefly "吧!表示確認接收。

  轉回來服務器那寫代碼了:
  到接收確認信息,我們開始進行讀文件操作了!把文件打開,聲明一個Byte數組。把打開的文件數據全讀入到這個數組裏。哈哈~不就成了嗎?那傳過去試試...

  戶端開始接收到數據了:
  ,糟糕,怎麼會有數據類型錯誤呢?原來每當有數據到達客戶端,我們在DataArrival事件寫的代碼都執行了。還以爲是命令或聊天內容呢!不行,要給它設置一個標記,用作記錄什麼時候傳過來的數據是文件的的數據吧。用一個Static的語句(見MSDN)聲明一個靜態布爾的變量,當False的時候是我們命令或聊天內容,而True的時候就是文件數據吧!再改一改上一次在客戶端那寫的源碼,在答應接收之後,把這個布爾變量設爲True!啊?問題又出來了!這時我想到,那什麼時候才能把布爾變量設回False呢?倒,還是有問題!目光集中到服務器那裏去...

  回到服務器的代碼編輯框:
  什麼辦法讓客戶端知道什麼時候傳完呢?發送命令?不行,那裏會把命令寫入到文件的。連接關閉?更加不行,我還要聊天...嗯,要是讓客戶端知道傳過去的文件長度,不就可以確定什麼時候是接收完了嗎?哈哈!行動,改一改傳送文件的命令爲:"Send 文件名 文件長度"。哦?太多空格啦,那樣對方解釋起來相當麻煩嘛,用InStr?不行,要是文件名裏有空格怎辦?不就又出錯了...還好,我們有Split嘛(見MSDN)。好,那得把命令用個特別的字符分開了!就vbNullChar吧(見MSDN)!把命令改成:["SEND" & vbNullChar & 文件名 & vbNullChar & 文件長度]。傳給對方。(注:FileLen函數)

  戶端要修改代碼了:
  明一個動態字串數組,把Split函數的返回值附給它,哈哈!下標0是命令,下標1是文件名,下標2是文件長度!爽了~把長度用模塊域變量保存着,以備計算什麼時候收完吧!(注:Val函數)

  續在客戶端那寫我們沒完的代碼:
  爾變量爲True,有數據到達。那就取得它的數據吧!可要注意接收到的數據類型哦,對面傳過來的不是字符串了,我們要指定類型爲"vbArray + vbByte"(見MSND關於GetData的說明),意思就是字節數組啦。當然,我們要定義一個字節數組才能接收嘛 Dim ByteArray() As Byte。好,那剛纔保存下來的長度就有用啦,總長度-當次收到的字節數=剩餘字節數。當剩餘字節數爲0的時候,不就接收完了嗎?哈哈!不急,先來組織一下思路:數據到達,取得,保存到字節數組。用UBound函數(見MSDN)得到字節數組的最大下標,但由於數組下標0也存着數據,但文件不是這麼算啊,所以要算就得+1算!總長度 = 總長度 - UBound(ByteArray) + 1 嘿,行了!

  快運行試試,找個50多MB的傳。呵呵,突然,鐺!“內存不足”暈!搞什麼鬼啊,再看一下進程列表,哇!我的程序佔用了60MB左右的內存!暈,什麼回事啊?是不是那裏出錯了?再重組一下思路:選擇文件,發送命令,當收到返回確認信息,打開文件,讀...讀???不會吧,我把文件整個讀入內存了,難怪難怪(其實不會發生這個錯誤的,因爲Windows有虛擬內存嘛!但的確,要把整個文件讀入內存再發送出去,有點讓系統難爲啊)!得優化優化了。

  到服務器代碼裏進行優化:
  能一次讀入這麼多,那...分開來吧!一次傳送:8192個字節如何?好!就這樣吧。但不是每個文件的字節數都爲8192的倍數,怎辦呢?嗯,作個計算。之前,我們不是得到了要傳的文件的長度?既然對方都可以計算什麼時候收完,那我也可以計算什麼時候傳完嘛!首先,確定一下,要傳的文件長度是不是比8192大。如果是,那我們就把文件長度-8192。然後定義個下標是8191的字節數組(因爲數組下標0也可以存數據嘛,所以要-1),用Get方法,讀取打開的文件數據來填充這個字節數組。然後傳過去...嗯?對方好像會收了啊,未達到文件總長度時,客戶端不會把布爾數組設回來的。但,這邊怎麼知道什麼時候可以傳下一筆數據呢?嗯~SendComplete 事件——在完成一個發送操作時出現。好,就是這個了!我們在這邊也用文件長度爲0時作爲結束傳送的條件。如果不爲0,那就再看看還沒傳的文件剩下多少,因爲上面的計算已經-去了上次傳送的字節了,那現在的文件長度變量就是剩下字節的數量啦,再比較一下,是否比8192大。哈哈~不是就定義一個這麼大小的字節數組,讀文件,送出!完成~[MsgBox "傳輸完畢!", vbInformation, "⊙_⌒γ - Server"]...
  突然發現,原來SendComplete事件,在我們把傳文件命令發出去之後也會觸發。那沒法子了,只好再定義一個布爾變量來確定什麼時候是傳文件吧...

  此,一個用Winsock控件傳送文件的VB程序寫完了!呵呵,沒睡着吧?不過我快睡着了!好睏,上一次睡覺的時間只有3小時!現在-_-#了...但得說明一下,上面提到的8192,並不是我隨便安上去的。這個在以後再作詳細說明吧!

  ,要做的事還沒完!好像我沒說爲什麼:“blog出現在CSDN,也就blog將不blog也”哈哈~其實這篇東東已經說得很清楚了!

qyii
2004年12月02日 -_-#
發佈了29 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章