C#实现P2P⽹络通讯程序源代码与演⽰
在⽹上看了很多程序(QQ、Azureus、Ants、PPStream)都实现了p2p,以前觉得技术很⾼深。通过这段时间的学习才发现,单纯的实现p2p在局域⽹通讯很容易,但是要实现外⽹穿透(NAT)感觉很困难。最近看了Azureus和emule源码,分别是JAVA和C++版,本⼈对这两门语⾔都不熟悉,看起来很吃⼒。最后只好根据VC++实现的P2PDemo程序进⾏了改版,根据设计思路⽤c#写了⼀个Demo出来。通过测试,多个客户端在局域⽹能脱离服务端实现端到端⼯作。外⽹的情况要通过路由器,看了Azureus要实现uPnp进⾏端⼝映射,在CodeProject上下载了⼀个uPnp源码看,测试结果没有启⽤uPnp路由器。结果现在郁闷了,不知道下⼀步怎么测试,是不是⽤upnp实现了端⼝⾃动映射成功就能实现象QQ那样通讯。
下⾯是程序说明:
1、公共类
公共类主要定义⼀些包结构
a、Packet.cs
Code
[Serializable()]
public abstract class Packet
{
/// <summary>
/// 命令类型
/// </summary>
/// <returns></returns>
public virtual int GetCommandType()
{
return -1;
}
/
// <summary>
/// ⽤户名
/// </summary>
public string UrName
{
get;
t;
}
public Packet()
{ }
public Packet(string urname)
{
this.UrName = urname;
}
}b、MassTextPacket.cs --分⽚传输类
Code
[Serializable()]
public class MassTextPacket:TextPacket
{
private int qID;
/// <summary>
/// 包序列
/
// </summary>
public int SeqID
{
get { return qID; }
t { qID = value; }
}
private int qCount;
/// <summary>
/// 包数量
/// </summary>
public int SeqCount
兴趣爱好{
get { return qCount; }
t { qCount = value; }
}
private int _CLSD;
public int CLSD
{
get { return _CLSD; }
t { _CLSD = value; }
}
}
2、客户端
a、消息传送时进⾏p2p通讯
Code
private bool SendMessageTo(string toUrName, Packet packet)
{
PeerEntity toUr = urList.Single(c => c.UrName == toUrName);
if (toUr == null)
{
return fal;
}
ReceivedACK = fal;
for (int i=0; i<MAXRETRY; i++)
{
// 如果对⽅P2P地址不为0,就试图以它为⽬的地址发送数据,
// 如果发送失败,则认为此P2P地址⽆效
if (toUr.P2PAddress != null && toUr.P2PAddress.Port != 0)
{
if (packet.GetType() == typeof(TextPacket))
{
TextPacket msgPacket = new TextPacket(toUrName, (packet as TextPacket).Message); byte[] buffer = UtilityHelper.Serialize(msgPacket);
if (buffer.Length > MAXBUFFERSIZE)
{
MassTextPacket mtp = new MassTextPacket();
mtp.SeqID = 0;
mtp.SeqCount = (int)System.Math.Ceiling(buffer.Length / (decimal)MAXBUFFERSIZE); mtp.CLSD = mtp.GetHashCode();
long pos = 0;
long count = buffer.Length < MAXBUFFERSIZE ? buffer.Length : MAXBUFFERSIZE;
while (pos < buffer.Length && pos > 0)
{
byte[] bytes = new byte[count]; ;
for (int k = 0; k < count; k++)
bytes[k] = buffer[pos + k];
//数据组包
mtp.SeqID = mtp.SeqID + 1;
mtp.Message = Convert.ToBa64String(bytes);
//发送数据
byte[] buf = UtilityHelper.Serialize(mtp);
client.Send(buf, buf.Length, toUr.P2PAddress);
Thread.Sleep(100);
}
}
el
client.Send(buffer, buffer.Length, toUr.P2PAddress);
}
el if (packet.GetType() == typeof(FileStreamPacket))
{
FileStreamPacket fsp = packet as FileStreamPacket;
System.IO.FileStream fs = new System.IO.FileStream(fsp.FileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, FileShare.Read);
handle1.Ret();
fsp.SeqID = 0;
fsp.SeqCount = (int)System.Math.Ceiling(fs.Length / (decimal)MAXBUFFERSIZE);
fsp.CLSD = fsp.GetHashCode();
long pos = 0;
long count = fs.Length < MAXBUFFERSIZE ? fs.Length : MAXBUFFERSIZE;
while (pos < fs.Length && count > 0)
{
byte[] buffer = new byte[count];
fs.Seek(pos, SeekOrigin.Begin);
fs.Read(buffer, 0, (int)count);
pos += count;
count = pos + MAXBUFFERSIZE < fs.Length ? MAXBUFFERSIZE : fs.Length - pos;
//数据组包
fsp.SeqID = fsp.SeqID + 1;
fsp.Message = Convert.ToBa64String(buffer);
//发送数据
byte[] buf = UtilityHelper.Serialize(fsp);
手机本机ip地址
client.Send(buf, buf.Length, toUr.P2PAddress);
Thread.Sleep(300);
}
handle1.Set();
}
// 等待接收线程将标记修改
for (int j = 0; j < 10; j++)
{
if (this.ReceivedACK)
{
this.ReceivedACK = fal;
return true;
}
el
{
Thread.Sleep(300);
}
}
}
// 构建P2P打洞封包
// 然后通过服务器转发,请求对⽅向⾃⼰打洞
P2PConnectionPacket transMsg = new P2PConnectionPacket(UrName, toUrName);
byte[] msgBuffer = UtilityHelper.Serialize(transMsg);
client.Send(msgBuffer, msgBuffer.Length, hostPoint);
// 等待对⽅的P2PCONNECTACK消息
for(int j = 0; j < 10; ++j)
{
toUr = urList.Single(c => c.UrName == toUrName);
if ( toUr.P2PAddress != null && toUr.P2PAddress.Port != 0)
break;
Thread.Sleep(300);
}
}
return fal;
}
b、消息接受线程
Code
/// <summary>
/// 接受线程处理
/// </summary>
怒放之青春再见private void RecvThreadProc()
{
byte[] buffer;
while (true)
{
高中毕业生登记表
buffer = client.Receive(ref remotePoint);
Packet packet = UtilityHelper.Derialize(buffer) as Packet;
Type msgType = packet.GetType();
if (msgType == typeof(UrListAckPacket))
{
// 转换消息
UrListAckPacket ursMsg = (UrListAckPacket)packet;
// 更新⽤户列表
urList.Clear();
foreach (PeerEntity ur in ursMsg.Urs)
{
urList.Add(ur);
}
bUrListComplete = true;
}
el if (msgType == typeof(UrLoginAckPacket))
{
ProcUrLogAckMsg(packet);
}
el if (msgType == typeof(TextPacket))
{
/
/ 转换消息
TextPacket txtPacket = (TextPacket)packet;
printf("Receive a message: {0}", txtPacket.Message);
// 发送应答消息
P2PAckPacket ackMsg = new P2PAckPacket();
buffer = UtilityHelper.Serialize(ackMsg);
client.Send(buffer, buffer.Length, remotePoint);
}
el if (msgType == typeof(MassTextPacket))
{
lock (this)
{
MassTextPacket fPacket = (MassTextPacket)packet;
if (packets.ContainsKey(fPacket.CLSD))
packets[fPacket.CLSD].Add(fPacket);
el
packets.Add(fPacket.CLSD, new List<MassTextPacket>() { fPacket });
printf("PacketID:{0}--SeqNo:{1}--progress:{2}%", fPacket.CLSD, fPacket.SeqID, (int)
(System.Math.Round(packets[fPacket.CLSD].Count / (decimal)(fPacket as MassTextPacket).SeqCount, 2) * 100)); //组包
if ((fPacket as MassTextPacket).SeqCount == packets[fPacket.CLSD].Count)
{王者荣耀海报
怎么看吉他谱List<MassTextPacket> temp = packets[fPacket.CLSD].OrderBy(c => c.SeqID).ToList();
List<byte> values = new List<byte>();
foreach (MassTextPacket mt in temp)
{
byte[] buf = Convert.FromBa64String(mt.Message);
values.AddRange(buf);
}
MassTextPacket value = UtilityHelper.Derialize(values.ToArray()) as MassTextPacket;
printf("Receive a message: {0}", value.Message);
// 发送应答消息
P2PAckPacket ackMsg = new P2PAckPacket();
buffer = UtilityHelper.Serialize(ackMsg);
client.Send(buffer, buffer.Length, remotePoint);
}
}
}
el if (msgType == typeof(FileStreamPacket))
{
lock (this)
{
FileStreamPacket fPacket = (FileStreamPacket)packet;
if (packets.ContainsKey(fPacket.CLSD))
packets[fPacket.CLSD].Add(fPacket);
el抛撒
packets.Add(fPacket.CLSD, new List<MassTextPacket>() { fPacket });
printf("PacketID:{0}--SeqNo:{1}--progress:{2}%", fPacket.CLSD, fPacket.SeqID, (int)
(System.Math.Round(packets[fPacket.CLSD].Count / (decimal)(fPacket as FileStreamPacket).SeqCount, 2) * 100));
//组包
if ((fPacket as FileStreamPacket).SeqCount == packets[fPacket.CLSD].Count)
{
List<MassTextPacket> temp = packets[fPacket.CLSD].OrderBy(c => c.SeqID).ToList();
System.IO.FileStream fs = new System.IO.FileStream((fPacket as FileStreamPacket).FileName + ".tmp", System.IO.FileMode.Create, System.IO.FileAccess.ReadWrite);
foreach (FileStreamPacket mt in temp)
{
服装三要素byte[] buf = Convert.FromBa64String(mt.Message);
fs.Write(buf, 0, buf.Length);
}
fs.Flush();
fs.Clo();
printf("Receive a file: {0}", (fPacket as FileStreamPacket).FileName);
//清除数据包
packets[fPacket.CLSD].Clear();
// 发送应答消息
P2PAckPacket ackMsg = new P2PAckPacket();
buffer = UtilityHelper.Serialize(ackMsg);
client.Send(buffer, buffer.Length, remotePoint);
}
}
}
el if (msgType == typeof(P2PAckPacket))
{
this.ReceivedACK = true;