C#基于SocketAsyncEventArgs实现的完成端口封装,可以支持65535个连接

SQLDebug_Fan 2014-02-12 01:57:21
加精
原文地址:http://blog.csdn.net/sqldebug_fan/article/details/17556353
例子主要包括SocketAsyncEventArgs通讯封装、服务端实现日志查看、SCOKET列表、上传、下载、远程文件流、吞吐量协议,用于测试SocketAsyncEventArgs的性能和压力,最大连接数支持65535个长连接,最高命令交互速度达到250MB/S(使用的是127.0.0.1的方式,相当于千兆网卡1Gb=125MB/S两倍的吞吐量)。服务端用C#编写,并使用log4net作为日志模块;客户端使用DELPHI编写,和以前的DELPHI版完成端口共用一个客户端,没有提供C#版的客户端。
服务端
服务端是一个控制台程序,支持分颜色显示日志,界面截图如下:

客户端
用于对服务端进行性能测试,实现了吞吐量测试协议,还有日志、查看客户端、上传、下载、远程文件流五个应用层协议,可以作为要实现其他协议的例子参考,其中远程文件流可以作为分布式系统的底层数据交互模块,界面截图如下:

性能测试结果
支持65535个长连接,为了支持65535个连接,由于测试程序是32位,最大内存2G,需要把每个连接使用的内存改下,主要修改ProtocolKey.cs单元的这两个参数,默认是4KB,我们修改为1KB。

public class ProtocolConst
{
public static int InitBufferSize = 1024; //解析命令初始缓存大小
public static int ReceiveBufferSize = 1024; //IOCP接收数据缓存大小,设置过小会造成事件响应增多,设置过大会造成内存占用偏多
}

循环发包网络流量可以达到250MB/S,受限于是网卡流量,千兆网卡最大1Gb=125MB/S,为了测试最大吞吐量,我们需要把客户端和服务器放在同一台电脑上,使用127.0.0.1这个IP测试,127.0.0.1是本机回送地址(Loopback Address),即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,协议软件立即返回,不进行任何网络传输。
其中截图如下:


DEMO下载地址:http://download.csdn.net/detail/sqldebug_fan/6874257,例子代码中服务端用C#编写,客户端用DELPHI编写。
免责声明:此代码只是为了演示C#完成端口编程,仅用于学习和研究,切勿用于商业用途。水平有限,C#也属于初学,错误在所难免,欢迎指正和指导。邮箱地址:fansheng_hx@163.com。
...全文
8409 58 打赏 收藏 转发到动态 举报
写回复
用AI写文章
58 条回复
切换为时间正序
请发表友善的回复…
发表回复
越过越咸 2015-11-12
  • 打赏
  • 举报
回复
Jeece_JX 2015-08-28
  • 打赏
  • 举报
回复
太牛了!!!
OriginalCobra 2015-01-06
  • 打赏
  • 举报
回复
请问怎么通过服务端向客户端发送消息呢?
bob76012 2014-02-19
  • 打赏
  • 举报
回复
jAmEs_ 2014-02-18
  • 打赏
  • 举报
回复
好帖,见识了
austin9972 2014-02-17
  • 打赏
  • 举报
回复
啥都得学呀 2014-02-17
  • 打赏
  • 举报
回复
2个大牛引经据典的PK,喜闻乐见!真是大开眼界!
bob76012 2014-02-17
  • 打赏
  • 举报
回复
godshome 2014-02-16
  • 打赏
  • 举报
回复
学习了..
SQLDebug_Fan 2014-02-16
  • 打赏
  • 举报
回复
Windows提供了Select、重叠IO(AcceptSync、SendSync)和完成端口,其中完成端口只是一种线程模型,需要使用CreateIoCompletionPort来调用,NET中的SocketAsyncEventArgs封装了IOCP的调用,调用SendSync是投递请求,《Windows网络编程》有详细的网络编程说明。
showjim 2014-02-15
  • 打赏
  • 举报
