首先,打开VS.net,新建一个C#项目(注意:模板为Windows应用程序),不妨命名为"P2Pchat"。图示如下:
其次,参照ICQ、OICQ等聊天工具,我们可以将程序的主界面布置成和常见聊天工具的消息发送对话框类似的布局。不过由于程序仅一个主界面,所以还要添加一些诸如"开始监听"、"停止监听"等控制按钮。同时,程序是支持昵称显示的。于是,按照这个思想,我们可以开始布置程序的主界面了。首先,往主界面上拖放如下一些控件:两个Label控件、三个Button控件、三个TextBox控件、一个RichTextBox控件以及一个StatusBar控件。
各个控件的属性设置如下表所示:
Form1 Text属性 P2P聊天工具
Label1 Text属性 目标计算机地址:
TestSlign属性 MiddleCenter
Label2 Text属性 昵称:
TextAlign属性 MiddleCenter
Button1 Text属性 开始监听
FlatStyle属性 Flat
Button2 Text属性 停止监听
FlatStyle属性 Flat
Enabled属性 False
Anchor属性 Top, Right
Button3 Text属性 发送消息
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属性是设置窗体上控件布局用的,当窗体大小改变后各个控件如何在窗体上重新分布由该属性决定,读者可参考更详细的介绍文章来理解。
其他属性为默认即可,最终布置主窗体界面如下所示:
现在到了程序的主体部分,即代码部分。在给出代码之前,我想先向大家介绍一下实现的基本原理以及其中的一些逻辑关系。
首先,程序运行后,用户得先按下“开始监听”按钮,按钮相应事件后,程序就进入了监听状态,状态栏有相应的显示。这样,本机就相当于“服务器/客户机”模式中的服务器了,其他计算机可以连接到本机并向本机发送消息。其他计算机通过该程序连接到本机是通过IP地址来实现的,C#对网络编程有很好的支持,所以程序员的工作量是比较小的。如此,一台计算机可以向另一台发送消息了。然而,这是个P2P程序,所以只要另一台计算机的用户也按下"开始监听"按钮,那台计算机也成了这台计算机的服务器了。于是就实现了消息互发功能,然而真正的服务器是不存在的,每台计算机都是服务器,每台计算机同时也是客户机,这就体现了P2P技术的"非中心化"原则。
程序主要用到了一个Listen()函数和一个Send()函数。前者实现程序的监听功能,函数实现如下:
private void Listen(){ try
{
tcpl = new TcpListener(5656);
tcpl.Start();
statusBar1.Text = "正在监听...";
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 = "已停止监听!";
}
}
函数的主体是一个try-catch语句,try部分又是一个while循环,这表示只要用户不按"停止监听"按钮,程序就会一直处于监听状态。监听的端口是5656,这个端口是可以自己定义的,只要不跟常用的端口混淆就行了。一旦程序接收到远程计算机的一条消息,就将该消息添加到消息显示框中(消息显示框就是那个RichTextBox控件)。函数的catch部分是捕捉一些异常用的,如用户之间设置了防火墙,就不能彼此通讯了,或是对方已经停止监听了,那当然就不能向它发送消息了。另一个函数Send()是实现程序发送消息的功能的。函数实现如下:
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 ="目标计算机拒绝连接请求!";
}
}
该函数的主体部分也是一个try-catch语句,它先根据用户的输入,建立一个和远程计算机的连接,注意其端口也为5656,而且必须是5656,这是为了和接收方端口保持一致,这样对方才能收到这里发送的消息。接着,函数根据用户在消息输入框中的内容以及用户的昵称向远程计算机发送消息。这样,只要网络无故障、远程计算机已经处于监听状态,它就能接收到这里发送的消息了。当然,这里处于监听状态了,远程的计算机也可以自如地往这里发消息。函数的catch部分也是用于捕捉一些异常的。
同时还要注意的是,由于该程序用到了许多网络编程所需的对象以及输入输出对象,又运用了多线程编程机制,所以在程序的开始出得添加如下一些名字空间:
using System.IO;
using System.Net.Sockets;
using System.Threading;
最后,程序中各个控件的事件处理函数以及完整的代码请参看文后附带的源代码包。程序运行的图示如下:
现在一个很基本的P2P运用程序以及完成,通过它,我们可以利用P2P技术的基本特性实现点对点通信。通过这个程序,我相信大家对C#下的P2P编程应该有了大致的了解。对于这个程序,不足的一点是功能比较简单,只可以发送、接受信息,而且不能穿过防火墙进行通讯,读者可以试着开发出功能更强P2P应用程序。
总结
最后,笔者希望能通过此文唤起大家对P2P技术的兴趣。因为P2P身后所蕴藏着的无比的创造力使人们对未来互联网充满了美好的憧憬,现在世界范围的P2P应用热潮也是一浪高过一浪。在可以预见的未来,随着对P2P研究的进一步深入和关注P2P的群体逐渐增多,P2P必将进入一个飞速发展的新时期。然而国内的P2P起步得比较晚,所以更需要有大量的技术研究投入以及足够的重视以赢得更好的发展。在此,笔者希望国内的P2P能取得辉煌的发展。
转自:奕影残峰的Blog