C#下用P2P技術(shù)實現(xiàn)點對點聊天

2010-08-28 10:50:44來源:西部e網(wǎng)作者:

P2P技術(shù)簡介

P2P,英文Peer-to-Peer的縮寫,中譯為對等互聯(lián)或點對點技術(shù)。P2P技術(shù)可以讓用戶可以直接連接到其他用戶的計算機(jī),進(jìn)行文件共享與交換,同時P2P在深度搜索、分布計算、協(xié)同工作等方面也大有用途。

簡單地說,P2P就是一種用于不同PC用戶之間,不經(jīng)過中繼設(shè)備直接交換數(shù)據(jù)或服務(wù)的技術(shù),它允許Internet用戶直接使用對方的文件。每個人可以直接連接到其他用戶的計算機(jī),并進(jìn)行文件的交換,而不需要連接到服務(wù)器上再進(jìn)行瀏覽與下載。

目前Internet的存儲模式是“內(nèi)容位于中心”,而P2P技術(shù)的運用將使Internet上的內(nèi)容向邊緣移動。這將帶來以下改變:

首先,客戶不再需要將文件上傳到服務(wù)器,而只需要使用P2P與其他計算機(jī)進(jìn)行共享;

其次,使用P2P技術(shù)的計算機(jī)不需要固定的IP地址和永久的Internet連接,這使得占有極大比例的撥號上網(wǎng)用戶也可以享受P2P帶來的變革。

實例介紹


理解P2P技術(shù)的最好方法莫過于仔細(xì)觀察并理解一個實際的P2P應(yīng)用程序。C#作為微軟.Net戰(zhàn)略的重要棋子,對網(wǎng)絡(luò)編程提供了很好的支持和優(yōu)化。本文就通過一個程序,向大家介紹一下C#下的P2P編程的方法和實現(xiàn)機(jī)理。本文的這個程序不是很有用,但卻很直觀地給出了P2P(點對點)編程以及套接口編程的一些基本知識和概念。它是建立在TcpListener以及TcpClient這兩個類基礎(chǔ)上的。程序?qū)崿F(xiàn)的原理也比較簡單,但是用到了P2P技術(shù)重返“非中心化”的基本原則。簡言之,用這個程序可以在網(wǎng)絡(luò)中發(fā)送、接受消息,任何一臺計算機(jī)既可以作為服務(wù)器端,又可以作為客戶端。

系統(tǒng)要求:

1.Widows 2000服務(wù)器版操作系統(tǒng)或后續(xù)版本的操作系統(tǒng)。

2.Visual Studio.net Framework正式版。

具體方法:

首先,打開VS.net,新建一個C#項目(注意:模板為Windows應(yīng)用程序),不妨命名為"P2Pchat"。圖示如下:

\



其次,參照ICQ、OICQ等聊天工具,我們可以將程序的主界面布置成和常見聊天工具的消息發(fā)送對話框類似的布局。不過由于程序僅一個主界面,所以還要添加一些諸如“開始監(jiān)聽”、“停止監(jiān)聽”等控制按鈕。同時,程序是支持昵稱顯示的。于是,按照這個思想,我們可以開始布置程序的主界面了。首先,往主界面上拖放如下一些控件:兩個Label控件、三個Button控件、三個TextBox控件、一個RichTextBox控件以及一個StatusBar控件。各個控件的屬性設(shè)置如下表所示:

Form1 Text屬性 P2P聊天工具
Label1 Text屬性 目標(biāo)計算機(jī)地址:
TestSlign屬性 MiddleCenter
Label2 Text屬性 昵稱:
TextAlign屬性 MiddleCenter
Button1 Text屬性 開始監(jiān)聽
FlatStyle屬性 Flat
Button2 Text屬性 停止監(jiān)聽
FlatStyle屬性 Flat
Enabled屬性 False
Anchor屬性 Top, Right
Button3 Text屬性 發(fā)送消息
FlatStyle屬性 Flat
Anchor屬性 Top, Right
TextBox1 Text屬性 (空)
Anchor屬性 Top, Left, Right
TextBox2 Text屬性 (空)
Multiline屬性 True
Anchor屬性 Top, Left, Right
TextBox3 Text屬性 (空)
ForeColor屬性 Firebrick
Anchor屬性 Top, Left, Right
RichTextBox1 Text屬性 (空)
ForeColor屬性 Steelblue
ReadOnly屬性 True
Anchor屬性 Top, Bottom, Left, Right



注:其中的Anchor屬性是設(shè)置窗體上控件布局用的,當(dāng)窗體大小改變后各個控件如何在窗體上重新分布由該屬性決定,讀者可參考更詳細(xì)的介紹文章來理解。

其他屬性為默認(rèn)即可,最終布置主窗體界面如下所示:

\


現(xiàn)在到了程序的主體部分,即代碼部分。在給出代碼之前,我想先向大家介紹一下實現(xiàn)的基本原理以及其中的一些邏輯關(guān)系。

首先,程序運行后,用戶得先按下“開始監(jiān)聽”按鈕,按鈕相應(yīng)事件后,程序就進(jìn)入了監(jiān)聽狀態(tài),狀態(tài)欄有相應(yīng)的顯示。這樣,本機(jī)就相當(dāng)于“服務(wù)器/客戶機(jī)”模式中的服務(wù)器了,其他計算機(jī)可以連接到本機(jī)并向本機(jī)發(fā)送消息。其他計算機(jī)通過該程序連接到本機(jī)是通過IP地址來實現(xiàn)的,C#對網(wǎng)絡(luò)編程有很好的支持,所以程序員的工作量是比較小的。如此,一臺計算機(jī)可以向另一臺發(fā)送消息了。然而,這是個P2P程序,所以只要另一臺計算機(jī)的用戶也按下"開始監(jiān)聽"按鈕,那臺計算機(jī)也成了這臺計算機(jī)的服務(wù)器了。于是就實現(xiàn)了消息互發(fā)功能,然而真正的服務(wù)器是不存在的,每臺計算機(jī)都是服務(wù)器,每臺計算機(jī)同時也是客戶機(jī),這就體現(xiàn)了P2P技術(shù)的"非中心化"原則。

