Googler

两情相悦,又岂在朝朝暮暮。

C# TCP异步服务/客户端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace Rocky.Net
{
    public class AsyncTcpListener
    {
        #region Fields
        public event ErrorEventHandler Error;
        public event EventHandler Started;
        public event EventHandler Stopped;
        public event EventHandler<TcpEventArgs> SessionFull;
        public event EventHandler<TcpEventArgs> SessionStart;
        public event EventHandler<TcpEventArgs> SessionSent;
        public event EventHandler<TcpEventArgs> SessionReceived;
        public event EventHandler<TcpEventArgs> SessionEnd;

        private volatile bool _isListening;
        private ushort _maxSession;
        private IPEndPoint _boundEndPoint;
        private Socket _sock;
        private ConcurrentDictionary<int, SessionState> _sessions;
        private BufferSegment _bufferManager;
        #endregion

        #region Properties
        public bool IsListening
        {
            get { return _isListening; }
        }
        public IPEndPoint BoundEndPoint
        {
            get { return _boundEndPoint; }
        }
        public Socket Server
        {
            get { return _sock; }
        }
        public ICollection<SessionState> Clients
        {
            get { return _sessions.Values; }
        }
        #endregion

        #region Constructor
        /// <summary> 
        /// 构造函数 
        /// </summary> 
        /// <param name="endpoint">服务器端监听的地址和端口号</param> 
        /// <param name="maxClient">服务器容纳Session的最大数</param> 
        /// <param name="bufferSize">服务器每个Session的缓冲区大小</param>
        public AsyncTcpListener(IPEndPoint endpoint, ushort maxClient = 100, BufferSizeof? bufferSize = null)
        {
            Contract.Requires(endpoint != null);

            _maxSession = maxClient;
            _boundEndPoint = endpoint;
            int lev = (int)(_maxSession * 0.3);
            _sessions = new ConcurrentDictionary<int, SessionState>(lev, _maxSession);
            if (bufferSize != null)
            {
                _bufferManager = new BufferSegment((int)bufferSize.Value, _maxSession * 2);
            }
        }
        #endregion

        #region EventMethods
        protected virtual void OnError(ErrorEventArgs e)
        {
            if (Error != null)
            {
                Error(this, e);
            }
        }

        protected virtual void OnStarted(EventArgs e)
        {
            _isListening = true;
            if (Started != null)
            {
                Started(this, e);
            }
        }
        protected virtual void OnStopped(EventArgs e)
        {
            _isListening = false;
            if (Stopped != null)
            {
                Stopped(this, e);
            }
        }

        protected virtual void OnSessionFull(TcpEventArgs e)
        {
            if (SessionFull != null)
            {
                SessionFull(this, e);
            }
        }
        protected virtual void OnSessionStart(TcpEventArgs e)
        {
            if (SessionStart != null)
            {
                SessionStart(this, e);
            }
        }
        protected virtual void OnSessionSent(TcpEventArgs e)
        {
            if (SessionSent != null)
            {
                SessionSent(this, e);
            }
        }
        protected virtual void OnSessionReceived(TcpEventArgs e)
        {
            if (SessionReceived != null)
            {
                SessionReceived(this, e);
            }
        }
        protected virtual void OnSessionEnd(TcpEventArgs e)
        {
            if (SessionEnd != null)
            {
                SessionEnd(this, e);
            }
        }
        #endregion

        #region PublicMethods
        public void Start()
        {
            Contract.Requires(!this.IsListening, "服务已运行");

            _sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _sock.Bind(_boundEndPoint);
            _sock.Listen(_maxSession);
            _sock.BeginAccept(AcceptCallback, _sock);
            OnStarted(EventArgs.Empty);
        }

        public void Stop()
        {
            Contract.Requires(this.IsListening, "服务已停止");

            //这个条件语句,一定要在关闭所有客户端以前
            OnStopped(EventArgs.Empty);
            //关闭连接,否则客户端会认为是强制关闭 
            if (_sock.Poll(-1, SelectMode.SelectRead))
            {
                _sock.Shutdown(SocketShutdown.Both);
            }
            foreach (var session in _sessions.Values)
            {
                session.Abandon();
            }
            _sessions.Clear();
            _sock.Close();
        }

        public bool TryGet(int sessionID, out SessionState session)
        {
            return _sessions.TryGetValue(sessionID, out session);
        }

        public IAsyncResult Send(SessionState session)
        {
            Contract.Requires(this.IsListening, "服务已停止");

            var buffer = session.GetBuffer(FileAccess.Write);
            return session.Client.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, SendCallback, session);
        }

        public void Abandon(SessionState session)
        {
            Contract.Requires(this.IsListening, "服务已停止");

            SessionState current;
            if (_sessions.TryRemove(session.SessionID, out current))
            {
                OnSessionEnd(new TcpEventArgs(session));
                current.Abandon();
            }
        }
        #endregion

        #region ProtectedMethods
        protected virtual void AcceptCallback(IAsyncResult ar)
        {
            if (!_isListening)
            {
                return;
            }
            Socket serverSock = (Socket)ar.AsyncState;
            try
            {
                Socket clientSock = serverSock.EndAccept(ar);
                if (_sessions.Count >= _maxSession)
                {
                    var session = new SessionState(clientSock, null);
                    OnSessionFull(new TcpEventArgs(session));
                    //Must do it 服务器满了,必须关闭新来的客户端连接
                    session.Abandon();
                }
                else
                {
                    var session = new SessionState(clientSock, _bufferManager);
                    _sessions.GetOrAdd(session.SessionID, session);
                    //开始接受来自该客户端的数据 
                    var buffer = session.GetBuffer(FileAccess.Read);
                    clientSock.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, session);
                    OnSessionStart(new TcpEventArgs(session));
                }
                _sock.BeginAccept(AcceptCallback, _sock);
            }
            catch (Exception ex)
            {
                OnError(new ErrorEventArgs(ex));
            }
        }

        protected virtual void ReceiveCallback(IAsyncResult ar)
        {
            SessionState session = (SessionState)ar.AsyncState;
            Socket client = session.Client;
            try
            {
                //因为两次开始异步接收,所以当客户端退出的时候会执行两次EndReceive 
                int recv = client.EndReceive(ar);
                SessionState current;
                //正常关闭 
                if (!_sessions.TryGetValue(session.SessionID, out current) || recv == 0)
                {
                    goto abandon;
                }

                var e = session.GetSyncArgs(FileAccess.Read);
                client.ReceiveSync(e, false);
                if (e.IsShutdown)
                {
                    goto abandon;
                }
                OnSessionReceived(new TcpEventArgs(session));
                var buffer = session.GetBuffer(FileAccess.Read);
                client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, session);
                return;

            abandon:
                session.Status = StatusType.NormalExit;
                Abandon(session);
            }
            catch (SocketException ex)
            {
                //客户端强制关闭
                if (ex.ErrorCode == 10054)
                {
                    session.Status = StatusType.ExceptionExit;
                    Abandon(session);
                    return;
                }
                OnError(new ErrorEventArgs(ex));
            }
            catch (ObjectDisposedException ex)
            {
                //当调用Abandon()时,会结束数据接收,但是数据接收处理中会调用int recv = client.EndReceive(ar);就访问了Abandon()已经处置的对象 
                //这里的实现不够优雅
                if (ex != null)
                {
                    ex = null;
                    //DoNothing; 
                }
            }
            catch (Exception ex)
            {
                OnError(new ErrorEventArgs(ex));
            }
        }

        protected virtual void SendCallback(IAsyncResult ar)
        {
            SessionState session = (SessionState)ar.AsyncState;
            Socket client = session.Client;
            try
            {
                long sent = client.EndSend(ar);
                if (sent < 1L)
                {
                    return;
                }

                var e = session.GetSyncArgs(FileAccess.Write);
                sent = client.SendSync(e, false);
                OnSessionSent(new TcpEventArgs(session));
            }
            catch (Exception ex)
            {
                OnError(new ErrorEventArgs(ex));
            }
        }
        #endregion
    }
}

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace Rocky.Net
{
    public class AsyncTcpClient : Disposable
    {
        #region Fields
        public event ErrorEventHandler Error;
        public event EventHandler<TcpEventArgs> Connected;
        public event EventHandler<TcpEventArgs> Sent;
        public event EventHandler<TcpEventArgs> Received;
        public event EventHandler<TcpEventArgs> Disconnected;

        private bool _isConnected;
        private SessionState _session;
        #endregion

        #region Properties
        public bool IsConnected
        {
            get { return _isConnected; }
        }
        public SessionState Session
        {
            get { return _session; }
        }
        #endregion

        #region Constructor
        public AsyncTcpClient()
        {
            _session = new SessionState(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp));
        }

        protected override void DisposeInternal(bool disposing)
        {
            if (disposing)
            {
                Session.Abandon();
                _isConnected = false;
            }
            Connected = null;
            Sent = null;
            Received = null;
            Disconnected = null;
        }
        #endregion

        #region EventMethods
        protected virtual void OnError(ErrorEventArgs e)
        {
            if (Error != null)
            {
                Error(this, e);
            }
        }

        protected virtual void OnConnected(TcpEventArgs e)
        {
            _isConnected = true;
            if (Connected != null)
            {
                Connected(this, e);
            }
        }

        protected virtual void OnSent(TcpEventArgs e)
        {
            if (Sent != null)
            {
                Sent(this, e);
            }
        }

        protected virtual void OnReceived(TcpEventArgs e)
        {
            if (Received != null)
            {
                Received(this, e);
            }
        }

        protected virtual void OnDisconnected(TcpEventArgs e)
        {
            _isConnected = false;
            if (Disconnected != null)
            {
                Disconnected(this, e);
            }
        }
        #endregion

        #region PublicMethods
        public IAsyncResult Connect(string host)
        {
            Contract.Requires(!this.IsConnected, "已连接服务器");
            base.CheckDisposed();

            var ipe = SocketHelper.ParseEndPoint(host);
            return _session.Client.BeginConnect(ipe, ConnectCallback, _session.Client);
        }

        public IAsyncResult Disconnect()
        {
            Contract.Requires(this.IsConnected, "未连接服务器");
            base.CheckDisposed();

            _session.Client.Shutdown(SocketShutdown.Both);
            return _session.Client.BeginDisconnect(true, DisconnectCallback, _session.Client);
        }

        public IAsyncResult Send()
        {
            Contract.Requires(this.IsConnected, "未连接服务器");
            base.CheckDisposed();

            var buffer = _session.GetBuffer(FileAccess.Write);
            return _session.Client.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, SendCallback, _session.Client);
        }
        #endregion

        #region ProtectedMethods
        protected virtual void ConnectCallback(IAsyncResult ar)
        {
            Socket sock = (Socket)ar.AsyncState;
            try
            {
                sock.EndConnect(ar);
                var buffer = _session.GetBuffer(FileAccess.Read);
                _session.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, _session.Client);
                OnConnected(new TcpEventArgs(_session));
            }
            catch (Exception ex)
            {
                OnError(new ErrorEventArgs(ex));
            }
        }

        protected virtual void DisconnectCallback(IAsyncResult ar)
        {
            Socket sock = (Socket)ar.AsyncState;
            try
            {
                sock.EndDisconnect(ar);
                OnDisconnected(new TcpEventArgs(_session));
            }
            catch (Exception ex)
            {
                OnError(new ErrorEventArgs(ex));
            }
        }

        protected virtual void ReceiveCallback(IAsyncResult ar)
        {
            Socket remote = (Socket)ar.AsyncState;
            try
            {
                int recv = remote.EndReceive(ar);
                if (recv == 0)
                {
                    goto disconnected;
                }

                var e = _session.GetSyncArgs(FileAccess.Read);
                remote.ReceiveSync(e, false);
                if (e.IsShutdown)
                {
                    goto disconnected;
                }
                OnReceived(new TcpEventArgs(_session));
                var buffer = _session.GetBuffer(FileAccess.Read);
                remote.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, remote);
                return;

            disconnected:
                _session.Status = StatusType.NormalExit;
                OnDisconnected(new TcpEventArgs(_session));
            }
            catch (SocketException ex)
            {
                if (ex.ErrorCode == 10054)
                {
                    _session.Status = StatusType.ExceptionExit;
                    OnDisconnected(new TcpEventArgs(_session));
                    return;
                }
                OnError(new ErrorEventArgs(ex));
            }
            catch (Exception ex)
            {
                OnError(new ErrorEventArgs(ex));
            }
        }

        protected virtual void SendCallback(IAsyncResult ar)
        {
            Socket remote = (Socket)ar.AsyncState;
            try
            {
                long sent = remote.EndSend(ar);
                if (sent < 1L)
                {
                    return;
                }

                var e = _session.GetSyncArgs(FileAccess.Write);
                sent = remote.SendSync(e, false);
                OnSent(new TcpEventArgs(_session));
            }
            catch (Exception ex)
            {
                OnError(new ErrorEventArgs(ex));
            }
        }
        #endregion
    }
}

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace Rocky.Net
{
    /// <summary>
    /// 建议使用BinaryWriter/BinaryReader 来包装Stream
    /// </summary>
    public class SessionState : Disposable
    {
        #region Fields
        private readonly BufferSegment BufferManager;
        private bool _reuseSocket;
        private Socket _sock;
        private byte[] _sendLength, _receiveLength;
        private MemoryStream _sendStream, _receiveStream;
        private SocketSyncArgs _sendArgs, _receiveArgs;
        #endregion

        #region Properties
        public int SessionID
        {
            get { return _sock.Handle.ToInt32(); }
        }
        public Socket Client
        {
            get { return _sock; }
        }
        public StatusType Status { get; set; }
        #endregion

        #region Constructor
        protected internal SessionState(Socket client, BufferSegment bufferManager = null)
        {
            Contract.Requires(client != null);

            _sock = client;
            int length = sizeof(int);
            _sendLength = new byte[length];
            _receiveLength = new byte[length];
            if (bufferManager == null)
            {
                _reuseSocket = true;
                _sendStream = new MemoryStream();
                _receiveStream = new MemoryStream();
            }
            else
            {
                BufferManager = bufferManager;
                _sendStream = (BufferedMemoryStream)BufferManager.Take();
                _receiveStream = (BufferedMemoryStream)BufferManager.Take();
            }
            _sendArgs = new SocketSyncArgs();
            _receiveArgs = new SocketSyncArgs();
        }

        protected override void DisposeInternal(bool disposing)
        {
            if (disposing)
            {
                //1.Call shutdown with how=SD_SEND.
                //2.Call recv until zero returned, or SOCKET_ERROR.
                //3.Call closesocket.
                if (_sock.Connected)
                {
                    _sock.Shutdown(SocketShutdown.Send);
                }
                if (_reuseSocket)
                {
                    _sock.Disconnect(true);
                }
                else
                {
                    _sock.Close();
                }
            }
            _sendStream.Dispose();
            _receiveStream.Dispose();
            _receiveArgs = _sendArgs = null;
        }

        public void Abandon()
        {
            this.Dispose();
        }
        #endregion

        #region Methods
        public bool Equals(SessionState obj)
        {
            return this.SessionID == obj.SessionID;
        }

        /// <summary> 
        /// 使用Socket对象的Handle值作为HashCode,它具有良好的线性特征.
        /// </summary> 
        /// <returns></returns> 
        public override int GetHashCode()
        {
            return this.SessionID;
        }

        public override string ToString()
        {
            return string.Format("SessionID:{0};Remote={1}", this.SessionID, _sock.RemoteEndPoint);
        }

        internal byte[] GetBuffer(FileAccess access)
        {
            switch (access)
            {
                case FileAccess.Read:
                    return _receiveLength;
                case FileAccess.Write:
                    var e = this.GetSyncArgs(access);
                    var pack = e.GetPackets()[1];
                    _sendLength.SetBytes(0, (int)pack.ContentLength);
                    return _sendLength;
                default:
                    throw new NotSupportedException("FileAccess.ReadWrite");
            }
        }
        internal SocketSyncArgs GetSyncArgs(FileAccess access)
        {
            long length;
            switch (access)
            {
                case FileAccess.Read:
                    length = _receiveLength.ToInt32(0);
                    _receiveStream.Position = 0L;
                    _receiveStream.SetLength(length);
                    _receiveArgs.SetPackets(_receiveStream, length);
                    return _receiveArgs;
                case FileAccess.Write:
                    length = _sendStream.Position;
                    if (length == 0L)
                    {
                        throw new InvalidOperationException("空包");
                    }
                    _sendStream.Position = 0L;
                    _sendStream.SetLength(length);
                    _sendArgs.SetPackets(_sendStream, length);
                    return _sendArgs;
                default:
                    throw new NotSupportedException("FileAccess.ReadWrite");
            }
        }

        public Stream GetStream(FileAccess access)
        {
            base.CheckDisposed();

            long length;
            switch (access)
            {
                case FileAccess.Read:
                    length = _receiveStream.Position;
                    _receiveStream.Position = 0L;
                    _receiveStream.SetLength(length);
                    return _receiveStream;
                case FileAccess.Write:
                    //扩充buffer
                    _sendStream.Position = 0L;
                    _sendStream.SetLength(-1L);
                    return _sendStream;
                default:
                    throw new NotSupportedException("FileAccess.ReadWrite");
            }
        }
        #endregion
    }
}

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Rocky.Net
{
    public enum StatusType
    {
        /// <summary>
        /// 正常
        /// </summary>
        Normal,
        /// <summary>
        /// 超时,待移除
        /// </summary>
        Timeout,
        /// <summary>
        /// 正常断开
        /// </summary>
        NormalExit,
        /// <summary>
        /// 异常断开
        /// </summary>
        ExceptionExit
    }
}

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace Rocky.Net
{
    /// <summary> 
    /// 网络通讯事件参数,包含了激发该事件的会话对象 
    /// </summary> 
    public class TcpEventArgs : EventArgs
    {
        public SessionState Session { get; private set; }

        public TcpEventArgs(SessionState session)
        {
            Contract.Requires(session != null);

            this.Session = session;
        }
    }
}

 

posted on 2013-05-02 20:13  RockyLOMO  阅读(1098)  评论(0编辑  收藏  举报

导航

Apple/苹果笔记本 Mac Air MC968CH/A 行货在保 I5 11寸 超级本