目录
1:P2P设计模式简介
2:P2P设计模式实例
1:P2P设计模式简介
P2P技术属于覆盖层网络(Overlay Network)的范畴,是相对于客户机/服务器(C/S)模式来说的一种网络信息交换方式。在C/S模式中,数据的分发采用专门的服务器,多个客户端都从此服务器获取数据。
优点是:数据的一致性容易控制,系统也容易管理。
缺点是:因为服务器的个数只有一个(即便有多个也非常有限),系统容易出现单一失效点;单一服务器面对众多的客户端,由于CPU能力、内存大小、网络带宽的限制,可同时服务的客户端非常有限,可扩展性差。
P2P技术正是为了解决这些问题而提出来的一种对等网络结构。在P2P网络中,每个节点既可以从其他节点得到服务,也可以向其他节点提供服务。这样,庞大的终端资源被利用起来,一举解决了C/S模式中的两个弊端。
P2P应用软件主要包括文件分发软件、语音服务软件、流媒体软件。目前P2P应用种类多、形式多样,没有统一的网络协议标准,其体系结构和组织形式也在不断发展。
2:P2P设计模式实例
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading;
namespace P2PTest
{
public partial class frmP2P : Form
{
private Thread myThread;
private TcpListener tcpListener;
private IPAddress myIPAddress;
private int myPort;
private System.Diagnostics.Stopwatch secondWatch;
public frmP2P()
{
InitializeComponent();
secondWatch = new System.Diagnostics.Stopwatch();
ColumnHeader ipColumn = new ColumnHeader();
ipColumn.Text = "IP地址";
ipColumn.Width = 140;
ColumnHeader portColumn = new ColumnHeader();
portColumn.Text = "端口号";
ColumnHeader onlineColumn = new ColumnHeader();
onlineColumn.Text = "是否在线";
onlineColumn.Width = 70;
lvMyFriend.View = View.Details;
lvMyFriend.Columns.AddRange(new ColumnHeader[] { ipColumn,portColumn,onlineColumn});
}
private void frmP2P_Load(object sender, EventArgs e)
{
//启动秒表;
secondWatch.Start();
tmSecond.Enabled = true;
btnStartTimer.Enabled = true;
btnStopTimer.Enabled = false;
//使用代理指定在线程上执行的方法
ThreadStart myThreadStartDelegate = new ThreadStart(Listening);
//创建一个用于监听的线程对象,通过代理执行线程中的方法
myThread = new Thread(myThreadStartDelegate);
//启动线程
myThread.Start();
}
//该方法是通过代理调用执行
private void Listening()
{
Socket socket = null;
//获取本机第一个可用的IP地址
myIPAddress = (IPAddress)Dns.GetHostAddresses(Dns.GetHostName()).GetValue(0);
Random r = new Random();
while(true)
{
try
{
//随机产生一个0~65535之间的端口号
myPort = r.Next(65535);
//创建TcpListner对象,在本机的IP和port端口监听连接到该IP和端口的请求
tcpListener = new TcpListener(myIPAddress, myPort);
//开始监听,如果不出现异常,则说明IP地址和端口号可用
tcpListener.Start();
//显示IP地址和端口
ShowLocalIpAndPort();
ShowMyMessage(string.Format("尝试端口{0}监听成功",myPort));
ShowMyMessage(string.Format("<message>[{0}]{1:h点m分s秒}开始在{2}端口监听与本机的连接",myIPAddress,DateTime.Now,myPort));
break;
}
catch
{
//继续while循环,以便随机找下一个可用端口号,同时显示失败信息
ShowMyMessage(string.Format("尝试端口{0}监听失败",myPort));
}
}
while(true)
{
try{
//使用阻塞方式接收客户端连接,根据连接信息创建TcpClient对象
//注意:AccpetSocet接收到新的连接请求才会继续执行其后的语句
socket = tcpListener.AcceptSocket();
//如果再往下执行,说明已经根据客户端连接请求创建了套接字
//使用创建的套接字接收客户端发送的信息
NetworkStream stream = new NetworkStream(socket);
StreamReader sr = new StreamReader(stream);
string receiveMessage = sr.ReadLine();
int i1 = receiveMessage.IndexOf(",");
int i2 = receiveMessage.IndexOf(",",i1+1);
int i3 = receiveMessage.IndexOf(",",i2+1);
string ipString = receiveMessage.Substring(0,i1);
string portString = receiveMessage.Substring(i1+1,i2-i1-1);
string messageTypeString = receiveMessage.Substring(i2+1,i3-i2-1);
string messageString = receiveMessage.Substring(i3+1);
ShowMyMessage(ipString,portString,messageTypeString,messageString);
}
catch{
//通过停止TcpListener使AcceptSocket方法出现异常
//在异常处理中关闭套接字并终止线程
if (socket != null)
{
if (socket.Connected)
{
socket.Shutdown(SocketShutdown.Receive);
}
socket.Close();
}
myThread.Abort();
}
}
}
delegate void ShowMessageDelegate(string str);
private void ShowMyMessage(string str)
{
//比较调用的线程和创建的线程是否为同一个线程
//如果不是,结果为ture
if (this.lbMessage.InvokeRequired == true)
{
//如果结果为true,则自动通过代理执行eles中语句的功能
//这里只需要传入参数str即可
//但是执行的功能会始终与else中的指定的功能相同,区别仅是通过代理完成
ShowMessageDelegate messageDelegate = new ShowMessageDelegate(ShowMyMessage);
this.Invoke(messageDelegate,new object[]{str});
}
else{
//在这里指定如果是同一个线程需要完成什么功能
//如果是不同的线程,系统会自动通过代理实现这里指定的功能
lbMessage.Items.Add(str);
}
}
delegate void ShowMessageDelegate2(string ipString, string portString, string messageTypeString, string messageString);
private void ShowMyMessage(string ipString, string portString, string messageTypeString, string messageString)
{
if (this.lbMessage.InvokeRequired == true)
{
ShowMessageDelegate2 messageDelegate = new ShowMessageDelegate2(ShowMyMessage);
this.Invoke(messageDelegate,new object[]{ipString,portString ,messageTypeString,messageString});
}
else{
//messageType的含义为
//connect前缀:第1次连接
//check前缀:检查接收者是否在线
//message前缀:聊天信息
int myfriendIndex = CheckMyFriend(ipString,portString);
switch(messageTypeString)
{
case "connect":
if (myfriendIndex == -1)
{
ListViewItem myFriendItem= new ListViewItem(new string[]{ipString,portString,"是"});
lvMyFriend.Items.Add(myFriendItem);
}
break;
case "check":
if (myfriendIndex == -1)
{
ListViewItem myFriendItem = new ListViewItem(new string[]{ipString,portString,"是"});
lvMyFriend.Items.Add(myFriendItem);
}
//不显要显示
break;
case "message":
lbMessage.Items.Add(string.Format("[{0}:{1}说:{2}",ipString,portString,messageString));
break;
default :
lbMessage.Items.Add(string.Format("什么意思呀:“{0}”",messageString));
break;
}
}
}
delegate void ShowIpAndPortDelegate();
private void ShowLocalIpAndPort()
{
if (this.lbMessage.InvokeRequired)
{
ShowIpAndPortDelegate messageDelegate =
new ShowIpAndPortDelegate(ShowLocalIpAndPort);
this.Invoke(messageDelegate);
}
else
{
txtLocalIp.Text = myIPAddress.ToString();
txtLocalPort.Text = myPort.ToString();
}
}
private int CheckMyFriend(string remoteIpString, string remotePortString)
{
//在lvMyFriend中检查指定的ip+port是否存在
ListViewItem item = null;
bool bExixts = false;
for (int i = 0; i < lvMyFriend.Items.Count; i++)
{
item = lvMyFriend.Items[i];
if (item.Text == remoteIpString)
{
for (int j = 0; j < item.SubItems.Count; j++)
{
if (item.SubItems[j].Text == remotePortString)
{
bExixts = true;
break;
}
}
if (bExixts) break;
}
}
if (!bExixts)
{
return -1;
}
else
{
return item.Index;
}
}
private void SendMessage(string remoteIpString, string remotePortString, string strType, string str)
{
IPAddress remoteIP = IPAddress.Parse(remoteIpString);
int remotePort = int.Parse(remotePortString);
TcpClient tcpclient = null;
NetworkStream networkstream = null;
try
{
tcpclient = new TcpClient(remoteIpString, remotePort);
//得到一个用于发送和接收数据的网络流
networkstream = tcpclient.GetStream();
//使用默认编码和缓冲区大小初始化StreamWrite对象
StreamWriter streamWriter = new StreamWriter(networkstream);
//使用回车换行为每次发送的分隔符
streamWriter.WriteLine(string.Format("{0},{1},{2},{3}", myIPAddress, myPort, strType, str));
//将缓冲区的内容全部发送出去
streamWriter.Flush();
if (strType != "check")
{
lbMessage.Items.Add(string.Format("向{0}:{1}]发送成功,信息:{2}",remoteIpString,remotePortString,str));
}
}
catch (Exception err)
{
int i = CheckMyFriend(remoteIpString,remotePortString);
if (i != -1)
{
lvMyFriend.Items[i].SubItems[2].Text = "否";
}
if (strType != "check")
{
lbMessage.Items.Add(string.Format("向{0}:{1}]发送信息失败,原因:{2}",
remoteIpString,remotePortString,err.Message));
}
}
finally
{
if (networkstream != null)
{
networkstream.Close();
}
if (tcpclient != null)
{
tcpclient.Close();
}
}
}
private void btnAddFriend_Click(object sender, EventArgs e)
{
IPAddress myFriendIpAddress;
if (IPAddress.TryParse(txtFriendIp.Text, out myFriendIpAddress) == false)
{
MessageBox.Show("IP地址不正确!");
return;
}
int myFriendPort;
if (int.TryParse(txtFriendPort.Text, out myFriendPort) == false)
{
MessageBox.Show("端口号格式不正确!");
return;
}
else
{
if (myFriendPort < 1000 || myFriendPort > 65535)
{
MessageBox.Show("端口号范围不正确!必须在1000~65535之间");
return;
}
}
int i = CheckMyFriend(txtFriendIp.Text, txtLocalPort.Text);
if (i != -1)
{
MessageBox.Show("该好友已经在列表中");
}
else
{
ListViewItem friendItem = new ListViewItem(
new string[] { txtFriendIp.Text, txtFriendPort.Text, "是" });
lvMyFriend.Items.Add(friendItem);
//向对方发送连接信息
SendMessage(txtFriendIp.Text, txtFriendPort.Text, "connect", "哈哈,我上线了");
}
}
private void btnSendMessage_Click(object sender, EventArgs e)
{
if (lvMyFriend.SelectedItems.Count > 0)
{
//可以群发
for (int i = 0; i < lvMyFriend.SelectedItems.Count; i++)
{
if (lvMyFriend.SelectedItems[i].SubItems[2].Text == "是")
{
string remoteIpString = lvMyFriend.SelectedItems[i].SubItems[0].Text;
string remotePortString = lvMyFriend.SelectedItems[i].SubItems[1].Text;
SendMessage(remoteIpString, remotePortString, "message", txtSendMessage.Text);
}
}
}
else
{
MessageBox.Show("请先选择发送的好友", "提示");
}
}
private void btnStartTimer_Click(object sender, EventArgs e)
{
secondWatch.Reset();
int i;
if (int.TryParse(txtTimeInterval.Text, out i) == true)
{
if (i < 0)
{
MessageBox.Show("刷新间隔必须是正整数", "范围不正确");
}
else
{
tmSecond.Enabled = true;
secondWatch.Start();
btnStartTimer.Enabled = false;
btnStopTimer.Enabled = true;
}
}
else
{
MessageBox.Show("刷新间隔必须是正整数", "范围不正确");
}
}
private void btnStopTimer_Click(object sender, EventArgs e)
{
tmSecond.Enabled = false;
secondWatch.Stop();
txtTimeStatus.Text = "已经停止定时刷新";
btnStartTimer.Enabled = true;
btnStopTimer.Enabled = false;
}
private void tmSecond_Tick(object sender, EventArgs e)
{
if (secondWatch.ElapsedMilliseconds / 1000 == int.Parse(txtTimeInterval.Text))
{
txtTimeStatus.Text = "刷新";
tmSecond.Stop();
secondWatch.Reset();
for (int i = 0; i < lvMyFriend.Items.Count; i++)
{
string remoteIpString = lvMyFriend.Items[i].SubItems[0].Text;
string remotePortString = lvMyFriend.Items[i].SubItems[1].Text;
SendMessage(remoteIpString, remotePortString, "check", "看看你还在线没?");
}
tmSecond.Start();
secondWatch.Start();
}
else
{
txtTimeStatus.Text = string.Format("{0}", secondWatch.ElapsedMilliseconds / 1000);
}
}
private void frmP2P_FormClosing(object sender, FormClosingEventArgs e)
{
tcpListener.Stop();
}
}
}
实现效果如下