v9.8.2018.0605   由DataReader直接映射实体列表,以支持netstandard的MySql和SQLite,且提升性能
大石头 编写于 2018-06-05 00:45:23
X
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace NewLife.Net.Sockets
{
    /// <summary>Socket客户端</summary>
    /// <remarks>
    /// 处理的过程中,即使使用异步,也允许事件订阅者阻塞下一次接收的开始<see cref="ReceiveAsync"/>,
    /// 因为事件订阅者可能需要处理完手头的数据才开始下一次接收。
    /// </remarks>
    public abstract class SocketClient : SocketBase, ISocketClient
    {
        #region 属性
        /// <summary>基础Socket对象</summary>
        public Socket Client { get { if (Socket == null) EnsureCreate(); return Socket; } set { Socket = value; } }
        #endregion

        #region 远程
        private NetUri _RemoteUri;
        /// <summary>远程地址</summary>
        public NetUri RemoteUri
        {
            get
            {
                if (_RemoteUri != null)
                {
                    _RemoteUri.ProtocolType = ProtocolType;
                    return _RemoteUri;
                }

                var uri = new NetUri();
                uri.ProtocolType = ProtocolType;
                var socket = Socket;
                try
                {
                    if (socket != null && socket.Connected) uri.EndPoint = socket.RemoteEndPoint as IPEndPoint;
                }
                catch (ObjectDisposedException) { }

                return _RemoteUri = uri;
            }
        }

        /// <summary>远程终结点</summary>
        public IPEndPoint RemoteEndPoint { get { return RemoteUri.EndPoint; } }
        #endregion

        #region 构造
        /// <summary>实例化</summary>
        public SocketClient()
        {
            // 客户端可能需要阻塞,默认打开延迟
            //NoDelay = false;
        }
        #endregion

        #region 连接
        /// <summary>建立与远程主机的连接</summary>
        /// <param name="hostname"></param>
        /// <param name="port"></param>
        /// <returns>返回自身,用于链式写法</returns>
        public virtual ISocketClient Connect(String hostname, Int32 port) { return Connect(NetHelper.ParseAddress(hostname), port); }

        /// <summary>建立与远程主机的连接</summary>
        /// <param name="address"></param>
        /// <param name="port"></param>
        /// <returns>返回自身,用于链式写法</returns>
        public virtual ISocketClient Connect(IPAddress address, Int32 port) { return Connect(new IPEndPoint(address, port)); }

        /// <summary>建立与远程主机的连接</summary>
        /// <param name="remoteEP">表示远程设备。</param>
        /// <returns>返回自身,用于链式写法</returns>
        public ISocketClient Connect(EndPoint remoteEP)
        {
            AddressFamily = remoteEP.AddressFamily;
            if (!Client.IsBound) Bind();

            Client.Connect(remoteEP);

            // 引发基类重设个地址参数
            Socket = Client;

            return this;
        }
        #endregion

        #region 异步开始
        /// <summary>开始异步接收数据</summary>
        public abstract void ReceiveAsync();
        #endregion

        #region 事件
        /// <summary>数据到达,在事件处理代码中,事件参数不得另作他用,套接字事件池将会将其回收。</summary>
        public event EventHandler<NetEventArgs> Received;

        /// <summary>接收到数据时</summary>
        /// <remarks>
        /// 网络事件参数使用原则:
        /// 1,得到者负责回收(通过方法参数得到)
        /// 2,正常执行时自己负责回收,异常时顶级负责回收
        /// 3,把回收责任交给别的方法
        /// </remarks>
        /// <param name="e"></param>
        protected virtual void OnReceive(NetEventArgs e)
        {
            // Socket错误由各个处理器来处理
            if (e.SocketError != SocketError.Success)
            {
                OnError(e, null);
                return;
            }

            // 没有接收事件时,马上开始处理重建委托
            if (Received == null)
            {
                ReceiveAsync();
                return;
            }

            Process(e, ReceiveAsync, ProcessReceive);
        }

        /// <summary>处理接收到的数据</summary>
        /// <param name="e"></param>
        internal protected virtual void ProcessReceive(NetEventArgs e)
        {
            // 统计接收数
            IncCounter();

            CheckBufferSize(e);
            if (Received != null) Received(this, e);
        }

        /// <summary>已重载。</summary>
        /// <param name="e"></param>
        protected override void OnComplete(NetEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                case SocketAsyncOperation.ReceiveFrom:
                case SocketAsyncOperation.ReceiveMessageFrom:
                    OnReceive(e);
                    return;
                default:
                    break;
            }

            base.OnComplete(e);
        }
        #endregion

        #region 发送
        ///// <summary>发送数据流</summary>
        ///// <param name="stream"></param>
        ///// <param name="remoteEP"></param>
        ///// <returns></returns>
        //public virtual Int64 Send(Stream stream, EndPoint remoteEP = null)
        //{
        //    Int64 total = 0;

        //    var size = stream.CanSeek ? stream.Length - stream.Position : BufferSize;
        //    Byte[] buffer = new Byte[size];
        //    while (true)
        //    {
        //        Int32 n = stream.Read(buffer, 0, buffer.Length);
        //        if (n <= 0) break;

        //        Send(buffer, 0, n, remoteEP);
        //        total += n;

        //        if (n < buffer.Length) break;
        //    }
        //    return total;
        //}

        ///// <summary>发送数据</summary>
        ///// <param name="buffer">缓冲区</param>
        ///// <param name="offset">位移</param>
        ///// <param name="size">写入字节数</param>
        ///// <param name="remoteEP">远程终结点</param>
        //public virtual void Send(Byte[] buffer, Int32 offset = 0, Int32 size = 0, EndPoint remoteEP = null)
        //{
        //    if (!Client.IsBound) Bind();

        //    if (size <= 0) size = buffer.Length - offset;
        //    if (remoteEP != null && Socket == null) AddressFamily = remoteEP.AddressFamily;
        //    if (remoteEP != null && ProtocolType == ProtocolType.Udp)
        //        Client.SendTo(buffer, offset, size, SocketFlags.None, remoteEP);
        //    else
        //        Client.Send(buffer, offset, size, SocketFlags.None);
        //}

        ///// <summary>发送字符串</summary>
        ///// <param name="msg"></param>
        ///// <param name="encoding"></param>
        ///// <param name="remoteEP">远程终结点</param>
        //public void Send(String msg, Encoding encoding = null, EndPoint remoteEP = null)
        //{
        //    if (String.IsNullOrEmpty(msg)) return;

        //    if (encoding == null) encoding = Encoding.UTF8;
        //    Send(encoding.GetBytes(msg), 0, 0, remoteEP);
        //}
        #endregion

        #region 接收
        /// <summary>接收数据</summary>
        /// <returns></returns>
        public Byte[] Receive()
        {
            var buffer = new Byte[BufferSize];
            if (!Client.IsBound) Bind();

            var size = Client.Receive(buffer);
            if (size <= 0) return null;

            return buffer.ReadBytes(0, size);
        }

        ///// <summary>接收字符串</summary>
        ///// <param name="encoding"></param>
        ///// <returns></returns>
        //public String ReceiveString(Encoding encoding = null)
        //{
        //    var buffer = Receive();
        //    if (buffer == null || buffer.Length < 1) return null;

        //    if (encoding == null) encoding = Encoding.UTF8;
        //    return encoding.GetString(buffer);
        //}
        #endregion

        #region 创建会话
        /// <summary>为指定地址创建会话。对于无连接Socket,必须指定远程地址;对于有连接Socket,指定的远程地址将不起任何作用</summary>
        /// <param name="remoteEP"></param>
        /// <returns></returns>
        public abstract ISocketSession CreateSession(IPEndPoint remoteEP = null);
        #endregion

        #region 辅助
        /// <summary>已重载。</summary>
        /// <returns></returns>
        public override string ToString()
        {
            var remote = RemoteEndPoint;
            if (remote != null) return base.ToString() + "=>" + remote;

            return base.ToString();
        }
        #endregion
    }
}