一言不合,直接上代码,有需要的小伙伴可以加作者QQ号:541655940;QQ群:700600021
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using P2P.Main.Enums;
using P2P.Main.Model;
using P2P.Main.Session;
using P2P.Network.ExtensionMethods;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace P2P.Main.Handler
{
/// <summary>
/// 客户端NAT检测Handler
/// </summary>
public class NatCheckHandler
{
private ApiProtocol apiProtocol;
public NatCheckHandler(ApiProtocol apiProtocol)
{
this.apiProtocol = apiProtocol;
}
#region NAT类型检测步骤
/// <summary>
/// 第一步:检测客户端NAT类型是否是对称NAT
/// 若为对称NAT,则检测结束
/// 若不是对称NAT,则继续检测是否是全锥型NAT
/// </summary>
public void Step1()
{
string AccountId = (string)apiProtocol.Params["AccountId"];
ActiveNode activeNode = ActiveNodeSessionManager.Instance.Find(AccountId);
if (activeNode == null)
{
return;
}
string localIP = (string)apiProtocol.Params["LocalIP"];
int localPort = (int)apiProtocol.Params["LocalPort"];
if (!activeNode.IP.Equals(localIP) || activeNode.UdpPublicPort != localPort)
{
ActiveNodeSessionManager.Instance.Flush(AccountId, NatType.SYMMETRIC);
//Nat类型检测结束
//当前Nat类型为对称型Nat
JObject data = new JObject();
data.Add("Name", "NatCheck");
data.Add("Method", "NatCheckFinished");
data.Add("Version", "1.0");
JObject param = new JObject();
param.Add("NatType", (int)NatType.SYMMETRIC);
data.Add("Params", param);
string ack = JsonConvert.SerializeObject(R<JObject>.Ok(data));
apiProtocol.Socket.SendTo(ack,activeNode.IP, activeNode.UdpPublicPort);
}
else
{
//继续检测
//判断Nat类型是否为全锥形Nat
JObject data = new JObject();
data.Add("Name", "NatCheck");
data.Add("Method", "Step1");
data.Add("Version", "1.0");
JObject param = new JObject();
param.Add("NatType", (int)NatType.UNKNOW);
data.Add("Params", param);
string ack = JsonConvert.SerializeObject(R<JObject>.Ok(data));
Task.Run(() =>
{
bool exit = false;
while (!exit)
{
try
{
//启用服务端第二个IP和随机端口
//发送数据包给客户端公网IP和监听的端口
//若,客户端收到数据,则客户端为全锥形Nat
Random r = new Random();
int randomPort = r.Next(6000, 6500);
string localIP = SharedModel.Instance.Hosts.UDPMainServerSecondInternalIP;
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
sock.Bind(new IPEndPoint(IPAddress.Parse(localIP), randomPort));
sock.SendTo(ack, activeNode.IP, activeNode.UdpPublicPort);
sock.Close();
exit = true;
}
catch{ }
}
});
JObject deepData = (JObject)data.DeepClone();
deepData["Method"] = "Step2";
apiProtocol.Socket.SendTo(
JsonConvert.SerializeObject(R<JObject>.Ok(deepData)), activeNode.IP, activeNode.UdpPublicPort);
}
}
/// <summary>
/// 第二步:检测完成更新NAT类型为全锥型NAT
/// </summary>
public void Step2()
{
string AccountId = (string)apiProtocol.Params["AccountId"];
ActiveNode activeNode = ActiveNodeSessionManager.Instance.Find(AccountId);
if (activeNode == null)
{
return;
}
//检测结束
//刷新Nat为全锥形Nat
//检测结束
ActiveNodeSessionManager.Instance.Flush(AccountId, NatType.FULLCONE);
JObject data = new JObject();
data.Add("Name", "NatCheck");
data.Add("Method", "NatCheckFinished");
data.Add("Version", "1.0");
JObject param = new JObject();
param.Add("NatType", (int)NatType.FULLCONE);
data.Add("Params", param);
string ack = JsonConvert.SerializeObject(R<JObject>.Ok(data));
apiProtocol.Socket.SendTo(ack, apiProtocol.RemoteEndPoint);
}
/// <summary>
/// 第三步:检测NAT类型是否是IP受限锥型Nat
/// </summary>
public void Step3()
{
string AccountId = (string)apiProtocol.Params["AccountId"];
ActiveNode activeNode = ActiveNodeSessionManager.Instance.Find(AccountId);
if (activeNode == null)
{
return;
}
JObject data = new JObject();
data.Add("Name", "NatCheck");
data.Add("Method", "Step3");
data.Add("Version", "1.0");
JObject param = new JObject();
param.Add("NatType", (int)NatType.UNKNOW);
data.Add("Params", param);
string ack = JsonConvert.SerializeObject(R<JObject>.Ok(data));
Task.Run(() =>
{
bool exit = false;
while (!exit)
{
try
{
//启用服务端第一个已经与客户端通讯过的IP和随机端口
//发送数据包给客户端公网IP和监听的端口
//若,客户端收到数据,则客户端为IP受限锥型Nat
Random r = new Random();
int randomPort = r.Next(6000, 6500);
string localIP = SharedModel.Instance.Hosts.UDPMainServerFirstInternalIP;
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
sock.Bind(new IPEndPoint(IPAddress.Parse(localIP), randomPort));
sock.SendTo(ack, activeNode.IP, activeNode.UdpPublicPort);
sock.Close();
exit = true;
}
catch { }
}
});
JObject deepClone = (JObject)data.DeepClone();
deepClone["Method"] = "Step4";
apiProtocol.Socket.SendTo(JsonConvert.SerializeObject(R<JObject>.Ok(deepClone)), apiProtocol.RemoteEndPoint);
}
/// <summary>
/// 第四步:更新NAT类型为IP受限锥型NAT
/// </summary>
public void Step4()
{
string AccountId = (string)apiProtocol.Params["AccountId"];
ActiveNode activeNode = ActiveNodeSessionManager.Instance.Find(AccountId);
if (activeNode == null)
{
return;
}
//检测结束
//刷新Nat为IP受限锥形Nat
//检测结束
ActiveNodeSessionManager.Instance.Flush(AccountId, NatType.RESTRICTEDCONE);
JObject data = new JObject();
data.Add("Name", "NatCheck");
data.Add("Method", "NatCheckFinished");
data.Add("Version", "1.0");
JObject param = new JObject();
param.Add("NatType", (int)NatType.RESTRICTEDCONE);
data.Add("Params", param);
string ack = JsonConvert.SerializeObject(R<JObject>.Ok(data));
apiProtocol.Socket.SendTo(ack, apiProtocol.RemoteEndPoint);
}
/// <summary>
/// 第五步:检测NAT类型是否是端口受限锥型NAT
/// </summary>
public void Step5()
{
string AccountId = (string)apiProtocol.Params["AccountId"];
ActiveNode activeNode = ActiveNodeSessionManager.Instance.Find(AccountId);
if (activeNode == null)
{
return;
}
JObject data = new JObject();
data.Add("Name", "NatCheck");
data.Add("Method", "Step5");
data.Add("Version", "1.0");
JObject param = new JObject();
param.Add("NatType", (int)NatType.UNKNOW);
data.Add("Params", param);
string ack = JsonConvert.SerializeObject(R<JObject>.Ok(data));
Task.Run(() =>
{
bool exit = false;
while (!exit)
{
try
{
//启用服务端第二个IP和第二个端口给客户端发送数据包
//若,客户端收到了数据包,则Nat类型为端口受限锥形Nat
string secondIP = SharedModel.Instance.Hosts.UDPMainServerSecondInternalIP;
int secondPort = SharedModel.Instance.Hosts.UDPMainServerSecondInternalPort.Value;
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
sock.Bind(new IPEndPoint(IPAddress.Parse(secondIP), secondPort));
sock.SendTo(ack, activeNode.IP, activeNode.UdpPublicPort);
sock.Close();
exit = true;
}
catch { }
}
});
JObject deepClone = (JObject)data.DeepClone();
deepClone["Method"] = "Step6";
apiProtocol.Socket.SendTo(JsonConvert.SerializeObject(R<JObject>.Ok(deepClone)), apiProtocol.RemoteEndPoint);
}
/// <summary>
/// 第六步:更新NAT类型为端口受限锥型NAT
/// </summary>
public void Step6()
{
string AccountId = (string)apiProtocol.Params["AccountId"];
ActiveNode activeNode = ActiveNodeSessionManager.Instance.Find(AccountId);
if (activeNode == null)
{
return;
}
//检测结束
//刷新Nat为端口受限锥形Nat
//检测结束
ActiveNodeSessionManager.Instance.Flush(AccountId, NatType.PORTRESTRICTEDCONE);
JObject data = new JObject();
data.Add("Name", "NatCheck");
data.Add("Method", "NatCheckFinished");
data.Add("Version", "1.0");
JObject param = new JObject();
param.Add("NatType", (int)NatType.PORTRESTRICTEDCONE);
data.Add("Params", param);
string ack = JsonConvert.SerializeObject(R<JObject>.Ok(data));
apiProtocol.Socket.SendTo(ack, apiProtocol.RemoteEndPoint);
}
/// <summary>
/// 第七步:检测结束,NAT类型未知
/// </summary>
public void Step7()
{
string AccountId = (string)apiProtocol.Params["AccountId"];
ActiveNode activeNode = ActiveNodeSessionManager.Instance.Find(AccountId);
if (activeNode == null)
{
return;
}
//检测结束
//刷新Nat为未知Nat类型
//检测结束
ActiveNodeSessionManager.Instance.Flush(AccountId, NatType.UNKNOW);
JObject data = new JObject();
data.Add("Name", "NatCheck");
data.Add("Method", "NatCheckFinished");
data.Add("Version", "1.0");
JObject param = new JObject();
param.Add("NatType", (int)NatType.UNKNOW);
data.Add("Params", param);
string ack = JsonConvert.SerializeObject(R<JObject>.Ok(data));
apiProtocol.Socket.SendTo(ack, apiProtocol.RemoteEndPoint);
}
#endregion
}
}