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

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);
            }
        }
    }


 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值