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. ArrayBuffer

    ArrayBuffer对象.TypedArray视图和DataView视图是 JavaScript 操作二进制数据的一个接口.这些对象早就存在,属于独立的规格(2011 年 2 月发布),ES6 将它 ...

  2. angularjs1 自定义图片查看器(可旋转、放大、缩小、拖拽)

    笔记: angularjs1 制作自定义图片查看器(可旋转.放大.缩小.拖拽) 2018-01-12 更新  可以在我的博客  查看我 已经封装好的 纯 js写的图片查看器插件    博客链接 懒得把 ...

  3. 使用location.hash保存页面状态

    hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分). 语法 location.hash 在我们的项目中,有大量ajax查询表单+结果列表的页面,由于查询结果是a ...

  4. IE 中的 button type默认值问题

    今天遇到一个问题. 将项目页面的渲染模式从 IE7 改为 IE10 后(<meta http-equiv="X-UA-Compatible" content="IE ...

  5. 上传网站后建议执行:chown www:www -R /path/to/dir 对网站目录进行权限设置,/path/to/dir替换为你网站目录。

    上传网站后建议执行:chown www:www -R /path/to/dir 对网站目录进行权限设置,/path/to/dir替换为你网站目录.

  6. linux正则表达式回忆记录

    好久没用linux grep相关正则表达式,现在简单记录下. grep简介 grep 是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来.通常grep有三种版本grep.egr ...

  7. Android--------工具类StatusBarUtil实现完美状态栏

    很早就想写这篇博客了,直到前几天有人问我这方面的问题才想起. 沉浸式状态栏是从android Kitkat(Android 4.4)开始出现的,顶部状态栏的颜色可以根据开发需求改变,使得APP风格更加 ...

  8. NEU 1497 Kid and Ants 思路 难度:0

    问题 I: Kid and Ants 时间限制: 1 Sec  内存限制: 128 MB提交: 42  解决: 33[提交][状态][讨论版] 题目描述 Kid likes interest ques ...

  9. hdu 6113 度度熊的01世界(结构体的赋值问题)

    题目大意: 输入n*m的字符串矩形,判断里面的图形是1还是0,还是什么都不是 注意:结构体中放赋值函数,结构体仍旧能定义的写法 #include <iostream> #include&l ...

  10. web运行异常解决

    端口占用: 在dos下,输入  netstat   -ano|findstr  8080 //说明:查看占用8080端口的进程 显示占用端口的进程 taskkill  /pid  6856  /f   ...