回复
引用 45 楼 zanfeng 的回复:
public bool ReceiveAsync(SocketAsyncEventArgs e) 这个不就是我说的??? SocketAsyncEventArgs
难道Socket还有其它的ReceiveAsync?好吧,既然你认为ReceiveAsync与SendAsync就是你说的SocketAsyncEventArgs,那我就贴BeginReceive,不过代码层次比较多
public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
{
    errorCode = this.DoBeginReceive(buffer, offset, size, socketFlags, asyncResult);
}
private SocketError DoBeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, OverlappedAsyncResult asyncResult)
{
    SocketError socketError = SocketError.SocketError;
    try
    {
        asyncResult.SetUnmanagedStructures(buffer, offset, size, null, false, ref this.Caches.ReceiveOverlappedCache);
        socketError = UnsafeNclNativeMethods.OSSOCK.WSARecv(this.m_Handle, ref asyncResult.m_SingleBuffer, 1, out num, ref socketFlags, asyncResult.OverlappedHandle, IntPtr.Zero);
    }
    finally
    {
        socketError = asyncResult.CheckAsyncCallOverlappedResult(socketError);
    }
}
internal void SetUnmanagedStructures(byte[] buffer, int offset, int size, SocketAddress socketAddress, bool pinSocketAddress, ref OverlappedCache overlappedCache)
{
    this.SetUnmanagedStructures(buffer, offset, size, socketAddress, pinSocketAddress);
}
internal void SetUnmanagedStructures(byte[] buffer, int offset, int size, SocketAddress socketAddress, bool pinSocketAddress)
{
    if (pinSocketAddress && (this.m_SocketAddress != null))
    {
        base.SetUnmanagedStructures(objectsToPin);
    }
    else
    {
        base.SetUnmanagedStructures(buffer);
    }
}
internal void SetUnmanagedStructures(object objectsToPin)
{
        Socket asyncObject = (Socket) base.AsyncObject;
        if (this.m_UseOverlappedIO)
        {
        }
        else
        {
            asyncObject.BindToCompletionPort();
        }
    }
}
http://msdn.microsoft.com/zh-cn/magazine/cc163356.aspx中提到BeginXXX的性能问题出在两个方面,一是IAsyncResult不可复用,二是调用路径比较深(程序繁琐)。
足球中国 2014-02-15
  • 打赏
  • 举报
回复
public bool ReceiveAsync(SocketAsyncEventArgs e) 这个不就是我说的??? SocketAsyncEventArgs
足球中国 2014-02-15
  • 打赏
  • 举报
回复
CreateIoCompletionPort 这个API才创建完成端口的。 GetQueuedCompletionStatus error = UnsafeNclNativeMethods.OSSOCK.WSARecv(this.m_Handle, ref e.m_WSABuffer, 1, out num, ref socketFlags, e.m_PtrNativeOverlapped, IntPtr.Zero);
引用 42 楼 sbwwkmyd 的回复:
[quote=引用 41 楼 zanfeng 的回复:]这个能说明是完成端口嘛??这个只是重叠IO。 WSASend WSARecv 这个是出现sp2中的。重叠IO只是完成端口的基础。
http://msdn.microsoft.com/zh-cn/magazine/cc163356.aspx这个链接明确提到
2.0 版本的 Socket 类使用 Windows I/O 完成端口来完成异步 I/O 操作。
当然不可否认.NET2.0中的IOCP装封有性能问题比较大。 在贴一段.NET2.0源代码
public bool ReceiveAsync(SocketAsyncEventArgs e)
{
    this.BindToCompletionPort();
    SocketFlags socketFlags = e.m_SocketFlags;
    try
    {
        if (e.m_Buffer != null)
        {
            error = UnsafeNclNativeMethods.OSSOCK.WSARecv(this.m_Handle, ref e.m_WSABuffer, 1, out num, ref socketFlags, e.m_PtrNativeOverlapped, IntPtr.Zero);
        }
        else
        {
            error = UnsafeNclNativeMethods.OSSOCK.WSARecv(this.m_Handle, e.m_WSABufferArray, e.m_WSABufferArray.Length, out num, ref socketFlags, e.m_PtrNativeOverlapped, IntPtr.Zero);
        }
    }
    catch (Exception exception)
    {
        e.Complete();
        throw exception;
    }
}
这段代码我删除了一些无关代码,其它子调用我也懒得贴了。[/quote]
t101706 2014-02-15
  • 打赏
  • 举报
回复
学习学习
showjim 2014-02-15
  • 打赏
  • 举报
