(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 ...
随机推荐
- 【转】jQuery的attr与prop
原文:<jQuery的attr与prop> jQuery1.6中新添加了一个prop方法,看起来和用起来都和attr方法一样,这两个方法有什么区别呢?这要从HTMl 的attribute与 ...
- UIAutomation学习入门
一.界面的自动化操作 .Ui自动化测试 .软件外挂 二.Win32基础知识 a.Win32中一切元素皆窗口,窗口之间有父子关系.整个桌面是一个“根窗口”. b.进程: 根据进程id拿到进程对象Proc ...
- mysql数据库优化课程---14、常用的sql技巧
mysql数据库优化课程---14.常用的sql技巧 一.总结 一句话总结:其实就是sql中那些函数的使用 1.mysql中函数如何使用? 选择字段 其实就是作用域select的选择字段 3.转大写: ...
- JSP 表达式语言
JSP 表达式语言 JSP表达式语言(EL)使得访问存储在JavaBean中的数据变得非常简单.JSP EL既可以用来创建算术表达式也可以用来创建逻辑表达式.在JSP EL表达式内可以使用整型数,浮点 ...
- 初学Laravel框架与ThinkPHP框架的不同
作为一个PHP菜鸟初学Laravel框架 在学习过程中我发现了其与TP框架的不同点,由于时间问题和认识还不够完善我先写出其中几点,有错误的地方希望各位大牛斧正... 1.渲染模版方式的不同:在Lara ...
- Codeforces Round #202 (Div. 2)
第一题水题但是wa了一发,排队记录下收到的25,50,100,看能不能找零,要注意100可以找25*3 复杂度O(n) 第二题贪心,先找出最小的花费,然后就能得出最长的位数,然后循环对每个位上的数看能 ...
- Python 字典的一键多值,即一个键对应多个值
转自:http://blog.csdn.net/houyj1986/article/details/22624981 #encoding=utf-8 print '中国' #字典的一键多值 print ...
- 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 ...
- vue中element 的上传功能
element 的上传功能 最近有个需求,需要在上传文件前,可以进行弹窗控制是否上传upload 看完文档后,感觉有两种思路可以实现 基于before-upload :上传文件之前的钩子,参数为上传的 ...
- 013PHP基础知识——流程控制(一)
<?php /** * 13 流程控制(一) * if语句: if(表达式){ 表达式 }elseif(表达式){ 代码段 } * if语句中,一个条件成立,其他分支不执行. * if中的表达式 ...