c# P2P穿透 已知NAT类型,NAT类型可根据Stun协议获取
2012-07-26 11:17
375 查看
NatType.cs
IUdpPauch,cs
PunchType.cs
PunchPacket.cs
PunchPacketParse.cs
UdpPunch.cs
UdpPunchI.cs
UdpPunchII.cs
UdpPunchIII.cs
PaunchAgent.cs
public enum NatType { /// <summary> /// 收到则表网络为开放网络 Opened 主机拥有公网IP且没有防火墙 /// </summary> OpenInternet = 0, /// <summary> /// 主机前有Nat设备,且Nat规则如下: /// 从主机UDP端口A发出的数据都会对应到Nat设备出口IP的端口B,并且从任意外部地址发送到该Nat设备端口B的数据都会转到 /// </summary> FullCone = 1, /// <summary> /// 主机前有Nat设备,且Nat规则如下: /// 从主机UDP端口A发出的数据都会对应到Nat设备出口IP的端口B,但只有从之前该主机发出包的目的IP发出到该Nat设备UDP端口B的包 /// 才会被转发到主机端口A /// </summary> RestrictedCone = 2, /// <summary> /// 主机前有Nat设备,且Nat规则如下 /// 从主机UDP端口A发出的数据都会对应到Nat设备出口IP的端口B, /// 但只有从之前该主机发出包的目的IP/Port发出到该Nat设备UDP端口B的包才会被转发到主机端口A /// </summary> PortRestrictedCone = 3, /// <summary> /// 收不到回复则表示网络为Symmetric UDP Firewall /// 本机出口没有Nat设备,但是有防火墙,且防火墙规则如下: /// 从主机UDP端口A发出的数据保持源地址 /// 但只有从该端口的目的地址对应的IP/Port发出的包才能通过防火墙 /// </summary> SymmetricUdpFirewall = 4, /// <summary> /// 主机前有Nat设备,且Nat规则如下: /// 即使数据包都从主机UDP端口A发出,但只要目的地址不一致,Nat就会为之分配不同的端口B /// </summary> Symmetric = 5, /// <summary> /// 如果收不到数据,则为Blocked类型,认为被防火墙阻断UDP /// </summary> UdpBlocked = 6, }
IUdpPauch,cs
/// <summary> /// UDP打洞接口 /// </summary> public interface IUdpPauch { /// <summary> /// 开始打洞 /// </summary> /// <returns>开始打洞</returns> bool DoPunch(); /// <summary> /// 打洞成功后的远程地址 /// </summary> IPEndPoint PunchRemote { get; } }
PunchType.cs
public enum PunchType : byte { HandShake, SencondHandShake, FinalHandShake, AckHandShake, }
PunchPacket.cs
public sealed class PunchPacket { public string Key { get; set; } public PunchType PunchType { get; set; } public IPEndPoint Remote { get; set; } public byte[] GetBuffer() { return PunchPacketParse.GetBuffer(this); } public static PunchPacket Parse(byte[] buffer, int offset, int len) { return PunchPacketParse.Parse(buffer, offset, len); } }
PunchPacketParse.cs
public static class PunchPacketParse { public static byte[] GetBuffer(PunchPacket pkt) { int bufferLength = 1;//类型长度1 byte[] buffer = null; byte[] keyBuffer = Encoding.Default.GetBytes(pkt.Key); bufferLength = bufferLength + 2 + keyBuffer.Length;//keyBuffer长度2+keyBuffer if (pkt.Remote != null) { bufferLength += 6; } buffer = new byte[bufferLength]; buffer[0] = (byte)pkt.PunchType; buffer[1] = (byte)((0xFF00 & keyBuffer.Length) >> 8); buffer[2] = (byte)(0xFF & keyBuffer.Length); Array.Copy(keyBuffer, 0, buffer, 3, keyBuffer.Length); if (pkt.Remote != null) { Array.Copy(pkt.Remote.Address.GetAddressBytes(), 0, buffer, keyBuffer.Length + 3, 4); buffer[keyBuffer.Length + 3 + 4] = (byte)((0xFF00 & pkt.Remote.Port) >> 8); buffer[keyBuffer.Length + 3 + 5] = (byte)(0xFF & pkt.Remote.Port); } return buffer; } public static PunchPacket Parse(byte[] buffer, int offset, int len) { PunchType punchType = (PunchType)buffer[offset]; int keyLen = (buffer[offset + 1] << 8) | buffer[offset + 2]; if (len == keyLen + 3) { PunchPacket pkt = new PunchPacket(); pkt.PunchType = punchType; pkt.Key = Encoding.Default.GetString(buffer, offset + 3, keyLen); return pkt; } else if (len == keyLen + 3 + 6) { byte[] addressByte = new byte[4]; Array.Copy(buffer, offset + keyLen + 3, addressByte, 0, 4); IPAddress address = new IPAddress(addressByte); int port = (buffer[keyLen + 3 + 4] << 8) | buffer[keyLen + 3 + 5]; if (port > 0) { PunchPacket pkt = new PunchPacket(); pkt.PunchType = punchType; pkt.Key = Encoding.Default.GetString(buffer, offset + 3, keyLen); pkt.Remote = new IPEndPoint(address, port); return pkt; } } return null; } }
UdpPunch.cs
/// <summary> /// 打洞抽象类 /// </summary> public abstract class UdpPunch : IUdpPauch { private static readonly TracingImpl Tracing = TracingManager.GetTracing(typeof(UdpPunch)); private Socket Sock { get; set; } public IPEndPoint Remote { get; protected set; } public IPEndPoint PunchRemote { get; protected set; } protected ManualResetEvent PunchHandle { get; private set; } protected string Key { get; private set; } protected UdpPunch(Socket sock, IPEndPoint remote, string key) { this.Sock = sock; this.Remote = remote; this.Key = key; this.PunchHandle = new ManualResetEvent(false); } protected virtual PunchPacket GetPuchPacket() { PunchPacket pkt = new PunchPacket(); pkt.Key = this.Key; switch (this.Step) { case 0: pkt.PunchType = PunchType.HandShake; break; case 1: pkt.PunchType = PunchType.SencondHandShake; break; case 2: pkt.PunchType = PunchType.FinalHandShake; break; } pkt.Remote = this.PunchRemote; return pkt; } protected volatile int Step = 0; protected virtual void SendPauchPack(PunchPacket pkt, IPAddress address, int port) { this.SendPauchPack(pkt, new IPEndPoint(address, port)); } protected virtual void SendPauchPack(PunchPacket pkt, EndPoint remote) { byte[] buffer = pkt.GetBuffer(); this.Sock.SendTo(buffer, SocketFlags.None, remote); } public abstract bool DoPunch(); protected void StartRevice() { BackgroundThreadHelper.RunThread(this.ReviceThread); } private void ReviceThread() { byte[] buffer = new byte[1024 * 10]; int reviceTimeout = this.Sock.ReceiveTimeout; int sendTimeout = this.Sock.SendTimeout; this.Sock.ReceiveTimeout = 3; this.Sock.SendTimeout = 3; DateTime startTime = DateTime.Now; while (DateTime.Now.Subtract(startTime).TotalSeconds < 11 && !this.PunchHandle.WaitOne(0)) { try { EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); int len = this.Sock.ReceiveFrom(buffer, ref remoteEndPoint); PunchPacket pkt = PunchPacket.Parse(buffer, 0, len); if (pkt != null && this.Key.Equals(pkt.Key, StringComparison.OrdinalIgnoreCase)) { if (pkt.PunchType == PunchType.HandShake)///接收到握手包 { if (this.PunchRemote == null) { this.PunchRemote = remoteEndPoint as IPEndPoint; this.Step = 1; } PunchPacket sencondPkt = new PunchPacket(); sencondPkt.Key = this.Key; sencondPkt.PunchType = PunchType.SencondHandShake; sencondPkt.Remote = this.PunchRemote; this.SendPauchPack(sencondPkt, this.PunchRemote);//发送首次确认包 } else if (pkt.PunchType == PunchType.SencondHandShake) { if (this.PunchRemote == null) { this.PunchRemote = remoteEndPoint as IPEndPoint; this.Step = 2; } PunchPacket finalPkt = new PunchPacket(); finalPkt.Key = this.Key; finalPkt.PunchType = PunchType.FinalHandShake; finalPkt.Remote = this.PunchRemote; this.SendPauchPack(finalPkt, this.PunchRemote);//发送二次确认包 } else if (pkt.PunchType == PunchType.FinalHandShake) { PunchPacket ackPkt = new PunchPacket(); ackPkt.Key = this.Key; ackPkt.PunchType = PunchType.AckHandShake; ackPkt.Remote = this.PunchRemote; this.SendPauchPack(ackPkt, this.PunchRemote); Thread.Sleep(5); this.SendPauchPack(ackPkt, this.PunchRemote); Thread.Sleep(5); this.SendPauchPack(ackPkt, this.PunchRemote); this.PunchHandle.Set();//更改本地打洞状态 Tracing.Info("Paunch Successed.Remote:udp://{0}:{1}", this.PunchRemote.Address, this.PunchRemote.Port); } else if (pkt.PunchType == PunchType.AckHandShake) { Tracing.Info("Paunch Successed.Remote:udp://{0}:{1}", this.PunchRemote.Address, this.PunchRemote.Port); this.PunchHandle.Set(); } } else if (pkt == null && this.PunchRemote != null && this.PunchRemote.Equals(remoteEndPoint) && this.Step == 2) { //如果已经接收过打洞包,且收到了对方的打洞确认包,则认为打洞已经成功 this.PunchHandle.Set(); } } catch (ObjectDisposedException) { return; } catch (Exception) { } } this.Sock.ReceiveTimeout = reviceTimeout; this.Sock.SendTimeout = sendTimeout; }
UdpPunchI.cs
/// <summary> /// 该打洞类适用于开放性网络和本地为FullCone型NAT对方不是FullCone、OpenInternet类型 /// </summary> /// <remarks>该打动类不主动进行发送包,等待对方发送包进行被动打洞</remarks> public sealed class UdpPunchI : UdpPunch { public UdpPunchI(Socket sock, IPEndPoint remote, string key) : base(sock, remote, key) { } public override bool DoPunch() { this.StartRevice(); return this.PunchHandle.WaitOne(10000); } }
UdpPunchII.cs
/// <summary> /// 该打洞方法定期向对方端口发送包 /// </summary> public sealed class UdpPunchII : UdpPunch { public UdpPunchII(Socket sock, IPEndPoint remote, string key) : base(sock, remote, key) { } public override bool DoPunch() { this.StartRevice(); bool bRet = false; int nTimeoutCount = 0; while (true) { if (nTimeoutCount > 100) break; this.SendPauchPack(this.GetPuchPacket(), this.Remote.Address, this.Remote.Port); if (!this.PunchHandle.WaitOne(100)) { nTimeoutCount++; } else { bRet = true; break; } } return bRet; } }
UdpPunchIII.cs
/// <summary> /// 该打洞方法定期向远程的一组地址范围进行发包测试,适用于对方网络为对称型 /// </summary> public sealed class UdpPunchIII : UdpPunch { public UdpPunchIII(Socket sock, IPEndPoint remote, string key) : base(sock, remote, key) { } public override bool DoPunch() { this.StartRevice(); bool bRet = false; int nTimeoutCount = 0; while (true) { if (nTimeoutCount > 100) break; for (int i = 0; i < 5; i++) { this.SendPauchPack(this.GetPuchPacket(), this.Remote.Address, this.Remote.Port + i); this.SendPauchPack(this.GetPuchPacket(), this.Remote.Address, this.Remote.Port - i); this.SendPauchPack(this.GetPuchPacket(), this.Remote.Address, this.Remote.Port + i); this.SendPauchPack(this.GetPuchPacket(), this.Remote.Address, this.Remote.Port - i); } if (!this.PunchHandle.WaitOne(100)) { nTimeoutCount++; } else { bRet = true; break; } } return bRet; } }
PaunchAgent.cs
public static class PaunchAgent { private static readonly TracingImpl Tracing = TracingManager.GetTracing(typeof(PaunchAgent)); public static UdpPunch CreatePauch(Socket sock, IPEndPoint remote, string key, NatType localNatType, NatType remoteNatType) { if (localNatType == NatType.UdpBlocked || remoteNatType == NatType.UdpBlocked) { return null; } else if ((localNatType == NatType.OpenInternet || localNatType == NatType.FullCone) && (remoteNatType != NatType.OpenInternet && remoteNatType != NatType.FullCone)) { return new UdpPunchI(sock, remote, key); } else if (remoteNatType == NatType.OpenInternet || remoteNatType == NatType.FullCone || localNatType == NatType.RestrictedCone) { return new UdpPunchII(sock, remote, key); } else if (remoteNatType == NatType.Symmetric) { return new UdpPunchIII(sock, remote, key); } else if (remoteNatType == NatType.SymmetricUdpFirewall) { return new UdpPunchII(sock, remote, key); } else { return new UdpPunchII(sock, remote, key); } } }
相关文章推荐
- 网络穿透 与 NAT类型 及 STUN TURN 协议
- 网络穿透、NAT类型 、STUN TURN 协议等资料参考
- P2P穿透&四种NAT类型
- 网络协议之p2p---一个简单的nat 穿透demo
- C# 中反射获取某类的子类和根据类型名动态创建对象(转载)
- C# 中反射获取某类的子类和根据类型名动态创建对象(转载)
- P2P之UDP穿透NAT的原理与C#实现
- P2P之UDP穿透NAT的原理与C#实现
- STUN协议,探测NAT类型
- P2P之UDP穿透NAT的原理与实现-C#实现
- P2P之UDP穿透NAT的原理与C#实现
- 使用STUN协议了解所在网络的NAT类型(附Python实现)
- C#实现P2P之UDP穿透NAT及其原理讲解11
- P2P之UDP穿透NAT的原理与C#实现
- P2P之UDP穿透NAT的原理与实现-C#实现
- C#实现P2P之UDP穿透NAT及其原理讲解
- STUN协议,探测NAT类型(实现过程一)
- STUN协议,探测NAT类型(个人理解)
- C#实现P2P之UDP穿透NAT原理讲解