用Visual C#实现P2P应用程序

一.前言
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 {  

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地址;剩下的就是要发送的内容信息了。
在try块中,咱们根据远程计算机的IP地址在端口5656(要确保端口号统一)建立了一个TcpClient对象。而后,咱们创建一个NetworkStream和一个StremWriter对象来发送咱们的信息。在catch块中,咱们用它来捕获通常的例外,好比远程计算机拒绝链接请求、网络不通什么的。
3.InputHandler类:
InputHandler类主要用来控制用户输入。
代码以及注释以下:
namespace P2PTest  

{  

  using System;  

  public class InputHandler  

 {  

public bool appRun = true; //当appRun为false时,程序结束  

public InputHandler()  

{  

 Console.WriteLine( "type help for a list of commands.");  

 Input();  

}  

private static Listener li; //一个静态的Listener对象  

private string inparam;  

private string[] aInput; //数组aInput用于接受用户输入的信息  

public void Input()  

{  

  while(appRun)  

 {  

inparam = Console.ReadLine();  

aInput = inparam.Split(' ');  

//将inparam分割的目的是为了得到字符串中的第一个字,从而执行如下不一样的命令  

switch(aInput[0])  

{  

  case "send": //若是是"send",则新建一个Sender对象并发送信息  

Sender se = new Sender();  

se.Send(aInput);  

break;  

  case "start": //若是是"start",则新的开始监听  

try  

{  

 li.listenerRun = false;  

 li.Stop();  

}  

catch(NullReferenceException)  

{  

;  

}  

finally  

{  

 li = new Listener();  

}  

break;  

  case "stop": //若是是"stop",则中止监听  

try  

{  

 li.listenerRun = false;  

 li.Stop();  

}  

catch(NullReferenceException)  

{  

 ;  

}  

break;  

  case "exit": //退出程序  

try  

{  

 li.listenerRun = false;  

 li.Stop();  

}  

catch(NullReferenceException)  

{  

 ;  

}  

finally  

{  

 appRun = false;  

}  

break;  

  case "help": //显示帮助信息  

 Console.WriteLine( "Commands:");  

 Console.WriteLine( "start: starts the listener");  

 Console.WriteLine( "stop: stops the listener if started");  

 Console.WriteLine( "send: send sends a message");  

 Console.WriteLine( "exit: exits the application");  

 Console.WriteLine( "help: you already know");  

  break;  

  default:  

Console.WriteLine( "Invalid command");  

break;  

}  

 }  

}  

 }  

}  
对InputHandler类的补充说明:
该类中有一个静态的Listener对象li,一旦计算机运行此程序并执行"start"操做,该计算机就能够成为网络中的服务器来监听其余计算机的链接请求。而该类的核心部分是一个switch case语句系列,经过不一样的操做,咱们可使计算机扮演不一样的角色:"send"操做代表该计算机相对目的计算机而言成了客户端;而"start"操做就将计算机自身置为服务器端,这正体现了P2P的既是服务器端又是客户端的"非中心化"的原则;同时程序也提供了一些其余的辅助操做。
4.Initialize类:
Initialize类进行程序的初始化工做,它新建了一个InputHandler对象,只要该对象的布尔值appRun为true,就一直运行之,直到该值为false,程序退出。
代码以及注释以下:
namespace P2PTest  

{  

  using System;  

  public class Init  

 {  

public static void Main()  

{  

 InputHandler ih = new InputHandler(); //新建一个InputHandler对象  

  while(ih.appRun); //直到ih.appRun为false,程序退出  

Console.WriteLine( "exiting..");  

}  

 }  

}
到此为止,四个类已经介绍完毕,我想你们也早已等不及了吧,下面就简单给你们介绍一下具体实现程序的方法。
三.实现方法
首先,打开Visual Studio.Net,新建一个名为P2Ptest的控制台应用程序的Visual C#项目,图示以下:
图1
其次,将以上四个类分别保存为四个文件:listener.cs,sender.cs,inputHandler.cs,initialize.cs。而后将这四个文件添加到当前的工程中,同时把原有的主文件删除便可(由于在initialize.cs中已经有主函数了)。
最后,按Ctrl+F5便可执行程序了。
为了进行测试,咱们须要打开两个P2Ptest程序,一个做为服务器端,另外一个做为客户端。服务器端的图示以下(此时已经开始监听了):
图2
客户端的图示以下(输入命令行:send 10.85.7.79 Hello,I'm Pitt.Can you hear me?):
图3
再看服务器端的状况,图示以下:
相关文章
相关标签/搜索