• 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;
            }
        }
    }
  • 相关阅读:
    选择排序
    转:ASP.NET MVC中Unobtrusive Ajax的妙用
    转:MVC 下导航超链接本页面高亮的一种解决方案
    转载:iOS 推送的服务端实现
    转载:Unobtrusive JavaScript in ASP.NET MVC 3 隐式的脚本在MVC3
    待实践三:MVC3下 路由的测试 使用 RouteDebug.dll 来测试判断路由是否符合
    待实践二:MVC3下的3种验证 (1)前台 jquery validate验证 (2)MVC实体验证 (3)EF生成的/自己手写的 自定义实体校验(伙伴类+元素据共享)
    待实践一:仿造博客园后台上传头像并切图生成缩略图fine uploader.js jcrop.js
    Jquery 模板插件 jquery.tmpl.js 的使用方法(1):基本语法,绑定,each循环,ajax获取json数据
    Linq 时间对比陷阱坑
  • 原文地址:https://www.cnblogs.com/Googler/p/3055331.html
Copyright © 2020-2023  润新知