//==============================================================
//  版权所有：深圳杰文科技
//  文件名：HandlerHost.cs
//  版本：V1.0  
//  创建者：Jay  ( QQ： 85363208 )
//  创建时间：2017-11-28 16:18
//  创建描述：
//  修改者：
//  修改时间：
//  修改说明： 
//==============================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Serialization.Formatters;
using Framework.DomainBase;
using Framework.HandlerBase;
using Infrastructure.Log;

namespace Framework.Server
{
	//[DebuggerStepThrough]
	public class HandlerHost
	{
		private const string ChannelName = "Jewen";

		public static void Init()
		{
			//初始化EF，加快数据库访问。
			Console.WriteLine("开始初始化数据库...");
			DbContextExt.InitDB<BaseDB>();
			Console.WriteLine("--- 数据库初始化完毕 ---");

			Console.WriteLine("初始化服务...");

			SystemLogger.IsDebug = true;

			ServiceContext.Test = ConfigurationManager.AppSettings["test"] == "true";
			ServiceContext.AuditedLog = ConfigurationManager.AppSettings["auditedlog"] == "true";

			//TrackingServices.RegisterTrackingHandler(new HandlerTracking()); //注册跟踪服务。

			Unregister(); //注销通道。

			Register(); //注册通道。

			RemotingConfiguration.CustomErrorsMode = CustomErrorsModes.Off; //弹出具体的错误信息给客户端。
			RemotingConfiguration.CustomErrorsEnabled(false);

			Console.WriteLine("开始注册服务...");

			RegisterHandler(typeof(LoginHandler).Assembly.GetTypes(), typeof(BaseHandler)); //注册Handler。

			Console.WriteLine("--- 服务注册完毕 ---");

			Console.WriteLine("\r\n--- 开始注册后台任务组件 ---");
			System.Timers.Timer backgroupTask = new System.Timers.Timer(1000 * 60); //每隔1分钟唤醒一次后台任务。
			backgroupTask.Elapsed += backgroupTask_Elapsed;
			backgroupTask.Start();
			Console.WriteLine("--- 后台任务组件注册完毕 ---");

			//DbInterception.Add(new EFIntercepter());
			//Console.WriteLine("--- SQL跟踪已开启 ---");
		}

		public static void RegisterHandler(IEnumerable<Type> types, Type baseHandlerType)
		{
			if (types == null || baseHandlerType == null)
			{
				return;
			}

			bool isSoap = ConfigurationManager.AppSettings["channel"] == "http";

			foreach (Type type in types) //动态注册所有Handler类。
			{
				if (baseHandlerType.IsAssignableFrom(type))
				{
					/*
					 * 服务器端激活分为Singleton和SingleCall两种模式。
					 * 
					 * Singleton模式：此为有状态模式。如果设置为Singleton激活方式，则Remoting将为所有客户端建立同一个对象实例，即所有客户端共享同一个服务对象实例。
					 * 当对象处于活动状态时，Singleton实例会队列处理所有后来的客户端访问请求，而不管它们是同一个客户端，还是其他客户端。
					 * Singleton实例将在方法调用中一直维持其状态。
					 * 举例来说，如果一个远程对象有一个累加方法（i=0；++i），被多个客户端（例如两个）调用。
					 * 如果设置为Singleton方式，则第一个客户获得值为1，第二个客户获得值为2，因为他们获得的对象实例是相同的。
					 * 这类似Asp.Net的状态管理，我们可以认为它是一种Application状态。
					 * 
					 * SingleCall模式：SingleCall是一种无状态模式。
					 * 一旦设置为SingleCall模式，则当客户端调用远程对象的方法时，Remoting会为每一个客户端建立一个远程对象实例，而对象实例的销毁则是由GC自动管理的。
					 * 同上一个例子而言，则访问远程对象的两个客户获得的都是1。
					 * 我们仍然可以借鉴Asp.Net的状态管理，认为它是一种Session状态。
					 * 
					 * 注意，虽然Singleton模式节省资源，但Singleton是串行执行的，当多个客户端的请求同时进行时，会产生阻塞。
					 */

					if (!isSoap)
					{
						//默认为SingleCall模式，不要随便修改，否则会影响DbHandler的自动事务提交。
						//RemotingConfiguration.RegisterWellKnownServiceType(type, type.Name, WellKnownObjectMode.Singleton); //单例队列模式（节省服务器资源）。
						RemotingConfiguration.RegisterWellKnownServiceType(type, type.Name, WellKnownObjectMode.SingleCall);  //并行模式（提高访问效率，但耗服务器资源）。
					}
					else
					{
						RemotingConfiguration.RegisterWellKnownServiceType(type, type.Name + ".soap", WellKnownObjectMode.SingleCall);
					}

					Console.WriteLine("\t" + type.Name);
				}
			}
		}

