您的位置:首页 > 编程语言 > C#

c# P2P穿透 已知NAT类型,NAT类型可根据Stun协议获取

2012-07-26 11:17 375 查看
NatType.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);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: