C# Socket高性能 IOCP

本文作者:小飞Jim,转载请写明出处:http://blog.csdn.net/lanmangfeige/article/details/53118043

C#实现Socket通讯有同步模式与异步模式,异步模式的效率比同步模式高。在异步模式中,SocketAsyncEventArgs 类提供了增强功能。

这些增强功能可以避免重复分配的和在大量异步套接字 I/O 内进行同步的对象。通过创建一个SocketAsyncEventArgsPool 池来提高客户端连接速率。

源码下载地址:http://download.csdn.net/detail/lanmangfeige/9679294

IOCP主类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;   
using System.Net.Sockets;
using System.Threading;   

namespace CsharpIOCP
{
    class IOCP
    {
        /// 
        /// Socket-Server
        /// 
        Socket s_Server;

        /// 
        /// 通讯SAEA池
        /// 
        SAEAPool saeaPool_Receive;

        /// 
        /// 侦听客户端
        /// 
        public void ListenClient()
        {
            try
            {
                int iClientMaxCount = 1000; ;//最大客户端数量
                s_Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                s_Server.Bind(new IPEndPoint(IPAddress.Any, 8801));
                s_Server.Listen(iClientMaxCount);               
                int ibufferSize = 1024; //每个缓冲区大小
                BufferManager bufferManager = new BufferManager(ibufferSize * iClientMaxCount, ibufferSize);
                saeaPool_Receive = new SAEAPool(iClientMaxCount);
                for (int i = 0; i < iClientMaxCount; i++) //填充SocketAsyncEventArgs池
                {
                    SocketAsyncEventArgs saea_New = new SocketAsyncEventArgs();
                    saea_New.Completed += new EventHandler(OnIOCompleted);
                    bufferManager.SetBuffer(saea_New);
                    SAEAUserToken userToken = new SAEAUserToken();
                    SocketAsyncEventArgs saea_Send = new SocketAsyncEventArgs();
                    saea_Send.Completed += new EventHandler(OnIOCompleted);
                    userToken.SAEA_Send = saea_Send;
                    userToken.HeartbeatTime = DateTime.Now;
                    saea_New.UserToken = userToken;
                    saeaPool_Receive.Add(saea_New);
                }

                Thread tCheckClientHeartbeat = new Thread(CheckClientHeartbeat);
                tCheckClientHeartbeat.IsBackground = true;
                tCheckClientHeartbeat.Start();

                StartAccept(null);
            }
            catch { }
        }

        /// 
        /// 接受来自客户机的连接请求操作
        /// 
        private void StartAccept(SocketAsyncEventArgs saea_Accept)
        {
            if (saea_Accept == null)
            {
                saea_Accept = new SocketAsyncEventArgs();
                saea_Accept.Completed += new EventHandler(OnAcceptCompleted);
            }
            else
                saea_Accept.AcceptSocket = null;  //重用前进行对象清理

            if (!s_Server.AcceptAsync(saea_Accept))
                ProcessAccept(saea_Accept);
        }

        /// 
        /// 连接完成异步操作回调
        /// 
        private void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept(e);
        }

        /// 
        /// 接收或发送完成异步操作回调
        /// 
        private void OnIOCompleted(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
            }
        }

        /// 
        /// 异步连接操作完成后调用该方法
        /// 
        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            Socket s = e.AcceptSocket;
            if (s != null && s.Connected)
            {
                try
                {
                    string sClientIP = ((IPEndPoint)s.RemoteEndPoint).Address.ToString();
                    Console.WriteLine(sClientIP + " Client online");
                    SocketAsyncEventArgs saea_Receive = saeaPool_Receive.Pull();
                    if (saea_Receive != null)
                    {
                        Console.WriteLine("Online Client total:" + saeaPool_Receive.GetUsedSAEACount());
                        SAEAUserToken userToken = (SAEAUserToken)saea_Receive.UserToken;
                        userToken.S = s;

                        if (!userToken.S.ReceiveAsync(saea_Receive))
                            ProcessReceive(saea_Receive);
                    }
                    else
                    {
                        s.Close(); 
                        Console.WriteLine(sClientIP + " Can't connect server,because connection pool has been finished !");
                    }
                }
                catch { }
            }
            StartAccept(e);
        }

        /// 
        /// 异步接收操作完成后调用该方法
        /// 
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            try
            {
                if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                {
                    SAEAUserToken userToken = (SAEAUserToken)e.UserToken;
                    userToken.HeartbeatTime = DateTime.Now;
                    string sClientIP = ((IPEndPoint)userToken.S.RemoteEndPoint).Address.ToString();
                    try
                    {
                        byte[] abFactReceive = new byte[e.BytesTransferred];
                        Array.Copy(e.Buffer, e.Offset, abFactReceive, 0, e.BytesTransferred);
                        Console.WriteLine("From the " + sClientIP + " to receive " + e.BytesTransferred + " bytes of data:" + BitConverter.ToString(abFactReceive));
                    }
                    catch { }
                    finally
                    {
                        if (!userToken.S.ReceiveAsync(e))
                            ProcessReceive(e);
                    }
                }
                else
                    CloseClientSocket(e);
            }
            catch { }
        }

        /// 
        /// 异步发送操作完成后调用该方法
        /// 
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            
        }

        /// 
        /// Socket 断开处理
        /// 
        private void CloseClientSocket(SocketAsyncEventArgs saea)
        {
            try
            {
                SAEAUserToken userToken = (SAEAUserToken)saea.UserToken;
                if (!saeaPool_Receive.Push(saea))
                    return;                
                if (userToken.S != null)
                {                    
                    if (userToken.S.Connected)
                    {                        
                        try
                        {
                            userToken.S.Shutdown(SocketShutdown.Both);
                        }
                        catch { }
                        string sClientIP = ((IPEndPoint)userToken.S.RemoteEndPoint).Address.ToString();
                        Console.WriteLine(sClientIP + " disconnect !");
                    }
                    userToken.S.Close();                    
                }                
                Console.WriteLine("Online Client total:" + saeaPool_Receive.GetUsedSAEACount());
            }
            catch { }
        }

        /// 
        /// 客户端心跳检测
        /// 
        private void CheckClientHeartbeat()
        {
            while (true)
            {
                try
                {
                    int iCheckInterval = 10 * 1000; //10秒检测间隔
                    Thread.Sleep(iCheckInterval);
                    List lUserdSAEA = saeaPool_Receive.GetUsedSAEA();
                    if (lUserdSAEA != null && lUserdSAEA.Count > 0)
                    {
                        foreach (SocketAsyncEventArgs saea in lUserdSAEA)
                        {
                            SAEAUserToken userToken = (SAEAUserToken)saea.UserToken;
                            if (userToken.HeartbeatTime.AddMilliseconds(iCheckInterval).CompareTo(DateTime.Now) < 0)
                            {
                                if (userToken.S != null)
                                {
                                    try
                                    {
                                        string sClientIP = ((IPEndPoint)userToken.S.RemoteEndPoint).Address.ToString();
                                        Console.WriteLine(sClientIP + " the heartbeat timeout !");
                                    }
                                    catch { }
                                    userToken.S.Close(); //服务端主动关闭心跳超时连接,在此关闭连接,会触发OnIOCompleted回调                                    
                                }
                            }
                        }
                    }
                }
                catch { }
            }
        }
    }
}


SocketAsyncEventArgs 对象池

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;

namespace CsharpIOCP
{
    /// 
    /// 与每个客户Socket相关联,进行Send和Receive投递时所需要的参数
    /// 
    internal sealed class SAEAPool
    {
        List pool; //为每一个Socket客户端分配一个SocketAsyncEventArgs,用一个List管理,在程序启动时建立
        Int32 capacity; //pool对象池的容量
        Int32 boundary; //已分配和未分配对象的边界

        internal SAEAPool(Int32 capacity)
        {
            pool = new List();
            boundary = 0;
            this.capacity = capacity;
        }

        /// 
        /// 往pool对象池中增加新建立的对象,因为这个程序在启动时会建立好所有对象,
        /// 故这个方法只在初始化时会被调用,因此,没有加锁。
        /// 
        /// 
        /// 
        internal bool Add(SocketAsyncEventArgs arg)
        {
            if (arg != null && pool.Count < capacity)
            {
                pool.Add(arg);
                boundary++;
                return true;
            }
            else
                return false;
        }

