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

https://www.jianshu.com/p/c65c0eb59f22

引言

我一直在探寻一个高性能的Socket客户端代码。以前,我使用Socket类写了一些基于传统异步编程模型的代码(BeginSend、BeginReceive,等等)也看过很多博客的知识,在linux中有poll和epoll来实现,在windows下面

微软MSDN中也提供了SocketAsyncEventArgs这个类来实现IOCP 地址:https://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx

NET Framework中的APM也称为Begin/End模式。这是因为会调用Begin方法来启动异步操作,然后返回一个IAsyncResult 对象。可以选择将一个代理作为参数提供给Begin方法,异步操作完成时会调用该方法。或者,一个线程可以等待 IAsyncResult.AsyncWaitHandle。当回调被调用或发出等待信号时,就会调用End方法来获取异步操作的结果。这种模式很灵活,使用相对简单,在 .NET Framework 中非常常见。

但是,您必须注意,如果进行大量异步套接字操作,是要付出代价的。针对每次操作,都必须创建一个IAsyncResult对象,而且该对象不能被重复使用。由于大量使用对象分配和垃圾收集,这会影响性能。为了解决这个问题,新版本提供了另一个使用套接字上执行异步I/O的方法模式。这种新模式并不要求为每个套接字操作分配操作上下文对象。

代码下载:http://download.csdn.net/detail/zhujunxxxxx/8431289这里的代码优化了的

目标

在上面微软提供的例子我觉得不是很完整,没有具体一个流程,只是受到客户端消息后发送相同内容给客户端,初学者不容易看懂流程,因为我花了一天的时间来实现一个功能齐全的IOCP服务器,

效果如下



代码

首先是ICOPServer.cs 这个类是IOCP服务器的核心类,目前这个类是网络上比较全的代码,MSDN上面的例子都没有我的全

using System;  

using System.Collections.Generic;  

using System.Linq;  

using System.Text;  

using System.Net.Sockets;  

using System.Net;  

using System.Threading;  

namespace ServerTest  

{  

///   

/// 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.UserTokenas 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);  

        }  

    }  

}

(IOCP)-C#高性能Socket服务器的实现的更多相关文章

  1. 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP)

    高性能TcpServer(C#) - 1.网络通信协议 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP) 高性能TcpS ...

  2. 转 C#高性能Socket服务器SocketAsyncEventArgs的实现(IOCP)

    原创性申明 本文作者:小竹zz  博客地址:http://blog.csdn.net/zhujunxxxxx/article/details/43573879转载请注明出处引言 我一直在探寻一个高性能 ...

  3. C#高性能Socket服务器IOCP实现

    引言我一直在探寻一个高性能的Socket客户端代码.以前,我使用Socket类写了一些基于传统异步编程模型的代码(BeginSend.BeginReceive,等等)也看过很多博客的知识,在linux ...

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

    网址:http://blog.csdn.net/zhujunxxxxx/article/details/43573879 引言 我一直在探寻一个高性能的Socket客户端代码.以前,我使用Socket ...

  5. GJM : 【C# 高性能服务器】完成端口、心跳的高性能Socket服务器 [转载]

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  6. workerman是一个高性能的PHP socket服务器框架

    workerman-chatorkerman是一款纯PHP开发的开源高性能的PHP socket服务器框架.被广泛的用于手机app.手游服务端.网络游戏服务器.聊天室服务器.硬件通讯服务器.智能家居. ...

  7. Netty实现高性能RPC服务器优化篇之消息序列化

    在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...

  8. 高性能Linux服务器 第11章 构建高可用的LVS负载均衡集群

    高性能Linux服务器 第11章 构建高可用的LVS负载均衡集群 libnet软件包<-依赖-heartbeat(包含ldirectord插件(需要perl-MailTools的rpm包)) l ...

  9. 优化Linux内核参数/etc/sysctl.conf sysctl 《高性能Linux服务器构建实战:运维监控、性能调优与集群应用》

    优化Linux内核参数/etc/sysctl.conf  sysctl  <高性能Linux服务器构建实战:运维监控.性能调优与集群应用> http://book.51cto.com/ar ...

随机推荐

  1. 【转】jQuery的attr与prop

    原文:<jQuery的attr与prop> jQuery1.6中新添加了一个prop方法,看起来和用起来都和attr方法一样,这两个方法有什么区别呢?这要从HTMl 的attribute与 ...

  2. UIAutomation学习入门

    一.界面的自动化操作 .Ui自动化测试 .软件外挂 二.Win32基础知识 a.Win32中一切元素皆窗口,窗口之间有父子关系.整个桌面是一个“根窗口”. b.进程: 根据进程id拿到进程对象Proc ...

  3. mysql数据库优化课程---14、常用的sql技巧

    mysql数据库优化课程---14.常用的sql技巧 一.总结 一句话总结:其实就是sql中那些函数的使用 1.mysql中函数如何使用? 选择字段 其实就是作用域select的选择字段 3.转大写: ...

  4. JSP 表达式语言

    JSP 表达式语言 JSP表达式语言(EL)使得访问存储在JavaBean中的数据变得非常简单.JSP EL既可以用来创建算术表达式也可以用来创建逻辑表达式.在JSP EL表达式内可以使用整型数,浮点 ...

  5. 初学Laravel框架与ThinkPHP框架的不同

    作为一个PHP菜鸟初学Laravel框架 在学习过程中我发现了其与TP框架的不同点,由于时间问题和认识还不够完善我先写出其中几点,有错误的地方希望各位大牛斧正... 1.渲染模版方式的不同:在Lara ...

  6. Codeforces Round #202 (Div. 2)

    第一题水题但是wa了一发,排队记录下收到的25,50,100,看能不能找零,要注意100可以找25*3 复杂度O(n) 第二题贪心,先找出最小的花费,然后就能得出最长的位数,然后循环对每个位上的数看能 ...

  7. Python 字典的一键多值,即一个键对应多个值

    转自:http://blog.csdn.net/houyj1986/article/details/22624981 #encoding=utf-8 print '中国' #字典的一键多值 print ...

  8. leetcode 849. Maximize Distance to Closest Person

    In a row of seats, 1 represents a person sitting in that seat, and 0 represents that the seat is emp ...

  9. vue中element 的上传功能

    element 的上传功能 最近有个需求,需要在上传文件前,可以进行弹窗控制是否上传upload 看完文档后,感觉有两种思路可以实现 基于before-upload :上传文件之前的钩子,参数为上传的 ...

  10. 013PHP基础知识——流程控制(一)

    <?php /** * 13 流程控制(一) * if语句: if(表达式){ 表达式 }elseif(表达式){ 代码段 } * if语句中,一个条件成立,其他分支不执行. * if中的表达式 ...