回复
引用 41 楼 zanfeng 的回复:
这个能说明是完成端口嘛??这个只是重叠IO。 WSASend WSARecv 这个是出现sp2中的。重叠IO只是完成端口的基础。
http://msdn.microsoft.com/zh-cn/magazine/cc163356.aspx这个链接明确提到
2.0 版本的 Socket 类使用 Windows I/O 完成端口来完成异步 I/O 操作。
当然不可否认.NET2.0中的IOCP装封有性能问题比较大。 在贴一段.NET2.0源代码
public bool ReceiveAsync(SocketAsyncEventArgs e)
{
    this.BindToCompletionPort();
    SocketFlags socketFlags = e.m_SocketFlags;
    try
    {
        if (e.m_Buffer != null)
        {
            error = UnsafeNclNativeMethods.OSSOCK.WSARecv(this.m_Handle, ref e.m_WSABuffer, 1, out num, ref socketFlags, e.m_PtrNativeOverlapped, IntPtr.Zero);
        }
        else
        {
            error = UnsafeNclNativeMethods.OSSOCK.WSARecv(this.m_Handle, e.m_WSABufferArray, e.m_WSABufferArray.Length, out num, ref socketFlags, e.m_PtrNativeOverlapped, IntPtr.Zero);
        }
    }
    catch (Exception exception)
    {
        e.Complete();
        throw exception;
    }
}
这段代码我删除了一些无关代码,其它子调用我也懒得贴了。
足球中国 2014-02-15
  • 打赏
  • 举报
回复
引用 40 楼 sbwwkmyd 的回复:
[quote=引用 39 楼 zanfeng 的回复:]现在2.0 sp2里面确实有了。但2.0的sp2比3.5框架出来的还要晚。 BeginXXX与XXXAsync这个与完成端口一点关系也没有吧。 完成端口在 windows平台就是调用那几个API。 http://www.cnblogs.com/duzouzhe/archive/2010/01/13/1646514.html
.NET源码中BeginReceive与ReceiveAsync调用的是
[DllImport("ws2_32.dll", SetLastError=true)]
internal static extern SocketError WSARecv([In] SafeCloseSocket socketHandle, [In, Out] ref WSABuffer buffer, [In] int bufferCount, out int bytesTransferred, [In, Out] ref SocketFlags socketFlags, [In] IntPtr overlapped, [In] IntPtr completionRoutine);
BeginSend与SendAsync调用的是
[DllImport("ws2_32.dll", SetLastError=true)]
internal static extern SocketError WSASend([In] SafeCloseSocket socketHandle, [In] ref WSABuffer buffer, [In] int bufferCount, out int bytesTransferred, [In] SocketFlags socketFlags, [In] IntPtr overlapped, [In] IntPtr completionRoutine);
是包装了IOCP调用的[/quote] 这个能说明是完成端口嘛??这个只是重叠IO。 WSASend WSARecv 这个是出现sp2中的。重叠IO只是完成端口的基础。
showjim 2014-02-14
  • 打赏
  • 举报
回复
引用 39 楼 zanfeng 的回复:
现在2.0 sp2里面确实有了。但2.0的sp2比3.5框架出来的还要晚。 BeginXXX与XXXAsync这个与完成端口一点关系也没有吧。 完成端口在 windows平台就是调用那几个API。 http://www.cnblogs.com/duzouzhe/archive/2010/01/13/1646514.html
.NET源码中BeginReceive与ReceiveAsync调用的是
[DllImport("ws2_32.dll", SetLastError=true)]
internal static extern SocketError WSARecv([In] SafeCloseSocket socketHandle, [In, Out] ref WSABuffer buffer, [In] int bufferCount, out int bytesTransferred, [In, Out] ref SocketFlags socketFlags, [In] IntPtr overlapped, [In] IntPtr completionRoutine);
BeginSend与SendAsync调用的是
[DllImport("ws2_32.dll", SetLastError=true)]
internal static extern SocketError WSASend([In] SafeCloseSocket socketHandle, [In] ref WSABuffer buffer, [In] int bufferCount, out int bytesTransferred, [In] SocketFlags socketFlags, [In] IntPtr overlapped, [In] IntPtr completionRoutine);
是包装了IOCP调用的
足球中国 2014-02-14
  • 打赏
  • 举报
