C#高性能Socket服务器的实现(IOCP)

无论什么平台,编写支持高并发性的网络服务器,瓶颈往往出在I/O上,目前最高效的是采用Asynchronous I/O模型,Linux平台提供了epoll,Windows平台提供了I/O Completion Port(IO完成端口,即IOCP)。

在实际应用中,证明C#编写基于.Net IOCP的高性能服务器可以支持10000个以上的TCP长连接。但在具体实现过程中需要注意几个问题:
1.SocketAsyncEventArgs和Buffer最好预先分配,并能回收重复利用。
2.一个Socket的Send和Receive最好分别对应一个SocketAsyncEventArgs,因为当一个SocketAsyncEventArgs被ReceiveAsync调用挂起后,在调用SendAsync时就会出异常。同样不要对一个SocketAsyncEventArgs在一个异步操作被挂起时再次调用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace IOCPTest
{
    /// 

    /// IOCP SOCKET服务器

    /// 

    public class IOCPServer : IDisposable

    {

        const int opsToPreAlloc = 2;

        #region Fields

        /// 

        /// 服务器程序允许的最大客户端连接数

        /// 

        private int _maxClient;

        /// 

        /// 监听Socket,用于接受客户端的连接请求

        /// 

        private Socket _serverSock;

        /// 

        /// 当前的连接的客户端数

        /// 

        private int _clientCount;



        /// 

        /// 用于每个I/O Socket操作的缓冲区大小

        /// 

        private int _bufferSize = 1024;

        /// 

        /// 信号量

        /// 

        Semaphore _maxAcceptedClients;


        /// 

        /// 缓冲区管理

        /// 

        BufferManager _bufferManager;


        /// 

        /// 对象池

        /// 

        SocketAsyncEventArgsPool _objectPool;

        private bool disposed = false;

        #endregion

        #region Properties


        /// 

        /// 服务器是否正在运行

        /// 

        public bool IsRunning { get; private set; }

        /// 

        /// 监听的IP地址

        /// 

        public IPAddress Address { get; private set; }

        /// 

        /// 监听的端口

        /// 

        public int Port { get; private set; }

        /// 

        /// 通信使用的编码

        /// 

        public Encoding Encoding { get; set; }



        #endregion

        #region Ctors



        /// 

        /// 异步IOCP SOCKET服务器

        /// 

        /// 监听的端口

        /// 最大的客户端数量

        public IOCPServer(int listenPort, int maxClient)

            : this(IPAddress.Any, listenPort, maxClient)

        {

        }



        /// 

        /// 异步Socket TCP服务器

        /// 

        /// 监听的终结点

        /// 最大客户端数量

        public IOCPServer(IPEndPoint localEP, int maxClient)

            : this(localEP.Address, localEP.Port, maxClient)

        {

        }



        /// 

        /// 异步Socket TCP服务器

        /// 

        /// 监听的IP地址

        /// 监听的端口

        /// 最大客户端数量

        public IOCPServer(IPAddress localIPAddress, int listenPort, int maxClient)

        {

            this.Address = localIPAddress;

            this.Port = listenPort;

            this.Encoding = Encoding.Default;



            _maxClient = maxClient;



            _serverSock = new Socket(localIPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);



            _bufferManager = new BufferManager(_bufferSize * _maxClient * opsToPreAlloc, _bufferSize);



            _objectPool = new SocketAsyncEventArgsPool(_maxClient);



            _maxAcceptedClients = new Semaphore(_maxClient, _maxClient);

        }



        #endregion

        #region 初始化



        /// 

        /// 初始化函数

        /// 

        public void Init()

        {

            // Allocates one large byte buffer which all I/O operations use a piece of.  This gaurds 

            // against memory fragmentation

            _bufferManager.InitBuffer();



            // preallocate pool of SocketAsyncEventArgs objects

            SocketAsyncEventArgs readWriteEventArg;



            for (int i = 0; i < _maxClient; i++)

            {

                //Pre-allocate a set of reusable SocketAsyncEventArgs

                readWriteEventArg = new SocketAsyncEventArgs();

                readWriteEventArg.Completed += new EventHandler(OnIOCompleted);

                readWriteEventArg.UserToken = null;



                // assign a byte buffer from the buffer pool to the SocketAsyncEventArg object

                _bufferManager.SetBuffer(readWriteEventArg);



                // add SocketAsyncEventArg to the pool

                _objectPool.Push(readWriteEventArg);

            }



        }



        #endregion

        #region Start

        /// 

        /// 启动

        /// 

        public void Start()

        {

            if (!IsRunning)

            {

                Init();

                IsRunning = true;

                IPEndPoint localEndPoint = new IPEndPoint(Address, Port);

                // 创建监听socket

                _serverSock = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                //_serverSock.ReceiveBufferSize = _bufferSize;

                //_serverSock.SendBufferSize = _bufferSize;

                if (localEndPoint.AddressFamily == AddressFamily.InterNetworkV6)

                {

                    // 配置监听socket为 dual-mode (IPv4 & IPv6) 

                    // 27 is equivalent to IPV6_V6ONLY socket option in the winsock snippet below,

                    _serverSock.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false);

                    _serverSock.Bind(new IPEndPoint(IPAddress.IPv6Any, localEndPoint.Port));

                }

                else

                {

                    _serverSock.Bind(localEndPoint);

                }

                // 开始监听

                _serverSock.Listen(this._maxClient);

                // 在监听Socket上投递一个接受请求。

                StartAccept(null);

            }

        }

        #endregion

        #region Stop



        /// 

        /// 停止服务

        /// 

        public void Stop()

        {

            if (IsRunning)

            {

                IsRunning = false;

                _serverSock.Close();

                //TODO 关闭对所有客户端的连接



            }

        }



        #endregion

        #region Accept



        /// 

        /// 从客户端开始接受一个连接操作

        /// 

        private void StartAccept(SocketAsyncEventArgs asyniar)

        {

            if (asyniar == null)

            {

                asyniar = new SocketAsyncEventArgs();

                asyniar.Completed += new EventHandler(OnAcceptCompleted);

            }

            else

            {

                //socket must be cleared since the context object is being reused

                asyniar.AcceptSocket = null;

            }

            _maxAcceptedClients.WaitOne();

            if (!_serverSock.AcceptAsync(asyniar))

            {

                ProcessAccept(asyniar);

                //如果I/O挂起等待异步则触发AcceptAsyn_Asyn_Completed事件

                //此时I/O操作同步完成,不会触发Asyn_Completed事件,所以指定BeginAccept()方法

            }

        }



        /// 

        /// accept 操作完成时回调函数

        /// 

        /// Object who raised the event.

        /// SocketAsyncEventArg associated with the completed accept operation.

        private void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)

        {

            ProcessAccept(e);

        }



        /// 

        /// 监听Socket接受处理

        /// 

        /// SocketAsyncEventArg associated with the completed accept operation.

        private void ProcessAccept(SocketAsyncEventArgs e)

        {

            if (e.SocketError == SocketError.Success)

            {

                Socket s = e.AcceptSocket;//和客户端关联的socket

                if (s.Connected)

                {

                    try

                    {



                        Interlocked.Increment(ref _clientCount);//原子操作加1

                        SocketAsyncEventArgs asyniar = _objectPool.Pop();

                        asyniar.UserToken = s;



                        Log4Debug(String.Format("客户 {0} 连入, 共有 {1} 个连接。", s.RemoteEndPoint.ToString(), _clientCount));



                        if (!s.ReceiveAsync(asyniar))//投递接收请求

                        {

                            ProcessReceive(asyniar);

                        }

                    }

                    catch (SocketException ex)

                    {

                        Log4Debug(String.Format("接收客户 {0} 数据出错, 异常信息: {1} 。", s.RemoteEndPoint, ex.ToString()));

                        //TODO 异常处理

                    }

                    //投递下一个接受请求

                    StartAccept(e);

                }

            }

        }



        #endregion

        #region 发送数据



        /// 

        /// 异步的发送数据

        /// 

        /// 

        /// 

        public void Send(SocketAsyncEventArgs e, byte[] data)

        {

            if (e.SocketError == SocketError.Success)

            {

                Socket s = e.AcceptSocket;//和客户端关联的socket

                if (s.Connected)

                {

                    Array.Copy(data, 0, e.Buffer, 0, data.Length);//设置发送数据



                    //e.SetBuffer(data, 0, data.Length); //设置发送数据

                    if (!s.SendAsync(e))//投递发送请求,这个函数有可能同步发送出去,这时返回false,并且不会引发SocketAsyncEventArgs.Completed事件

                    {

                        // 同步发送时处理发送完成事件

                        ProcessSend(e);

                    }

                    else

                    {

                        CloseClientSocket(e);

                    }

                }

            }

        }



        /// 

        /// 同步的使用socket发送数据

        /// 

        /// 

        /// 

        /// 

        /// 

        /// 

        public void Send(Socket socket, byte[] buffer, int offset, int size, int timeout)

        {

            socket.SendTimeout = 0;

            int startTickCount = Environment.TickCount;

            int sent = 0; // how many bytes is already sent

            do

            {

                if (Environment.TickCount > startTickCount + timeout)

                {

                    //throw new Exception("Timeout.");

                }

                try

                {

                    sent += socket.Send(buffer, offset + sent, size - sent, SocketFlags.None);

                }

                catch (SocketException ex)

                {

                    if (ex.SocketErrorCode == SocketError.WouldBlock ||

                    ex.SocketErrorCode == SocketError.IOPending ||

                    ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)

                    {

                        // socket buffer is probably full, wait and try again

                        Thread.Sleep(30);

                    }

                    else

                    {

                        throw ex; // any serious error occurr

                    }

                }

            } while (sent < size);

        }





        /// 

        /// 发送完成时处理函数

        /// 

        /// 与发送完成操作相关联的SocketAsyncEventArg对象

        private void ProcessSend(SocketAsyncEventArgs e)

        {

            if (e.SocketError == SocketError.Success)

            {

                Socket s = (Socket)e.UserToken;



                //TODO

            }

            else

            {

                CloseClientSocket(e);

            }

        }



        #endregion

        #region 接收数据





        /// 

        ///接收完成时处理函数

        /// 

        /// 与接收完成操作相关联的SocketAsyncEventArg对象

        private void ProcessReceive(SocketAsyncEventArgs e)

        {

            if (e.SocketError == SocketError.Success)//if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)

            {

                // 检查远程主机是否关闭连接

                if (e.BytesTransferred > 0)

                {

                    Socket s = (Socket)e.UserToken;

                    //判断所有需接收的数据是否已经完成

                    if (s.Available == 0)

                    {

                        //从侦听者获取接收到的消息。 

                        //String received = Encoding.ASCII.GetString(e.Buffer, e.Offset, e.BytesTransferred);

                        //echo the data received back to the client

                        //e.SetBuffer(e.Offset, e.BytesTransferred);



                        byte[] data = new byte[e.BytesTransferred];

                        Array.Copy(e.Buffer, e.Offset, data, 0, data.Length);//从e.Buffer块中复制数据出来,保证它可重用



                        string info = Encoding.Default.GetString(data);

                        Log4Debug(String.Format("收到 {0} 数据为 {1}", s.RemoteEndPoint.ToString(), info));

                        //TODO 处理数据



                        //增加服务器接收的总字节数。

                    }



                    if (!s.ReceiveAsync(e))//为接收下一段数据,投递接收请求,这个函数有可能同步完成,这时返回false,并且不会引发SocketAsyncEventArgs.Completed事件

                    {

                        //同步接收时处理接收完成事件

                        ProcessReceive(e);

                    }

                }

            }

            else

            {

                CloseClientSocket(e);

            }

        }



        #endregion

        #region 回调函数



        /// 

        /// 当Socket上的发送或接收请求被完成时,调用此函数

        /// 

        /// 激发事件的对象

        /// 与发送或接收完成操作相关联的SocketAsyncEventArg对象

        private void OnIOCompleted(object sender, SocketAsyncEventArgs e)

        {

            // Determine which type of operation just completed and call the associated handler.

            switch (e.LastOperation)

            {

                case SocketAsyncOperation.Accept:

                    ProcessAccept(e);

                    break;

                case SocketAsyncOperation.Receive:

                    ProcessReceive(e);

                    break;

                default:

                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");

            }

        }



        #endregion

        #region Close

        /// 

        /// 关闭socket连接

        /// 

        /// SocketAsyncEventArg associated with the completed send/receive operation.

        private void CloseClientSocket(SocketAsyncEventArgs e)

        {

            Log4Debug(String.Format("客户 {0} 断开连接!", ((Socket)e.UserToken).RemoteEndPoint.ToString()));

            Socket s = e.UserToken as Socket;

            CloseClientSocket(s, e);

        }



        /// 

        /// 关闭socket连接

        /// 

        /// 

        /// 

        private void CloseClientSocket(Socket s, SocketAsyncEventArgs e)

        {

            try

            {

                s.Shutdown(SocketShutdown.Send);

            }

            catch (Exception)

            {

                // Throw if client has closed, so it is not necessary to catch.

            }

            finally

            {

                s.Close();

            }

            Interlocked.Decrement(ref _clientCount);

            _maxAcceptedClients.Release();

            _objectPool.Push(e);//SocketAsyncEventArg 对象被释放,压入可重用队列。

        }

        #endregion

        #region Dispose

        /// 

        /// Performs application-defined tasks associated with freeing, 

        /// releasing, or resetting unmanaged resources.

        /// 

        public void Dispose()

        {

            Dispose(true);

            GC.SuppressFinalize(this);

        }



        /// 

        /// Releases unmanaged and - optionally - managed resources

        /// 

        /// true to release 

        /// both managed and unmanaged resources; false 

        /// to release only unmanaged resources.

        protected virtual void Dispose(bool disposing)

        {

            if (!this.disposed)

            {

                if (disposing)

                {

                    try

                    {

                        Stop();

                        if (_serverSock != null)

                        {

                            _serverSock = null;

                        }

                    }

                    catch (SocketException ex)

                    {

                        //TODO 事件

                    }

                }

                disposed = true;

            }

        }

        #endregion

        public void Log4Debug(string msg)

        {

            Console.WriteLine("notice:" + msg);

        }



    }

}

 

你可能感兴趣的:(.net)