        /// 
        /// 获取正在使用的 SocketAsyncEventArgs 对象集合
        /// 
        /// 
        internal List GetUsedSAEA()
        {
            lock (pool)
            {
                List lUsedSAEA = new List();
                for (int i = boundary; i < capacity; i++)
                    lUsedSAEA.Add(pool[i]);
                return lUsedSAEA;
            }
        }

        /// 
        /// 获取已使用的 SocketAsyncEventArgs 对象总数
        /// 
        /// 
        internal string GetUsedSAEACount()
        {
            return (capacity - boundary).ToString();
        }

        /// 
        /// 从对象池中取出一个对象,交给一个socket来进行投递请求操作
        /// 
        /// 
        internal SocketAsyncEventArgs Pull()
        {
            lock (pool)
            {
                if (boundary > 0)
                {
                    --boundary;
                    return pool[boundary];
                }
                else
                    return null;
            }
        }

        /// 
        /// 一个socket客户断开,与其相关的SocketAsyncEventArgs被释放,重新投入Pool中,以备用。
        /// 
        /// 
        /// 
        internal bool Push(SocketAsyncEventArgs arg)
        {
            if (arg != null)
            {
                lock (pool)
                {
                    int index = pool.IndexOf(arg, boundary); //找出被断开的客户
                    if (index >= 0)
                    {
                        if (index == boundary) //正好是边界元素
                            boundary++;
                        else
                        {
                            pool[index] = pool[boundary]; //将断开客户移到边界上,边界右移
                            pool[boundary++] = arg;
                        }
                    }
                    else
                        return false;
                }
                return true;
            }
            else
                return false;
        }
    }
}


SocketAsyncEventArgs 缓冲区管理类

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

namespace CsharpIOCP
{
    /// 
    /// SocketAsyncEventArgs 缓冲区管理类,
    /// 创建一个大型缓冲区,该缓冲区可以进行分割并指定给 SocketAsyncEventArgs 对象以便用在每个套接字 I/O 操作中。
    /// 这样可以很方便地重用缓冲区,并防止堆内存碎片化。
    /// 
    internal class BufferManager
    {
        int m_numBytes; //缓冲池的总容量
        byte[] m_buffer; //缓冲池
        Stack m_freeIndexPool; //后进先出数据结构
        int m_currentIndex; //当前缓冲池索引
        int m_bufferSize; //单个缓冲区容量

        /// 
        /// 缓冲池重载
        /// 
        /// 缓冲池的总容量
        /// 单个缓冲区容量
        public BufferManager(int totalBytes, int bufferSize)
        {
            m_numBytes = totalBytes;
            m_currentIndex = 0;
            m_bufferSize = bufferSize;
            m_freeIndexPool = new Stack();
            InitBuffer();
        }

        /// 
        /// 初始化缓冲池
        /// 
        private void InitBuffer()
        {            
            m_buffer = new byte[m_numBytes]; //创建大型缓冲池
        }

        /// 
        /// 从缓冲池中分配一个缓冲区给指定SocketAsyncEventArgs对象
        /// 
        /// 
        /// 
        public bool SetBuffer(SocketAsyncEventArgs args)
        {
            lock(m_freeIndexPool)
            {
                if (m_freeIndexPool.Count > 0)
                {
                    args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
                }
                else
                {
                    if ((m_numBytes - m_bufferSize) < m_currentIndex)
                        return false;
                    args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
                    m_currentIndex += m_bufferSize;
                }
                return true;
            }
        }

        /// 
        /// 将缓冲区释放到缓冲池中
        /// 
        /// 
        public void FreeBuffer(SocketAsyncEventArgs args)
        {
            lock (m_freeIndexPool)
            {
                m_freeIndexPool.Push(args.Offset);
                args.SetBuffer(null, 0, 0);
            }
        }

    }
}

SocketAsyncEventArgs  用户标记类

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

namespace CsharpIOCP
{
    /// 
    /// SAEA用户标记类
    /// 
   internal class SAEAUserToken
    {
        /// 
        /// 用于发送数据的SocketAsyncEventArgs
        /// 
        public SocketAsyncEventArgs SAEA_Send;

        /// 
        /// 连接套接字
        /// 
        public Socket S;

        /// 
        /// 最新一次心跳时间
        /// 
        public DateTime HeartbeatTime;
    }
}


你可能感兴趣的:(网络通讯)