回复
引用 38 楼 sbwwkmyd 的回复:
而且IOCP并不非要SocketAsyncEventArgs,BeginXXX与XXXAsync是调用的是同一个函数处理的。
现在2.0 sp2里面确实有了。但2.0的sp2比3.5框架出来的还要晚。 BeginXXX与XXXAsync这个与完成端口一点关系也没有吧。 完成端口在 windows平台就是调用那几个API。 http://www.cnblogs.com/duzouzhe/archive/2010/01/13/1646514.html
showjim 2014-02-14
  • 打赏
  • 举报
回复
而且IOCP并不非要SocketAsyncEventArgs,BeginXXX与XXXAsync是调用的是同一个函数处理的。
showjim 2014-02-14
  • 打赏
  • 举报
回复
引用 35 楼 zanfeng 的回复:
[quote=引用 16 楼 sp1234 的回复:] [quote=引用 15 楼 wanyeye 的回复:] 不错呀 但是有个疑问,,C#里能实现IOCP,也就完成端口这个模式的么?
TcpListener框架类默认就是使用IOCP的。也就是说,人家都已经已经封装好了10年了,你还不认识。[/quote] 这可是纯粹胡扯。刚查看了一下tcplistener的代码。里面并没有使用iocp。 并且tcplistener 推出是在.net 刚推出就已经有了。 3.5框架发布时,才加了iocp。也就是SocketAsyncEventArgs[/quote].NET2.0中有SocketAsyncEventArgs啊,TcpListener不论什么AcceptXXX都可以得到Socket,有了Socket不就能IOCP了?
加载更多回复(32)
最近有项目要做一个高性能网络服务器,去网络上搜到到的都是C++版本而且是英文或者简单的DEMO,所以自己动手写了C# 的DEMO。 网络上只写接收到的数据,没有说怎么处理缓冲区数据,本DEMO简单的介绍如何处理接收到的数据。简单易用,希望对大家有用. 1、在C#中,不用去面对完成端口的操作系统内核对象,Microsoft已经为我们提供了SocketAsyncEventArgs类,它封装了IOCP的使用。请参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-1。 2、我的SocketAsyncEventArgsPool类使用List对象来存储对客户端来通信的SocketAsyncEventArgs对象,它相当于直接使用内核对象时的IoContext。我这样设计比用堆栈来实现的好处理是,我可以在SocketAsyncEventArgsPool池中找到任何一个与服务器连接的客户,主动向它发信息。而用堆栈来实现的话,要主动给客户发信息,则还要设计一个结构来存储已连接上服务器的客户。 3、对每一个客户端不管还发送还是接收,我使用同一个SocketAsyncEventArgs对象,对每一个客户端来说,通信是同步进行的,也就是说服务器高度保证同一个客户连接上要么在投递发送请求,并等待;或者是在投递接收请求,等待中。本例只做echo服务器,还未考虑由服务器主动向客户发送信息。 4、SocketAsyncEventArgs的UserToken被直接设定为被接受的客户端Socket。 5、没有使用BufferManager 类,因为我在初始化时给每一个SocketAsyncEventArgsPool中的对象分配一个缓冲区,发送时使用Arrary.Copy来进行字符拷贝,不去改变缓冲区的位置,只改变使用的长度,因此在下次投递接收请求时恢复缓冲区长度就可以了!如果要主动给客户发信息的话,可以new一个SocketAsyncEventArgs对象,或者在初始化中建立几个来专门用于主动发送信息,因为这种需求一般是进行信息群发,建立一个对象可以用于很多次信息发送,总体来看,这种花销不大,还减去了字符拷贝和消耗。 6、测试结果:(在我的笔记本上时行的,我的本本是T420 I7 8G内存) 100客户 100,000(十万次)不间断的发送接收数据(发送和接收之间没有Sleep,就一个一循环,不断的发送与接收) 耗时3004.6325 秒完成 总共 10,000,000 一千万次访问 平均每分完成 199,691.6 次发送与接收 平均每秒完成 3,328.2 次发送与接收 整个运行过程中,内存消耗在开始两三分种后就保持稳定不再增涨。 看了一下对每个客户端的延迟最多不超过2秒。

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