一.前言:
P2P,即英文Peer-to-Peer的縮寫,中譯為對等互聯或點對點技術。講到P2P,人們就會想起Napster,Napster讓人們認識到了P2P技術的威力,P2P技術也就通過Napster進入了大多數用戶的視野,Napster的音樂文件交換功能是P2P的一個主要應用。P2P技術可以讓用戶可以直接連接到其他用戶的計算機,進行文件共享與交換。同時P2P在深度搜索、分布計算、協同工作等方面也大有用途。
簡單地說,P2P就是一種用於不同PC用戶之間,不經過中繼設備直接交換數據或服務的技術,它允許 Internet用戶直接使用對方的文件。每個人可以直接連接到其他用戶的計算機,並進行文件的交換,而不需要連接到服務器上再進行浏覽與下載。因為消除了中間環節,P2P技術使得網絡上的溝通變得更容易、更直接。P2P改變了Internet現在的以大網站為中心的狀態、重返"非中心化",並把權力交還給用戶。從某種意義上講,P2P體現了Internet的本質。在網絡尚未發展成為現在的Web之前,網民就是利用所謂的"布告板"等渠道彼此直接交換信息和文件。
目前Internet的存儲模式是"內容位於中心",而P2P技術的運用將使Internet上的內容向邊緣移動。這將帶來以下改變:首先,客戶不再需要將文件上傳到服務器,而只需要使用P2P與其他計算機進行共享;其次,使用P2P技術的計算機不需要固定的IP地址和永久的Internet連接,這使得占有極大比例的撥號上網用戶也可以享受P2P帶來的變革。
理解P2P技術方面的最好方法是仔細觀察並理解一個實際的P2P應用程序。C#作為微軟.Net戰略的重要棋子,對網絡編程提供了很好的支持和優化。本文就通過一個程序,向大家介紹一下C#下的 P2P編程的方法和實現機理。本文的這個程序雖然不是很有用,但卻很直觀地給出了P2P(點對點)編程以及套接口編程的一些基本知識和概念。它是建立在 TcpListener以及TcpClient這兩個類基礎上的,除外還有相應的輸入和輸出控制。實現的原理也比較簡單,但是用到了P2P技術重返"非中心化"的基本原則。簡言之,用這個程序可以在網絡中發送、接受信息,任何一台計算機既可以作為服務器端,又可以作為客戶端。程序共用到了四個類:一個 Listener類(用來監聽新的連接)、一個Sender類(用來發送信息)、一個Inputhandler類(用來控制輸入)、一個 Initialize類(用來完成初始化工作)。下面,我先給大家介紹一下這四個類,最後再給出程序的具體實現方法。
二.基本類介紹:
1.Listener類:
Listener類是用來監聽新的連接。當它的一個對象被建立並開啟後,該對象就開始不斷監聽來自網絡中的連接請求。一旦有了一個連接請求,該對象就設法建立連接並取得它的字節流進而轉化成字符串顯示在控制台中。當一個連接結束後,該對象就繼續進行監聽來自網絡中的連接請求。
代碼以及注釋如下:
namespace P2PTest
{
using System;
using System.Net.Sockets;
using System.Threading;
public class Listener
{
private Thread th;
private TcpListener tcpl;
public bool listenerRun = true;
//listenerRun為true,表示可以接受連接請求,false則為結束程序
public Listener()//構造函數
{
th = new Thread(new ThreadStart(Listen));//新建一個用於監聽的線程
th.Start();//打開新線程
}
public void Stop()
{
tcpl.Stop();
th.Abort();//終止線程
}
private void Listen()
{
try
{
tcpl = new TcpListener(5656);//在5656端口新建一個TcpListener對象
tcpl.Start();
Console.WriteLine("started listening..");
while(listenerRun)//開始監聽
{
Socket s = tcpl.AcceptSocket();
string remote = s.RemoteEndPoint.ToString();
Byte[] stream = new Byte[80];
int i=s.Receive(stream);//接受連接請求的字節流
string msg = "<" + remote + ">" + System.Text.Encoding.UTF8.GetString(stream);
Console.WriteLine(msg);//在控制台顯示字符串
}
}
catch(System.Security.SecurityException)
{
Console.WriteLine("firewall says no no to application - application cries..");
}
catch(Exception)
{
Console.WriteLine("stoped listening..");
}
}
}
}
對Listen()函數的補充說明:
這個函數是Listener類的核心部分。該函數首先被構造函數調用。只要布爾值listenerRun為true,我們就可以在端口5656創建並開始一個Tcp監聽對象TcpListener進行監聽網絡中的連接請求,而一旦listenerRun被置為false,則表示程序結束了。在循環體內部,我們先接受一個連接,用s.RemoteEndPoint獲得它的IP地址並獲得其字節流。根據獲得的字節流,我們用UTF8編碼將它轉化為字符串。最後,我們就在控制台中顯示獲得的字符串。
對於catch語句,第一個塊捕獲一個可能由防火牆引起的例外。因為對於防火牆而言,它可能認為這是一個特洛依木馬或是儒蟲病毒什麼的,所以就會拒絕通過。解決辦法就是重新配置防火牆。第二個塊用於捕獲一般的例外,比如當我們調用了stop()函數後,我們銷毀了TcpListener對象,那就自然不可能再進行監聽了。
2.Sender類:
Sender類就一個函數,所以是相當簡單的。
代碼以及注釋如下:
namespace P2PTest
{
using System;
using System.IO;
using System.Net.Sockets;
public class Sender
{
public void Send(string[] aInput)
{
string stream = "";
//獲得要發送的信息
for(int i=2; i<aInput.Length; i++)
{
stream += aInput[i] + " ";
}
try
{
TcpClient tcpc = new TcpClient(aInput[1], 5656);
//在5656端口新建一個TcpClient對象
NetworkStream tcpStream = tcpc.GetStream();
StreamWriter reqStreamW = new StreamWriter(tcpStream);
reqStreamW.Write(stream);
reqStreamW.Flush();//發送信息
tcpStream.Close();
tcpc.Close();
}
catch(Exception)
{
Console.WriteLine("connection refused by target computer");
}
}
}
}
對Send()函數的補充說明:
Send(string[] aInput)函數將一個數組作為參數。數組的第一個元素Send(aInput[0])必須包含"send"這個字,否則Sender對象不會被創建(更多內容在InputHandler類中);第二個元素包含了目標計算機的IP地址;剩下