[fix]Config创建默认配置文件的开关Runtime.CreateConfigOnMissing,仅需对自动创建生效,而不应该阻止用户主动Save
智能大石头 编写于 2024-08-09 00:30:41 石头 提交于 2024-08-10 14:22:24
X
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using NewLife.Net.Sockets;

namespace NewLife.Net.P2P
{
    /// <summary>打洞服务器</summary>
    /// <remarks>
    /// Tcp打洞流程(A想连接B):
    /// 1,客户端A通过路由器NAT-A连接打洞服务器S
    /// 2,A向S发送标识,异步等待响应
    /// 3,S记录A的标识和会话<see cref="ISocketClient"/>
    /// 3,客户端B,从业务通道拿到标识
    /// 4,B通过路由器NAT-B连接打洞服务器S,异步等待响应
    /// 5,B向S发送标识
    /// 6,S找到匹配标识,同时向AB会话响应对方的外网地址,会话结束
    /// 7,AB收到响应,B先连接A,A暂停一会后连接B
    /// </remarks>
    public class HoleServer : NetServer
    {
        #region 属性
        private Dictionary<String, INetSession> _Clients;
        /// <summary>客户端集合</summary>
        public IDictionary<String, INetSession> Clients { get { return _Clients ?? (_Clients = new Dictionary<String, INetSession>(StringComparer.OrdinalIgnoreCase)); } }
        #endregion

        #region 方法
        /// <summary>收到数据时</summary>
        /// <param name="session"></param>
        /// <param name="stream"></param>
        protected override void OnReceive(ISocketSession session, Stream stream)
        {
            var remoteEP = session.Remote.EndPoint;

            var str = stream.ToStr();
            WriteLog("");
            WriteLog(remoteEP + "=>" + session.Local.EndPoint + " " + str);

            var ss = str.Split(":");
            ss[0] = ss[0].ToLower();
            if (ss[0] == "reg")
            {
                var name = ss[1];
                INetSession ns = null;
                if (!Clients.TryGetValue(name, out ns))
                {
                    // 集合里面没有,认为是发起邀请方,做好记录
                    ns = new NetSession();
                    //ns.Server = sender as ISocketServer;
                    ns.Session = session;
                    //ns.ClientEndPoint = remoteEP;
                    Clients[name] = ns;
                    session.OnDisposed += (s, e2) => ns.Dispose();
                    ns.OnDisposed += (s, e2) => Clients.Remove(name);

                    //session.Send("注册成功!你的公网地址是:" + client, null, client);
                    session.Send("注册成功!你的公网地址是:" + remoteEP, null);

                    WriteLog("邀请已建立:{0}", name);
                }
                else
                {
                    // 如果连续注册两次可能会有问题,这里要处理一下
                    //if ("" + ns.ClientEndPoint == "" + remoteEP)
                    //    session.Send("Has Register!", null);
                    //else
                    {
                        // 到这里,应该是被邀请方到来,同时响应双方
                        session.Send(remoteEP.ToString(), null);

                        // 同时还要通知对方
                        ns.Session.Send(remoteEP.ToString(), null);

                        WriteLog("邀请已接受:{0} {1} {2}", name, remoteEP, remoteEP);

                        Clients.Remove(name);
                        Thread.Sleep(1000);
                        //session.Disconnect();
                        //if (ns.Session != null) ns.Session.Disconnect();
                        session.Dispose();
                        ns.Session.Dispose();
                    }
                }
            }
            else if (ss[0] == "invite")
            {
                INetSession ns = null;
                if (Clients.TryGetValue(ss[1], out ns))
                {
                    session.Send("invite:" + remoteEP, null);

                    // 同时还要通知对方
                    ns.Session.Send("invite:" + remoteEP.ToString(), null);
                }
                else
                    session.Send("Not Found!", null);
            }
            else
            {
                if (!str.Contains("P2P")) session.Send("无法处理的信息:" + str, null);
            }
        }
        #endregion
    }
}