程序主要用到了一個Listen()函數(shù)和一個Send()函數(shù)。前者實現(xiàn)程序的監(jiān)聽功能,函數(shù)實現(xiàn)如下:

private void Listen()
            {
            try
            {
            tcpl = new TcpListener(5656);
            tcpl.Start();
            statusBar1.Text = "正在監(jiān)聽...";
            while(listenerRun)
            {
            Socket s = tcpl.AcceptSocket();
            Byte[] stream = new Byte[80];
            int i=s.Receive(stream) ;
            string message =
            System.Text.Encoding.UTF8.GetString(stream);
            richTextBox1.AppendText(message);
            }
            }
            catch(System.Security.SecurityException)
            {
            MessageBox.Show("防火墻安全錯誤!","錯誤",
            MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            catch(Exception)
            {
            statusBar1.Text = "已停止監(jiān)聽!";
            }
            }


函數(shù)的主體是一個try-catch語句,try部分又是一個while循環(huán),這表示只要用戶不按"停止監(jiān)聽"按鈕,程序就會一直處于監(jiān)聽狀態(tài)。監(jiān)聽的端口是5656,這個端口是可以自己定義的,只要不跟常用的端口混淆就行了。一旦程序接收到遠(yuǎn)程計算機(jī)的一條消息,就將該消息添加到消息顯示框中(消息顯示框就是那個RichTextBox控件)。函數(shù)的catch部分是捕捉一些異常用的,如用戶之間設(shè)置了防火墻,就不能彼此通訊了,或是對方已經(jīng)停止監(jiān)聽了,那當(dāng)然就不能向它發(fā)送消息了。另一個函數(shù)Send()是實現(xiàn)程序發(fā)送消息的功能的。函數(shù)實現(xiàn)如下:

private void Send()
            {
            try
            {
            string msg = "<"+textBox3.Text+">"+textBox2.Text;
            TcpClient tcpc = new TcpClient(textBox1.Text, 5656);
            NetworkStream tcpStream = tcpc.GetStream();
            StreamWriter reqStreamW = new
            StreamWriter(tcpStream);
            reqStreamW.Write(msg);
            reqStreamW.Flush();
            tcpStream.Close();
            tcpc.Close();
            richTextBox1.AppendText(msg);
            textBox2.Clear();
            }
            catch(Exception)
            {
            statusBar1.Text = "目標(biāo)計算機(jī)拒絕連接請求!";
            }
            }


該函數(shù)的主體部分也是一個try-catch語句,它先根據(jù)用戶的輸入,建立一個和遠(yuǎn)程計算機(jī)的連接,注意其端口也為5656,而且必須是5656,這是為了和接收方端口保持一致,這樣對方才能收到這里發(fā)送的消息。接著,函數(shù)根據(jù)用戶在消息輸入框中的內(nèi)容以及用戶的昵稱向遠(yuǎn)程計算機(jī)發(fā)送消息。這樣,只要網(wǎng)絡(luò)無故障、遠(yuǎn)程計算機(jī)已經(jīng)處于監(jiān)聽狀態(tài),它就能接收到這里發(fā)送的消息了。當(dāng)然,這里處于監(jiān)聽狀態(tài)了,遠(yuǎn)程的計算機(jī)也可以自如地往這里發(fā)消息。函數(shù)的catch部分也是用于捕捉一些異常的。

同時還要注意的是,由于該程序用到了許多網(wǎng)絡(luò)編程所需的對象以及輸入輸出對象,又運用了多線程編程機(jī)制,所以在程序的開始出得添加如下一些名字空間:

using System.IO;
            using System.Net.Sockets;
            using System.Threading;


最后,程序中各個控件的事件處理函數(shù)以及完整的代碼請參看文后附帶的源代碼包。程序運行的圖示如下:
\


現(xiàn)在一個很基本的P2P運用程序以及完成,通過它,我們可以利用P2P技術(shù)的基本特性實現(xiàn)點對點通信。通過這個程序,我相信大家對C#下的P2P編程應(yīng)該有了大致的了解。對于這個程序,不足的一點是功能比較簡單,只可以發(fā)送、接受信息,而且不能穿過防火墻進(jìn)行通訊,讀者可以試著開發(fā)出功能更強(qiáng)P2P應(yīng)用程序。

總結(jié)

最后,筆者希望能通過此文喚起大家對P2P技術(shù)的興趣。因為P2P身后所蘊藏著的無比的創(chuàng)造力使人們對未來互聯(lián)網(wǎng)充滿了美好的憧憬,現(xiàn)在世界范圍的P2P應(yīng)用熱潮也是一浪高過一浪。在可以預(yù)見的未來,隨著對P2P研究的進(jìn)一步深入和關(guān)注P2P的群體逐漸增多,P2P必將進(jìn)入一個飛速發(fā)展的新時期。然而國內(nèi)的P2P起步得比較晚,所以更需要有大量的技術(shù)研究投入以及足夠的重視以贏得更好的發(fā)展。在此,筆者希望國內(nèi)的P2P能取得輝煌的發(fā)展。

源代碼下載:立即下載

關(guān)鍵詞:C#