基于TCP協(xié)議的簡單即時通信軟件的設(shè)計與實現(xiàn)(含源文件)_第1頁
基于TCP協(xié)議的簡單即時通信軟件的設(shè)計與實現(xiàn)(含源文件)_第2頁
基于TCP協(xié)議的簡單即時通信軟件的設(shè)計與實現(xiàn)(含源文件)_第3頁
基于TCP協(xié)議的簡單即時通信軟件的設(shè)計與實現(xiàn)(含源文件)_第4頁
基于TCP協(xié)議的簡單即時通信軟件的設(shè)計與實現(xiàn)(含源文件)_第5頁
已閱讀5頁,還剩31頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

基于TCP協(xié)議的網(wǎng)絡(luò)通信系統(tǒng)的設(shè)計與實現(xiàn)摘要:網(wǎng)絡(luò)通信,由于其具有實時性、跨平臺性、本錢低、效率高等優(yōu)點而受到廣泛的使用.設(shè)計并實現(xiàn)一個能夠處理多用戶進行實時,平安的即時通信系統(tǒng)具有較強的現(xiàn)實意義.即時通信的底層通信是通過SOCKET套接字接口實現(xiàn)的.當前的主流UNIX系統(tǒng)和微軟的WINDOWS系統(tǒng)都在內(nèi)核提供了對SOCKET字接口的支持.使用這個統(tǒng)一的接口,可以編寫一個可移植的TCP/IP通信程序.使信息能夠在INTERNET上可靠的傳輸.本文設(shè)計并實現(xiàn)了基于局域網(wǎng)內(nèi)的簡單即時通信系統(tǒng),系統(tǒng)采用C/S模式,底層通信通過SOCKET套接字接口實現(xiàn),效勞器負責客戶端的登錄驗證,好友信息的保存和心跳報文的發(fā)送.客戶端采用P2P方式實現(xiàn)消息傳遞,并能實現(xiàn)文件的傳輸.本文首先討論了同步套接字,異步套接字,多線程并發(fā)執(zhí)行任務(wù)等;然后闡述了客戶端、效勞器如何使用XML序列化的消息進行通信.關(guān)鍵詞:即時通信;文件傳輸;套接字;TCP協(xié)議AbstractJnstantmessageshaveseveraladvantagessuchasreal-time,cross-platform,cheapandefficient.TodesignaMulti-userIM(instantmessage)architectureisveryimportantinboththeoryandrealism.InstantmessagebasedonTCP/IPprotocolthatisrealizedbysocketinterface.AlmostallUNIXoperationsystemsandMicrosoft!swindowsoperationsystemsprovidesupportofsocketinthekernel.Usingtheuniforminterface,wecandevelopaportableprogramofTCP/IP,whichhelpustransferinformationinInternetsafelyandcredibly.Thesystemusestheclient/server(C/S)mode.Theservertakestheresponsibilityoftheloginmessageofclient,thesavingoffriendmessageandMessageheartbeat.ThetransmissionofthebasicmessagesofthecustomerendwillbedesignedonP2Parchitecture.ThisthesisexplainshowtheclientandservercommunicateviaserializingXMLmessage.Keywords:InstantMessage;FileTransfer;Socket;TCPprotocol引言課題背景即時通信是一個終端連往一個即時通信網(wǎng)路的效勞.即時通信不同于e-mail在于它的交談是實時的.大局部的即時通信效勞提供了presenceawareness的特性一顯示聯(lián)絡(luò)人名單,聯(lián)絡(luò)人是否在在線與能否與聯(lián)絡(luò)人交談.最早的即時通信軟件是ICQ,ICQ是英文中Iseekyou的諧音,意思是我找你.四名以色列青年于2996年7月成立Mirabilis公司,并在it月份發(fā)布了最初的ICQ版本,在六個月內(nèi)有85萬用戶注冊使用.在因特網(wǎng)上受歡送的即時通信效勞包含了MSNMessenger.AOLInstantMessenger^Yah..!Messenger、NETMessengerService^Jabber、ICQ與QQo這些效勞有賴于許多想法更久的(與普遍)的在線聊天媒介,如InternetRelayChat一樣知名.197.年代早期,一種更早的即時通信形式是柏拉圖系統(tǒng)(PLATOsystem).之后在i98o年代,UNIX/Linux的交談實時信息被廣泛的使用于工程師與學術(shù)界,199.年代即時通信更跨越了因特網(wǎng)交流.1996年11月,ICQ是首個廣泛被非UNIX/Linux使用者用于因特網(wǎng)的即時通信軟件.在ICQ的介紹之后,同時在許多地方有一定數(shù)量的即時通信方式開展,且各式的即時通信程序有獨立的協(xié)議,無法彼此互通.這引導使用者同時執(zhí)行兩個以上的即時通信軟件,或者他們可以使用支持多協(xié)議的終端軟件,如Gaim、TrillianWcJabbero國內(nèi)外研究現(xiàn)狀國外研究現(xiàn)狀當今,國際上對網(wǎng)絡(luò)通信系統(tǒng)研究的較好的公司有,思科,Sun,Ms等公司,思科主要研究的是底層的傳輸;MS,Sun公司研究的是應用層.其中ms公司憑借其在操作系統(tǒng)的壟斷地位,為了在網(wǎng)絡(luò)的開展中取得先機,采用了各種各樣的手段.但是,其捆綁的msn,無論從功能上,還是技術(shù)上來說,都不算是非常先進的.當然,ie,同樣也不是很受人青睞,這讓人想起了,當年的網(wǎng)景公司,網(wǎng)景只是生不逢時.MS不擇手段的想打跨網(wǎng)景,可見其對網(wǎng)絡(luò)的重視.如今,Sun公司在網(wǎng)絡(luò)應用上捷足先登,憑借著Java,Sun在網(wǎng)絡(luò)的應用上領(lǐng)先于MS.微軟,想用同樣的方法搞跨對手,因此它拿出了Visualc#,來對抗Java.這些都是在應用層面的開發(fā)工具.應用層上的產(chǎn)品就更顯種類繁多.ICQ幾乎是國際上通用的即時通信工具,由于在我國它的應用不是很廣,所以,其原理也很少被介紹.msn,是M5的產(chǎn)品,同樣在國內(nèi)沒什么市場,所以,對其原理,也很少被討論過.至于ie,是在VisualC++下開發(fā)的產(chǎn)品,雖然有嚴重的平安隱患,不過,至少能在某種程度上代表當今國際研究的水平.此外,國際上最近出先了新的瀏覽器Firefox,其性能據(jù)說是遠高于ie,也許在網(wǎng)絡(luò)的天下,Ms乂有了更強勁的對手.國內(nèi)研究現(xiàn)狀國內(nèi)在應用層上的網(wǎng)絡(luò)應用軟件目前開展異常的火爆,由于我國有著網(wǎng)絡(luò)應用的最大的市場,現(xiàn)在國內(nèi)網(wǎng)絡(luò)的根底性建設(shè)開展迅速,應用軟件也層出不窮,其中,在游戲的領(lǐng)域中,網(wǎng)絡(luò)通信的工作做的不錯,如聯(lián)眾游戲平臺,還有其他的一些平臺,這些平臺根本上都是基于VC++的,用的都是Socket通信,但是為了效率,這些平臺沒有用MFC提供的CSocket類,而是直接用Socket進行通信.所以效率上不錯.此外,tencent的即時通信,也是做的很好的,從某中程度上來說,代表了國內(nèi)最高的水平.本課題的研究方法本系統(tǒng)采用C/S〔Client/Server〕結(jié)構(gòu)進行設(shè)計,使用SQLServer2000構(gòu)建數(shù)據(jù)庫,并在.NET環(huán)境下使用VisualC#.net語言和SOCKET套接字開發(fā)一個基于TCP協(xié)議的簡單即時通信軟件,實現(xiàn)簡單的即時聊天,文件傳輸?shù)裙δ?2相關(guān)技術(shù)介紹.NET開發(fā)平臺及Ctt.NET開發(fā)語言NET框架是Microsoft公司推出的一種全新的開發(fā)平臺,提供了統(tǒng)一的、面向?qū)ο蟛⑶铱梢詳U展的編程類庫和完善的集成開發(fā)環(huán)境,大大簡化了應用程序的開發(fā)過程,并且具有良好的移植性和平安性.微軟為了推行.NET戰(zhàn)略,特別為.NET平臺設(shè)計了一種語言一一C#oC#是由C和C++派生而來的一種“簡單、流行、面向?qū)ο?、類型平安〞的程序設(shè)計語言,其綜合了Visualbasic的高效率和C++的強大功能,然而更多的人感覺C#更類似JAVAoTCP協(xié)議TCP/IP網(wǎng)絡(luò)協(xié)議協(xié)議是對等的網(wǎng)絡(luò)實體之間通信的規(guī)那么,可以簡單地理解為網(wǎng)絡(luò)上各計算機彼此交流的一種“語言〞.網(wǎng)絡(luò)通信協(xié)議設(shè)計的根本原那么是層次化,層和協(xié)議的集合被稱為網(wǎng)絡(luò)體系結(jié)構(gòu).相鄰層之間的接口定義了下層向上層提供的根本操作和效勞,下層向上層提供的效勞分兩種形式:面向連接的效勞和無連接的效勞.計算機網(wǎng)絡(luò)中已經(jīng)形成的網(wǎng)絡(luò)體系結(jié)構(gòu)主要有兩個:051參考模型和TCP/IP參考模型.TCP/IP參考模型是因特網(wǎng)〔Internet〕的根底.和0SI的7層協(xié)議相比,TCP/IP協(xié)議只有4個層次.通常說的TCP/IP是一組協(xié)議的總稱,TCP/IP實際上是一個協(xié)議族,包括loo多個相互關(guān)聯(lián)的協(xié)議,其中IP〔lnternetProtocol,網(wǎng)際協(xié)議〕是網(wǎng)絡(luò)層最主要的協(xié)議:TCP〔TransmissionControlProtocol,傳輸限制協(xié)議〕和UDP〔UserDatagramProtocol,用戶數(shù)據(jù)報協(xié)議是傳輸層中最主要的協(xié)議〕,一般一―內(nèi)―認為IP、TCP、UDP是最根本的三種協(xié)議,是其他協(xié)議的根底.TCP——傳輸限制協(xié)議:面向連接的通信可以使用可靠通信,在這時候,第四層協(xié)議發(fā)送數(shù)據(jù)接收方確實認,如果未收到數(shù)據(jù)或者數(shù)據(jù)被損壞,那么請求重新傳輸.TCP協(xié)議就使用這種可靠通信.使用TCP協(xié)議的應用層協(xié)議包括HTTP、FTP、SMTP和Telnet等.現(xiàn)在可以發(fā)送和接收消息了.接收消息后,總是返回ACK消息.如果在收到ACK之前發(fā)送方已經(jīng)超時,那么消息將被放到重發(fā)隊列中以再次發(fā)送.由于它的握制,所以TCP協(xié)議比擬復雜并且費時,但此協(xié)議在處理數(shù)據(jù)時對數(shù)據(jù)包的傳送有保證,從而使得在應用程序協(xié)議中不需要再包括該功能.2.3套接字套接字這個術(shù)語并沒有定義某個協(xié)議:它具有兩層含義,但兩者都與一個協(xié)議相關(guān).第一個含義是套接字編程API,它最初由伯克利大學為BSDUNIX而創(chuàng)建.BSD套接字在經(jīng)過修改后被用作Windows環(huán)境的編程接口〔并且被命名為WinSock〕.WinSockAPI被包裝在System.Net.sockets命名空間的.NET類中.WindowsSockets是一個獨立于協(xié)議的編程接口,用于編寫網(wǎng)絡(luò)應用程序.2.4流241流的根本概念流的概念已經(jīng)存在很長時間了.流是一個用于傳輸數(shù)據(jù)的對象.數(shù)據(jù)的傳輸有兩個方向:二〕如果數(shù)據(jù)從外部源傳輸?shù)匠绦蛑?這就是讀取流.2〕如果數(shù)據(jù)從程序傳輸?shù)酵獠吭?這就是寫入流.外部源常常是一個文件,但也不完全都是文件,它還可以是:2〕網(wǎng)絡(luò),使用一定的網(wǎng)絡(luò)協(xié)議與網(wǎng)絡(luò)上其它計算機或終端交換數(shù)據(jù).2〕一個指定的管道.3〕一塊內(nèi)存區(qū)域.2.5同步、異步、阻塞和非阻塞同步〔synchronous〕:所謂同步方式,就是發(fā)送方發(fā)送數(shù)據(jù)包以后,不等接一―內(nèi)-受方響應,就接著發(fā)送下一個數(shù)據(jù)包.異步(asynchronous):異步方式就是當發(fā)送方發(fā)送一個數(shù)據(jù)包以后,一直等到接受方響應后,才接著發(fā)送下一個數(shù)據(jù)包.阻塞(Block):指執(zhí)行此套接字的網(wǎng)絡(luò)調(diào)用時.,直到調(diào)用成功才返回,否那么此套節(jié)字就一直阻塞在網(wǎng)絡(luò)調(diào)用上,比方調(diào)用StreamReader類的Readlin()方法讀取網(wǎng)絡(luò)緩沖區(qū)中的數(shù)據(jù),如果調(diào)用的時候沒有數(shù)據(jù)到達,那么此Readlin()方法將一直掛在調(diào)用上,直到讀到一些數(shù)據(jù),此函數(shù)調(diào)用才返回.非阻塞(Unblock):指在執(zhí)行此套接字的網(wǎng)絡(luò)調(diào)用時;不管是否執(zhí)行成功,都立即返回.同樣調(diào)用StreamReader類的Readlin.方法讀取網(wǎng)絡(luò)緩沖區(qū)中數(shù)據(jù),不管是否讀到數(shù)據(jù)都立即返回,而不會一直掛在此函數(shù)調(diào)用上.在Windows網(wǎng)絡(luò)通信軟件開發(fā)中,最為常用的方法就是異步非阻塞套接字.平常所說的C/S(客戶端/效勞器)結(jié)構(gòu)的軟件采用的方式就是異步非阻塞模式的.其實在用C#進行網(wǎng)絡(luò)編程中,我們并不需要了解什么同步、異步、阻塞和非阻塞的原理和工作機制,由于在.NetFrameWrokSDK中已經(jīng)已經(jīng)把這些機制給封裝好了.3.系統(tǒng)總體設(shè)計33需求分析軟件針對局域網(wǎng)內(nèi)部用戶,實現(xiàn)用戶間的即時通信.需要分別實現(xiàn)效勞器端和客戶端的軟件設(shè)計.效勞器端負責監(jiān)聽用戶連接請求,負責連接數(shù)據(jù)庫存儲用戶信息,負責發(fā)送給用戶好友信息,負責發(fā)送心跳報文檢查用戶在線狀態(tài)并即時讓用戶更新好友在新信息.客戶端發(fā)起主動連接,向效勞器請求登錄或者注冊.客戶端可以修改昵稱,可以加用戶為好友(類似于MSN的好友添加功能).客戶端之間可以發(fā)起P2P模式的聊天,可以傳送文件.系統(tǒng)根本架構(gòu)基于C/S架構(gòu)的即時通信軟件便于對用戶信息進行統(tǒng)一治理和保存,面向特定的用戶,對信息的平安限制水平很強.為了減輕效勞器負擔,客戶端之間的信息傳遞是采用P2P模式的,效勞器只負責用戶的注冊,登錄和用戶在線狀態(tài)的檢驗.根本結(jié)構(gòu)如圖:圖1原統(tǒng)西本期恂功能模塊設(shè)計CLIENT:.注冊:⑴可以完成客戶端注冊,客戶端可以通過填寫信息進行注冊,信息被發(fā)送到效勞器端..登錄:⑴客戶可以輸入賬號和密碼進行登錄,客戶端會發(fā)送登錄信息等待效勞器響應,登錄成功后會發(fā)出登錄成功信息并刷新好友列表..修改:⑴密碼修改:應該有密碼修改功能〔2〕信息修改:可以更改一些注冊信息.通信:⑴即時聊天模塊:客戶端與客戶端之間建立線程進行即時聊天,也包含有簡單的對稱加解密算法功能.〔2〕好友列表:可以對好友列表進行添加刪除等動作..文件傳輸:⑴文件傳輸:文件傳輸功能SERVER:.注冊回應:對客戶端傳送的注冊信息進行判斷.〔i〕HASH加密:對用戶的賬號和密碼信息進行HASH加密Q〕重復用戶檢查:將加密后信息與已存在賬號進行比擬,檢查是否賬號已存在,如果存在就返回錯誤信息〔3〕注冊成功:將可成功注冊的用戶賬號和密碼寫入數(shù)據(jù)庫內(nèi),并向客戶端返回成功信息.修改回應:⑴對密碼和信息修改請求進行判斷,執(zhí)行和返回修改成功信息..登錄回應:⑴對登錄的賬號和密碼進行加密檢查后發(fā)回正確或錯誤情況,并記錄上線信息.〔2〕好友列表發(fā)送:給成功登錄的賬號發(fā)送好友列表及好友上線信息.〔3〕上線信息發(fā)送:給成功登錄的賬號的好友發(fā)送在線信息〔包括舊端口等等信息〕..在線情況:⑴對登錄,在線,離線的用戶情況進行統(tǒng)計,記錄和通知⑵心跳測試:每隔一段時間發(fā)送報文測試用戶是否因意外原因離線〔3〕情況記錄:將用戶登錄時間,IP,下線時間等信息記錄入數(shù)據(jù)庫34邏輯圖:3.5數(shù)據(jù)庫設(shè)計3.5-1實體關(guān)系圖效勞器是作為記錄和讀取數(shù)據(jù)庫信息的載體,與客戶端關(guān)系并不復雜,這里需要重點考慮客戶端之間的關(guān)系.用戶與用戶之間的關(guān)系是較為特殊的遞歸關(guān)系,即描述發(fā)生在兩個相同實體上的關(guān)系.4系統(tǒng)實現(xiàn)使用XML定義的即時通信協(xié)議信息結(jié)構(gòu)MESSAGE.CS&UMESSAGE.CS這兩個C#類定義了包括效勞器信息,狀態(tài)信息,注冊信息,登錄信息,聊天信息或者請求文件傳輸信息的函數(shù),效勞器和客戶端通過將它們實例化和序列化再轉(zhuǎn)換成流在網(wǎng)絡(luò)上進行傳輸.UMESSAGE.CS主要代碼如下:[Serializable]publicclassUMessage{publicUMessage(){}privatestring-nickname;privatestring-password;privatestring-accounts;privatestring_email;privateint_inf.;〃表示注冊或者登錄信息,客戶端信息.為注冊,t為登錄;效勞器返回信息.為用戶已存在,1為注冊成功,2為效勞器未知錯誤,3為CLIENT在線檢查,1.為登錄失敗,it為登錄成功privateFriend[]_friend;privateint_fn;privatestring_fg;publicstringNickname[get{return-nickname;}set{.nickname=value;}|publicstringPasswordget{return_password;}set(-password=value;}}publicstringAccounts{get{return-accounts;}set{-accounts=value;}}publicstringEmail!get{return.email;}set{_email=value;}|publicintInfo{get{returnJnfo;}set{Jnfo=value;}}publicFriend[]Fri{get{return_friend;}set{_friend=value;}}publicintFnget{return_fn;}set{_fn=value;}publicstringFgget{return_fg;}set{_fg=value;}]}由于MESSAGE.CS與UMESSAGE.CS類似,在此不再詳述.效勞器和客戶端都可以通過相同的代碼對UMESSAGE賦值,再通過XmlSerializer方法進行將UMESSAGE序歹lj化為XML文檔,最后將XML文檔轉(zhuǎn)化為網(wǎng)絡(luò)流進行傳輸.代碼如下:#region將登錄信息轉(zhuǎn)為UMessageprivatevoidTraslator()_message.Accounts=this.TextBoxi.Text;_message.Nickname="";_message.Password=this.TextB0X2,Text;_message.Email="";_message.lnfo=i;_message.Fri=null;}#endregion數(shù)據(jù)結(jié)構(gòu)FriendStruct效勞器如果保存和傳遞用戶的好友信息是難點之一.數(shù)據(jù)庫的設(shè)計和信息的傳遞區(qū)分都是比擬難實現(xiàn)的.在數(shù)據(jù)庫方面,每個用戶擁有各自的好友分組信息(UserFav),分組中間使用“,〞分隔,在TCP_Friendlnf.表中那么分別保存了用戶ID和好友ID,使用一個INT字段保存分組信息.數(shù)據(jù)庫以用戶ID為標準對好友ID和分組信息進行內(nèi)連接查詢,就可以得到根本的好友信息了.代碼如下:一聊內(nèi)一select*fromTCP_UserlnfojoinTCP_FriendlnfoonTCP_Friendlnfo.U5erlD="'+uid+andTCP_Userlnfo.UserlD=TCP_FriendInfo.FriendID在好友信息的傳輸方面,首先定義一個FriendStruct數(shù)據(jù)結(jié)構(gòu)〔當然也可以用枚舉完成〕如下:usingSystem;usingSystem.Collections.Generic;usingSystem.Text;namespaceTCPpublicclassFriendStructpublicstructFileinfo{publicintfilere;〃接收和拒絕信息,1為接收,2為拒絕,3為取消publicstringfilename;publiclongfilelength;}publicstructFriendpublicstringaccount;publicstringnickname;publicstringIP;publicstringstatus;publicstringfg;〃好友分組1}在MESSAGE.CS或者UMESSAGE.CS中,我們那么定義了FriendStruct的數(shù)組.?那內(nèi)一在C#中使用DATAREADER語句可以逐句讀取數(shù)據(jù)庫查詢的結(jié)果,再依次將結(jié)果賦值FriendStruct數(shù)組元素,就得到了便于發(fā)送和讀取的存放好友信息的數(shù)組.賦值代碼如下:while(getf.Read())//getf即是以上的數(shù)據(jù)庫查詢的datareader語句{ff[i].account=getf[,,UserAccount"].ToString();ff[i].IP=getfl'^serlR'lToStringO;ff[i].nickname=getf["UserNickname"].ToString();ff[i].status=getf["UserOnline"].ToString();ff[i].fg=getf["FriendGroup"].ToString();i++;|getf.Close();42數(shù)據(jù)庫連接類實現(xiàn)一個快捷簡單的數(shù)據(jù)庫連接的相關(guān)代碼是非常有必要的.實現(xiàn)的途徑也多種多樣,鑒于平安性和復雜性的需求不同,實現(xiàn)方法有簡有繁.本設(shè)計使用了一個簡單的類(UserData.CS)實現(xiàn)了簡單快捷的數(shù)據(jù)庫連接和讀取.主要代碼如下:publicstaticSqlConnectionconnStr=newSqlConnection("Server=Dg6B85DD938A465.;uid=sa;pwd=change;database=TCPDB");publicstaticSqlDataReaderSqlReader(stringsql,SqlConnectionconnstr)SqlDataReadersqldr=null;SqlCommandemd=newSqlCommand(sqlzconnstr);if(cmd.Connection.State.ToStringO=="Closed")cmd.Connection.Open()try(sqldr=cmd.ExecuteReader();}catch(Exceptione)(catch(Exceptione)(if(e!=null)sqldr=null;]returnsqldr;}//數(shù)據(jù)庫操作連接publicstaticstringSqlCmd(stringsqlzSqlConnectionconnstr)(stringerrorstr=null;SqlCommandsqlcmd=newSqlCommand(sql,connstr);if(sqlcmd.Connection.State.ToStringO=="Open")sqlcmd.Connection.Close();sqlcmd.Connection.Open();trysqlcmd.ExecuteNonQueryO;catch(Exceptione)if(e!=null)errorstr=e.ToString();1sqlcmd.Connection.Close();returnerrorstr;}在UserData.CS的根底上,主程序可以更方便地實現(xiàn)數(shù)據(jù)庫連接操作,對數(shù)據(jù)庫進行讀寫和更新,在此不再詳述.43效勞器端效勞器端的界面設(shè)計是基于便于測試的目的而實現(xiàn)的.如下列圖:圖4效勞器端界面4.3」同步套接字網(wǎng)絡(luò)監(jiān)聽基于同步套接字的網(wǎng)絡(luò)監(jiān)聽器對效勞器來說并不是最好的解決方案,但是仍然可行并且實現(xiàn)簡單.主要代碼如下:開啟監(jiān)聽端口:publicvoidServe(){intport=8888;ServerlPEP=newIPEndPoint(IPAddress.Any,port);s=newSocket(ServerlPEP.AddressFamily,SocketType.Stream,ProtocolType.Tcp);s.Bind((EndPoint)ServerlPEP);s.Listen(io);alSock=newArrayList();以下代碼讀取連入的連接,依次將連接參加可變長數(shù)組alsock,并且讀取傳入的信息,進行反串行化:while(true)!try{uc=s.Accept();alSock.Add(uc);this.tb_states.AppendText(System.Convert.ToString(uc));byte[]data=newbyte[2O48];intrect=uc.Receive(data);byte[]chat=newbyte[rect];Buffer.BlockCopy(datazo,chat,o,rect);UMessageumessage=(UMessage)_translator.Deserialize(newMemoryStream(chat));intinfo=umessage.Info;對反串行化后的信息進行處理,通過inf.參數(shù)識別客戶端行為(注冊或者登錄),對注冊的信息進行數(shù)據(jù)庫查詢,注冊信息可插入,那么將?用戶信息插入數(shù)據(jù)庫,否那么返回客戶端“注冊出錯〞的信息:^region處理用戶注冊信息if(info==o)〃分辨出用戶發(fā)送的是注冊信息stringAccounts=umessage.Accounts;SqlDataReaderusdr=FPara.SqlReader("select*fromTCP_UserInfowhereUserAccount』"+Accounts+FPara.connStr);if(usdr!=null){if(usdr.Read())(#region此處寫入返回注冊失敗的代碼Socketsc=(Socket)alSock[alSock.lndexOf(uc,o)];sc.Send(chat);#endregion!else{#region此處寫入插入數(shù)據(jù)庫用戶注冊信息的代碼Streamms=newMemoryStream();Socketsc=(Socket)alSock[alSock.lndexOf(uc,o)];if(FPara.SqlCmd(NinsertintoTCP.Userlnfo(UserAccount,UserNickname,UserEmail,JoinDate,UserIP,UserPassword)values."+umessage.Accounts++umessage.Nickname++umessage.Email+"'J11+System.DateTime.Now.ToStringO++((IPEndPoint)uc.RemoteEndPoint).Address.ToStringO++umessage.Password+llT,FPara.connStr)==null)(umessage.Info=i;_translator.Serialize(ms,umessage);byte[]d=newbytefms.Length];ms.Seek(o,SeekOrigin.Begin);ms.Read(dzozd.Length);sc.Send(d);}else(umessage.lnfo=2;_translator.Serialize(ms,umessage);byte[]d=newbytefms.Length];ms.Seek(o,SeekOrigin.Begin);ms.Read(dzo,d.Length);sc.Send(d);}#endregion}usdr.Close();|]#endregion如果發(fā)現(xiàn)用戶發(fā)送的是登錄信息,就根據(jù)登錄信息中的用戶名和密碼判斷是否存在用戶,密碼是否正確,成功后再查詢出用戶的好友信息并且賦值給FriendStruct,再將信息返回給客戶端:#region處理用戶登錄信息elseif(inf.==」)//分辨出用戶發(fā)送的是登錄信息stringAccounts=umessage.Accounts;stringPassword=umessage.Password;SqlDataReaderusdr=FPara.SqlReader(,,select*fromTCP_UserlnfowhereUserAccount=n,+Accounts+1,1andUserPassword=,H+Password+H,,,ZFPara.connStr);if(usdr!=null){if(usdr.Read()){stringuid=usdr[,,UserlDl,].ToString();umessage.Fg=usdr["UserFav〞],ToString();usdr.Close();SqlDataAdaptersdr=newSqlDataAdapter(Hselect*fromTCP_UserlnfojoinTCP_FriendlnfoonTCP_Friendlnfo.UserlD=,"+uid+andTCP_Userlnfo.UserlD=TCP_FriendInfo.FriendIDN,FPara.connStr);DataSetds=newDataSet.;sdr.FiIKds/'find");intxxx=ds.Tables[l,findl,].Rows.Count;FPara.SqlCmd("updateTCP_UserlnfosetUserlP=H,+((IPEndPoint)uc.RemoteEndPoint).Address.ToStringO+1,1,UserOnline=iwhereUserAccount=l11+Accounts+FPara.connStr);ff=newFriend[xxx];inti=o;SqlDataReadergetf=FPara.SqlReader(l,select*fromTCP_UserlnfojoinTCP_FriendInfoonTCP_Friendlnfo.UserlD='"+uid+andTCP_Userlnfo.UserlD=TCP_FriendInfo.FriendID11,FPara.connStr);while(getf.Read())(ff[i].account=getf[,,UserAccount,,].ToStnng();ff[i].IP=getft^serlP'lToStringO;ff[i].nickname=getf[,,UserNickname,,].ToString();ff[i].status=getf[llUserOnline,,].ToString();ff[i].fg=getf["FriendGroup"].ToString();i++;1getf.Close();#region此處寫入登錄成功代碼Streamms=newMemoryStream();Socketsc=(Socket)alSock[alSock.lndexOf(uc,o)];this.lb_users.Items.Add(alSock.lndexOf(uc).ToString());□message.Info=11;umessage.Fri=ff;umessage.Fn=xxx;_translator.Serialize(ms,umessage);byte[]d=newbyte[rr)s.Length];ms.Seek(o,SeekOrigin.Begin);ms.Read(d,o,d.Length);sc.Send(d);〃在tb-status中寫入效勞器返回給客戶端的代碼便于測試觀察this.tb_states.AppendText(System.Text.Encoding.Default.GetString(d));#endregion}else{usdr.Close();#region此處寫入登錄失敗代碼Streamms=newMemoryStream();Socketsc=(Socket)alSock[alSock.lndexOf(uc,o)];umessage.lnfo=10;_translator.Serialize(ms,umessage);byte[]d=newbyte[ms.Length];ms.Seek(o,SeekOrigin.Begin);ms.Read(dzo,d.Length);sc.Send(d);#endregion}}#endregionTb-states是個用于監(jiān)視SOCKET傳入信息的文本框,便于觀察和測試相關(guān)信息:this.tb-states.AppendText(,,[u+uc.RemoteEndPoint.ToStringO++System.Text.Encoding.Default.GetString(chat));|catch(Exceptionex){MessageBox.Show(ex.Message);}}}以上代碼也包含了對客戶端的請求信息的判斷和對客戶端返回信息的生成和傳輸.432多線程對于效勞器來說,多線程是必不可少的,否那么它將無法處理不斷請求的新連接.C#的System.Threading提供了多線程編程的支持.本設(shè)計實現(xiàn)代碼如下:this.th=newThread(newThread5tart(Serve));〃新建一個用于監(jiān)聽的線程th.5tart();〃翻開新線程不僅僅是效勞器,基于P2P模式聊天的客戶端也必須支持多線程運行,實現(xiàn)代碼與之類似,在客戶端設(shè)計說明中將不再表達.4.3-3計時器計時器用于實現(xiàn)心跳報文的功能,效勞器在啟動以后就開始計時,每隔一定時間就向所有連入的客戶端發(fā)送信息,核心代碼如下:〃用計時器檢查客戶端是否掉線System.Timers.TimeraTimer=newSystem.Timers.Timer();aTimer.Elapsed+=newElapsedEventHandler(CheckStatus);〃設(shè)置引發(fā)時間的時間間隔此處設(shè)置為5秒(5000毫秒)aTimer.lnterval=5000;aTimer.Enabled=true;CheckStatus就是用于向客戶端發(fā)送檢查信息的方法,它會向遍歷連入的客戶端(alSock),然后依次向客戶端發(fā)送信息,如果發(fā)現(xiàn)客戶端沒有響應,就會如果發(fā)現(xiàn)對方無回應,那么關(guān)閉相應的SOCKET,并更新數(shù)據(jù)庫的用戶在線狀態(tài),同時向該用戶的所有好友發(fā)送用戶已下線的通知.4-4客戶端圖5注冊界面圖6登錄、聊天、文件傳輸界面4?4客戶端圖5注冊界面圖6登錄、聊天、文件傳輸界面4.4.1同步套接字客戶端客戶端發(fā)起同步套接字連接,并傳送登錄或者注冊信息,由于兩者方式類似,這里僅列出用戶登錄的代碼:#region發(fā)送效勞器登錄信息,并接收效勞器反應信息publicvoidClient.建立SOCKET發(fā)送信息:try{IPEndPointServerlPEP=newIPEndPoint(IPAddress.Parse(,,222.i8.i7O.i6,,)/8888);c=newSocket(ServerlPEP.AddressFamily,SocketType.Stream,ProtocolType.Tcp);?那內(nèi)一c.Connect((EndPoint)ServerlPEP);s=newMemoryStream();_translator.Serialize(s,_message);byte[]d=newbyte[s.Length];s.Seek(o,SeekOrigin.Begin);s.Read(dzo,d.Length);inti=c.Send(d,o,d.Length,SocketFlags.None);}catch(Exceptionex){MessageBox.Show(ex.Message);)以下代碼讀取了效勞器返回給客戶端的信息(注冊和登錄的成功與失敗),如果返回了登錄成功的信息,還會讀取效勞器給出的FriendStruct結(jié)構(gòu)以得到用戶的好友信息:#region接收反應信息byte[]data=newbyte[2O48];while(true){intrect=c.Receive(data);byte[]chat=newbyte[rect];Buffer.BlockCopy(data/o,chat/o/rect);UMessagebumessage=(UMessage)_translator.Deserialize(newMemoryStream(chat));string[]fg;string_fg=bumessage.Fg;if(bumessage.lnfo==3){}elseif(bumessage.lnfo==n)]fg^fg.SplitC/);intxxx=bumessage.Fn;ff=bumessage.Fri;for(inti=o;i<xxx;i++){string[]ems=newstring—];ems[o]=ff[i].account;ems[i]=ff[i].nickname;ems[2]=fg[int.Parse(ff[i].fg)];ems[3]=ff[i].IP;ems[4]=ff[i].status;ListViewItemitem=newListViewltem(ems);this.listViewi.Items.Add(iterr));CSERVER是一個用于開啟監(jiān)聽P2P信息的方法,客戶端在登錄成功以后就會馬上開啟監(jiān)聽器,才能夠?qū)崿F(xiàn)與其它客戶端的聊天:Th=newThread(newThreadStart(CServer));〃新建一個用于監(jiān)聽其它客戶端信息的線程th.Start();〃翻開新線程MessageBox.Show(bumessage.Accounts+,,S^^<^b!");this.Buttoni.Enabled=false;this.Button3.Enabled=faIse;elseif(bumessage.lnfo==2)MessageBox.Show〔"效勞器未知錯誤〞〕;else{MessageBox.Show(bumessage.lnfo.ToStringO);}}#endregion}#endregion施依川由不欣圖7登錄成功后的客戶端界面客戶端之間的聊天同樣使用了序列化的XML文檔,用戶在登錄成功后就會啟動一個新的監(jiān)聽器去監(jiān)聽其它客戶端傳入的聊天信息并且進行判斷再將其它用戶的聊天信息顯示在界面上.這里也不再闡述代碼.42采用異步套接字的文件傳輸文件傳輸是通過一個類庫實現(xiàn)的.由于文件傳輸?shù)拇a實現(xiàn)復雜,通過類庫可以大量的簡化代碼,使主程序簡潔易懂.類庫Infinity.Networking包括了ClientBase.cS/Clientlnfo.cS/Delegates.csJNPCIient.csJNPServer.cs,ClientBase.cs定義了根底的文件發(fā)送函數(shù),INPQient.cs那么僅包含初始化文件發(fā)送的函數(shù);ServerBase.cs和INPServer.es那么是反之亦然.核心代碼如下:ClientBase.cs:這個類實現(xiàn)了套接字的開啟和數(shù)據(jù)的傳輸ClientBase.cs:這個類實現(xiàn)了套接字的開啟和數(shù)據(jù)的傳輸usingSystem;usingSystem.Net;usingSystem.Net.Sockets;namespaceInfinity.Networking{〃/〈summary〉IIIClientBase摘要.III〈/summary〉publicclassClientBase(privateconstintBUFFERSIZE=4*1024;privateint_port;privatestring_serverlP;privateSocket.mainSoc;privateClientinfoJnfo;privateAsyncCallback_dataRecievedCallback;〃異步回調(diào)方法publiceventNetworkEventHandlerDataRecieved;〃定義一個事件:接收到數(shù)據(jù)時引發(fā)事件publicClientBase(stringserverIPJntport)(..serverIP=serverIP;.port=11000;_mainSoc=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);_info=newClientlnfo(_mainSoc,newbyte[BUFFERSIZE]);//Clientlnfo包含

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論