/// <summary>绑定</summary> /// <param name="request"></param> /// <param name="session"></param> /// <returns></returns> protected void OnBind(StunMessage request, ISocketSession session) { var rs = new StunMessage { Type = StunMessageType.BindingResponse, TransactionID = request.TransactionID.ReadBytes(), MappedAddress = session.Remote.EndPoint }; //rs.SourceAddress = session.GetRelativeEndPoint(remote.Address); if (Public != null) { if (session.Local.IsTcp) { rs.SourceAddress = Public[session.Port + 100000]; } else { rs.SourceAddress = Public[session.Port]; } } // 找另一个 ISocketSession session2 = null; var anotherPort = 0; for (var i = 0; i < Servers.Count; i++) { var server = Servers[i]; if (server.Local.Type == session.Local.Type && server.Local.Port != session.Local.Port) { anotherPort = server.Port; if (server.Local.IsTcp) { break; } else { session2 = (server as UdpServer).CreateSession(session.Remote.EndPoint); if (session2 != null) { break; } } } } //rs.ChangedAddress = Partner ?? session2.GetRelativeEndPoint(remote.Address); if (Public != null) { if (session.Local.IsTcp) { rs.ChangedAddress = Partner ?? Public[anotherPort + 100000]; } else { rs.ChangedAddress = Partner ?? Public[anotherPort]; } } var name = Name; if (name == GetType().Name) { name = GetType().FullName; } rs.ServerName = String.Format("{0} v{1}", name, AssemblyX.Create(Assembly.GetExecutingAssembly()).CompileVersion); // 换成另一个 if (request.ChangePort) { session = session2; } session.Send(rs.ToArray()); }
/// <summary>查询</summary> /// <param name="request"></param> /// <param name="remoteEndPoint"></param> /// <returns></returns> public StunMessage Query(StunMessage request, IPEndPoint remoteEndPoint) { EnsureSocket(); return(Query(Socket, request, remoteEndPoint)); }
/// <summary>接收到数据时</summary> /// <param name="session"></param> /// <param name="pk"></param> protected override void OnReceive(INetSession session, Packet pk) { if (pk.Total > 0) { var remote = session.Remote; //if (remote == null && session != null) remote = session.RemoteEndPoint; var request = StunMessage.Read(pk.GetStream()); WriteLog("{0} {1} {2}{3}", request.Type, remote, request.ChangeIP ? " ChangeIP" : "", request.ChangePort ? " ChangePort" : ""); // 如果是兄弟服务器发过来的,修正响应地址 switch (request.Type) { case StunMessageType.BindingRequest: //case StunMessageType.BindingResponse: request.Type = StunMessageType.BindingRequest; if (request.ResponseAddress != null) { remote.EndPoint = request.ResponseAddress; } break; case StunMessageType.SharedSecretRequest: //case StunMessageType.SharedSecretResponse: request.Type = StunMessageType.SharedSecretRequest; if (request.ResponseAddress != null) { remote.EndPoint = request.ResponseAddress; } break; default: break; } // 是否需要发给伙伴 if (request.ChangeIP) { //if (Partner != null && !Partner.Equals(session.Host.LocalEndPoint.GetRelativeEndPoint(Partner.Address))) //{ // // 发给伙伴 // request.ChangeIP = false; // // 记住对方的地址 // request.ResponseAddress = remote.EndPoint; // //session.Send(request.GetStream(), Partner); // var us = session.Host as UdpServer; // if (us != null) // { // //us.CreateSession(Partner).Send(request.GetStream()); // us.Send(request.GetStream(), Partner); // } // return; //} // 如果没有伙伴地址,采用不同端口代替 request.ChangePort = true; } // 开始分流处理 switch (request.Type) { case StunMessageType.BindingRequest: //case StunMessageType.BindingResponse: OnBind(request, session.Session); break; case StunMessageType.SharedSecretRequest: break; default: break; } } }
///// <summary>在指定服务器上执行查询</summary> ///// <param name="host"></param> ///// <param name="port"></param> ///// <returns></returns> //public StunResult QueryWithServer(String host, Int32 port = 3478) //{ // try // { // return QueryWithServer(NetHelper.ParseAddress(host), port); // } // catch { return null; } //} /// <summary>在指定服务器上执行查询</summary> /// <param name="address"></param> /// <param name="port"></param> /// <returns></returns> public StunResult QueryWithServer(IPAddress address, Int32 port) { EnsureSocket(); var client = Socket as ISocketClient; var remote = new IPEndPoint(address, port); // Test I // 测试网络是否畅通 var msg = new StunMessage(); msg.Type = StunMessageType.BindingRequest; var rs = Query(client, msg, remote); // UDP blocked. if (rs == null) { return(new StunResult(StunNetType.Blocked, null)); } WriteLog("服务器:{0}", rs.ServerName); WriteLog("映射地址:{0}", rs.MappedAddress); WriteLog("源地址:{0}", rs.SourceAddress); WriteLog("新地址:{0}", rs.ChangedAddress); var remote2 = rs.ChangedAddress; // Test II // 要求改变IP和端口 msg.ChangeIP = true; msg.ChangePort = true; msg.ResetTransactionID(); // 如果本地地址就是映射地址,表示没有NAT。这里的本地地址应该有问题,永远都会是0.0.0.0 //if (client.LocalEndPoint.Equals(test1response.MappedAddress)) var pub = rs.MappedAddress; if (pub != null && client.Local.Port == pub.Port && pub.Address.IsLocal()) { // 要求STUN服务器从另一个地址和端口向当前映射端口发送消息。如果收到,表明是完全开放网络;如果没收到,可能是防火墙阻止了。 rs = Query(client, msg, remote); // Open Internet. if (rs != null) { return(new StunResult(StunNetType.OpenInternet, pub)); } // Symmetric UDP firewall. return(new StunResult(StunNetType.SymmetricUdpFirewall, pub)); } else { rs = Query(client, msg, remote); if (rs != null && pub == null) { pub = rs.MappedAddress; } // Full cone NAT. if (rs != null) { return(new StunResult(StunNetType.FullCone, pub)); } // Test II msg.ChangeIP = false; msg.ChangePort = false; msg.ResetTransactionID(); // 如果是Tcp,这里需要准备第二个重用的Socket if (client.Local.IsTcp) { EnsureSocket2(); client = Socket2 as ISocketClient; } rs = Query(client, msg, remote2); // 如果第二服务器没响应,重试 if (rs == null) { rs = Query(client, msg, remote2); } if (rs != null && pub == null) { pub = rs.MappedAddress; } if (rs == null) { return(new StunResult(StunNetType.Blocked, pub)); } // 两次映射地址不一样,对称网络 if (!rs.MappedAddress.Equals(pub)) { return(new StunResult(StunNetType.Symmetric, pub)); } // Test III msg.ChangeIP = false; msg.ChangePort = true; msg.ResetTransactionID(); rs = Query(client, msg, remote2); if (rs != null && pub == null) { pub = rs.MappedAddress; } // 受限 if (rs != null) { return(new StunResult(StunNetType.AddressRestrictedCone, pub)); } // 端口受限 return(new StunResult(StunNetType.PortRestrictedCone, pub)); } }