优化ETL过滤模块
大石头 编写于 2017-08-29 17:11:46
X
using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Xml.Serialization;

namespace NewLife.Net
{
    /// <summary>协议类型</summary>
    public enum NetType : Byte
    {
        /// <summary>未知协议</summary>
        Unknown = 0,

        /// <summary>传输控制协议</summary>
        Tcp = 6,

        /// <summary>用户数据报协议</summary>
        Udp = 17,

        /// <summary>Http协议</summary>
        Http = 80,

        /// <summary>WebSocket协议</summary>
        WebSocket = 81
    }

    /// <summary>网络资源标识,指定协议、地址、端口、地址族(IPv4/IPv6)</summary>
    /// <remarks>
    /// 仅序列化<see cref="Type"/>和<see cref="EndPoint"/>,其它均是配角!
    /// 有可能<see cref="Host"/>代表主机域名,而<see cref="Address"/>指定主机IP地址。
    /// </remarks>
    public class NetUri
    {
        #region 属性
        private NetType _Type;
        /// <summary>协议类型</summary>
        public NetType Type { get { return _Type; } set { _Type = value; _Protocol = value.ToString(); } }

        [NonSerialized]
        private String _Protocol;
        /// <summary>协议</summary>
        [XmlIgnore]
        public String Protocol
        {
            get { return _Protocol; }
            set
            {
                _Protocol = value;
                if (String.IsNullOrEmpty(value))
                    _Type = NetType.Unknown;
                else
                {
                    try
                    {
                        if (value.EqualIgnoreCase("Http", "Https"))
                            _Type = NetType.Http;
                        else if (value.EqualIgnoreCase("ws", "wss"))
                            _Type = NetType.WebSocket;
                        else
                        {
                            _Type = (NetType)(Int32)Enum.Parse(typeof(ProtocolType), value, true);
                            // 规范化名字
                            _Protocol = _Type.ToString();
                        }
                    }
                    catch { _Type = NetType.Unknown; }
                }
            }
        }

        /// <summary>地址</summary>
        [XmlIgnore]
        public IPAddress Address { get { return EndPoint.Address; } set { EndPoint.Address = value; _Host = value + ""; } }

        private String _Host;
        /// <summary>主机</summary>
        public String Host { get { return _Host; } set { _Host = value; _EndPoint = null; /*只清空,避免这里耗时 try { EndPoint.Address = ParseAddress(value); } catch { }*/ } }

        private Int32 _Port;
        /// <summary>端口</summary>
        public Int32 Port { get { return _Port = EndPoint.Port; } set { _Port = EndPoint.Port = value; } }

        [NonSerialized]
        private IPEndPoint _EndPoint;
        /// <summary>终结点</summary>
        [XmlIgnore]
        public IPEndPoint EndPoint
        {
            get
            {
                // Host每次改变都会清空
                if (_EndPoint == null) _EndPoint = new IPEndPoint(ParseAddress(_Host) ?? IPAddress.Any, _Port);
                return _EndPoint;
            }
            set
            {
                // 考虑到序列化问题,Host可能是域名,而Address只是地址
                _EndPoint = value;
                if (value != null)
                {
                    _Host = _EndPoint.Address + "";
                    _Port = _EndPoint.Port;
                }
                else
                {
                    _Host = null;
                    _Port = 0;
                }
            }
        }
        #endregion

        #region 扩展属性
        /// <summary>是否Tcp协议</summary>
        [XmlIgnore]
        public Boolean IsTcp { get { return Type == NetType.Tcp; } }

        /// <summary>是否Udp协议</summary>
        [XmlIgnore]
        public Boolean IsUdp { get { return Type == NetType.Udp; } }
        #endregion

        #region 构造
        /// <summary>实例化</summary>
        public NetUri() { }

        /// <summary>实例化</summary>
        /// <param name="uri"></param>
        public NetUri(String uri) { Parse(uri); }

        /// <summary>实例化</summary>
        /// <param name="protocol"></param>
        /// <param name="endpoint"></param>
        public NetUri(NetType protocol, IPEndPoint endpoint)
        {
            Type = protocol;
            EndPoint = endpoint;
        }

        /// <summary>实例化</summary>
        /// <param name="protocol"></param>
        /// <param name="address"></param>
        /// <param name="port"></param>
        public NetUri(NetType protocol, IPAddress address, Int32 port)
        {
            Type = protocol;
            Address = address;
            Port = port;
        }

        /// <summary>实例化</summary>
        /// <param name="protocol"></param>
        /// <param name="host"></param>
        /// <param name="port"></param>
        public NetUri(NetType protocol, String host, Int32 port)
        {
            Type = protocol;
            Host = host;
            Port = port;
        }
        #endregion

        #region 方法
        static readonly String Sep = "://";

        /// <summary>分析</summary>
        /// <param name="uri"></param>
        public NetUri Parse(String uri)
        {
            if (uri.IsNullOrWhiteSpace()) return this;

            // 分析协议
            var p = uri.IndexOf(Sep);
            if (p >= 0)
            {
                Protocol = uri.Substring(0, p);
                uri = uri.Substring(p + Sep.Length);
            }

            // 特殊协议端口
            switch (Protocol.ToLower())
            {
                case "http":
                case "ws":
                    Port = 80;
                    break;
                case "https":
                case "wss":
                    Port = 443;
                    break;
            }

            // 这个可能是一个Uri,去掉尾部
            p = uri.IndexOf('/');
            if (p < 0) p = uri.IndexOf('\\');
            if (p < 0) p = uri.IndexOf('?');
            if (p >= 0) uri = uri.Substring(0, p);

            // 分析端口
            p = uri.LastIndexOf(":");
            if (p >= 0)
            {
                var pt = uri.Substring(p + 1);
                var port = 0;
                if (Int32.TryParse(pt, out port))
                {
                    Port = port;
                    uri = uri.Substring(0, p);
                }
            }

            Host = uri;

            return this;
        }
        #endregion

        #region 辅助
        /// <summary>分析地址</summary>
        /// <param name="hostname">主机地址</param>
        /// <returns></returns>
        public static IPAddress ParseAddress(String hostname)
        {
            if (String.IsNullOrEmpty(hostname)) return null;
            if (hostname == "*") return null;

            try
            {
                IPAddress addr = null;
                if (IPAddress.TryParse(hostname, out addr)) return addr;

                var hostAddresses = Dns.GetHostAddresses(hostname);
                if (hostAddresses == null || hostAddresses.Length < 1) return null;

                return hostAddresses.FirstOrDefault(d => d.AddressFamily == AddressFamily.InterNetwork || d.AddressFamily == AddressFamily.InterNetworkV6);
            }
            catch (SocketException ex)
            {
                throw new XException("解析主机" + hostname + "的地址失败!" + ex.Message, ex);
            }
        }

        /// <summary>已重载。</summary>
        /// <returns></returns>
        public override String ToString()
        {
            var p = Protocol;
            switch (Type)
            {
                case NetType.Unknown:
                    p = "";
                    break;
                case NetType.WebSocket:
                    p = Port == 443 ? "wss" : "ws";
                    break;
            }
            if (Port > 0)
                return String.Format("{0}://{1}:{2}", p, Host, Port);
            else
                return String.Format("{0}://{1}", p, Host);
        }
        #endregion

        #region 重载运算符
        /// <summary>重载类型转换,字符串直接转为NetUri对象</summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static implicit operator NetUri(String value)
        {
            return new NetUri(value);
        }
        #endregion
    }
}