		private static bool _IsExecuting = false;

		static void backgroupTask_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
		{
			if (_IsExecuting)
			{
				return;
			}
			_IsExecuting = true;

			try
			{
				ServiceContext.CheckSession(); //定时检查Session是否超时。

				if (ServiceContext.TimedTask != null) //自定义后台任务。
				{
					ServiceContext.TimedTask();
				}
			}
			finally
			{
				_IsExecuting = false;
			}
		}

		//注册通道。
		private static void Register()
		{
			string ip = ConfigurationManager.AppSettings["ip"];
			int port = int.Parse(ConfigurationManager.AppSettings["port"]);
			Console.WriteLine("监听端口：{0}", port);

			CustomServerSinkProvider customeServiceProvider = new CustomServerSinkProvider(); //设置自定义的ChannelSink，统一管理数据库事务。
			BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
			serverProvider.TypeFilterLevel = TypeFilterLevel.Full; //指定服务器序列化所有的级别。
			serverProvider.Next = customeServiceProvider; //设置自定义的ChannelSink，统一管理数据库事务。

			CustomClientSinkProvider customClientSinkProvider = new CustomClientSinkProvider();
			BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
			clientProvider.Next = customClientSinkProvider;

			IDictionary props = new Hashtable();
			props["name"] = ChannelName;
			props["port"] = port;
			props["rejectRemoteRequests"] = false;
			props["useIpAddress"] = false;
			props["timeout"] = 1000 * 60 * 10; //默认超时时间为10分钟。

			IChannel channel = null;

			bool isSoap = ConfigurationManager.AppSettings["channel"] == "http";
			if (!isSoap)
			{
				//如果不设置IP，那么Remoting会自动监听所有网卡的所有地址，可以同时允许内网和外网连接。
				//如果绑定了指定的IP，那么只能连接绑定的IP。
				if (!string.IsNullOrEmpty(ip))
				{
					props["bindTo"] = ip;
				}

				//注意，TcpChannel只能在局域网内使用，不能穿透防火墙。
				//如果要穿透防火墙请使用SOAP协议（HttpChannel）。
				channel = new TcpChannel(props, clientProvider, serverProvider);
			}
			else
			{
				//SOAP可以集成到IIS，以达到自动启动。HttpChannel还可以穿透防火墙（需要做端口映射）。
				channel = new HttpChannel(props, clientProvider, serverProvider);
			}

			//后一个参数表示是否对调用者做安全检查。
			//注意此参数必须与客户端的ChannelServices.RegisterChannel匹配。
			//当为true时，除非Cleint与Server同在一个Windows域里，否则Client需要提供Server的一个Windows用户的账号和密码才能登录。
			ChannelServices.RegisterChannel(channel, false);
		}

		//注销Remoting通道。
		private static void Unregister()
		{
			bool isSoap = ConfigurationManager.AppSettings["channel"] == "http";

			IChannel[] channels = ChannelServices.RegisteredChannels; //获得当前已注册的通道；
			foreach (IChannel eachChannel in channels)
			{
				if (eachChannel.ChannelName == ChannelName) //关闭指定名为JieWen的通道；
				{
					if (!isSoap)
					{
						TcpChannel tcpChannel = (TcpChannel)eachChannel;
						tcpChannel.StopListening(null); //关闭监听；
					}
					else
					{
						HttpChannel httpChannel = (HttpChannel)eachChannel;
						httpChannel.StopListening(null); //关闭监听；
					}

					//注销通道；
					ChannelServices.UnregisterChannel(eachChannel);
				}
			}
		}

	}
}
