(IOCP)-C#高性能Socket服务器的实现
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服务器的实现的更多相关文章
- 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP)
高性能TcpServer(C#) - 1.网络通信协议 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP) 高性能TcpS ...
- 转 C#高性能Socket服务器SocketAsyncEventArgs的实现(IOCP)
原创性申明 本文作者:小竹zz 博客地址:http://blog.csdn.net/zhujunxxxxx/article/details/43573879转载请注明出处引言 我一直在探寻一个高性能 ...
- C#高性能Socket服务器IOCP实现
引言我一直在探寻一个高性能的Socket客户端代码.以前,我使用Socket类写了一些基于传统异步编程模型的代码(BeginSend.BeginReceive,等等)也看过很多博客的知识,在linux ...
- C#高性能Socket服务器SocketAsyncEventArgs的实现(IOCP)
网址:http://blog.csdn.net/zhujunxxxxx/article/details/43573879 引言 我一直在探寻一个高性能的Socket客户端代码.以前,我使用Socket ...
- GJM : 【C# 高性能服务器】完成端口、心跳的高性能Socket服务器 [转载]
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- workerman是一个高性能的PHP socket服务器框架
workerman-chatorkerman是一款纯PHP开发的开源高性能的PHP socket服务器框架.被广泛的用于手机app.手游服务端.网络游戏服务器.聊天室服务器.硬件通讯服务器.智能家居. ...
- Netty实现高性能RPC服务器优化篇之消息序列化
在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...
- 高性能Linux服务器 第11章 构建高可用的LVS负载均衡集群
高性能Linux服务器 第11章 构建高可用的LVS负载均衡集群 libnet软件包<-依赖-heartbeat(包含ldirectord插件(需要perl-MailTools的rpm包)) l ...
- 优化Linux内核参数/etc/sysctl.conf sysctl 《高性能Linux服务器构建实战:运维监控、性能调优与集群应用》
优化Linux内核参数/etc/sysctl.conf sysctl <高性能Linux服务器构建实战:运维监控.性能调优与集群应用> http://book.51cto.com/ar ...
随机推荐
- mac下搭建eclipse+git环境并导入项目
首先官网下载eclipse,然后安装,选择eclipse for java developer. 安装git插件:eclipse-help-install new software-add name随 ...
- git tags 管理
新建标签: git tag -a V1.1 -m "some thing" (新建标签前请先commit代码) 推送标签: git push --tags (推送标签前请先推送代码 ...
- DJango简单的后台定义登录验证
第一步创建一个新的项目 APPLICATIONNAME : 表示创建子项目 第二步:找到主项目的url 进行 include 分发式url 简单的说,就是将app里边的url放在这里. 这里也可以找到 ...
- LM3S之boot loader学习笔记-2
LM3S之boot loader学习笔记-2 彭会锋 () 上一篇中介绍了bootloader的基础知识,对于bootloader的作用和如何编写bootloader也有了大概的了解了,这一篇主要讲解 ...
- 21.线程池ThreadPoolExecutor实现原理
1. 为什么要使用线程池 在实际使用中,线程是很占用系统资源的,如果对线程管理不善很容易导致系统问题.因此,在大多数并发框架中都会使用线程池来管理线程,使用线程池管理线程主要有如下好处: 降低资源消耗 ...
- 安装Linux环境
虚拟机:虚拟机(Virtual Machine),在计算机科学中的体系结构里,是指一种特殊的软件,他可以在计算机平台和终端用户之间建立一种环境,而终端用户则是基于这个软件所建立的环境来操作软件.在计算 ...
- EBS Certifications
Last Updated: September 29, 2017. This summary cross-references published blog articles and the off ...
- 十七、dbms_tts(检查表空间集合是否是自包含)
1.概述 作用:用于检查表空间集合是否是自包含的,并在执行了检查之后,将违反自包含规则的信息写入到临时表TRANSPORT_SET_VIOLATIONS中. 2.包的组成 1).transport_s ...
- Yii ExtendedActiveRecord 增强版 ActiveRecord 增加多数据库连接绑定功能
ExtendedActiveRecord 继承自 CActiveRecord,因此基础功能与 CActiveRecord 无异 为添加对多数据库连接的支持,增加了对 connectionName() ...
- LINUX系统下的shell命令---diff、cut、sort、uniq等
1)diff:比较两个文件或目录的不同 -b 不检查空格字符的不同 -B 不检查空白行 -c 显示全部内容,并标出不同之处 -i 不检查 ...