在c#多线程使用IOCP(完成端口)的简单示例

详解多线程高并发IOCP开发模式(含示例说明) IOCP(I/O Completion Port,输入/输出完成端口)是Windows操作系统中用于高效处理异步I/O操作的一种编程模型,特别适用于处理大量并发I/O操作的场景,如网络通信和文件I/O等。IOCP是一个强大的工具,能够让程序在需要处理大量并发I/O操作时,保持高效、稳定的性能。 阅读详情

在c#使用IOCP(完成端口)的简单示例
上次给大家发了利用winsock原生的api来做一个同步的socket服务器的例子,大致上只是贴了一些代码,相信大家这么冰雪聪明,已经研究的差不多了。因为winsock的api使用在msdn或者google上都能很方便的查到,所以我没太多罗嗦代码的原理。但是c#进行平台调用方面是有一些经验的,单靠google和msdn及社区的力量有时候不容易得到答案。这次给大家演示一下利用IOCP的在线程间传递数据的例子,顺便打算讲一些细节和注意的地方。

概述:这里主要使用IOCP的三个 API,CreateIoCompletionPort,PostQueuedCompletionStatus,GetQueuedCompletionStatus,第一个是用来创建一个完成端口对象,第二个是向一个端口发送数据,第三个是接受数据,基本上用着三个函数,就可以写一个使用IOCP的简单示例。

其中完成端口一个内核对象,所以创建的时候会耗费性能,CPU得切换到内核模式,而且一旦创建了内核对象,我们都要记着要不用的时候显式的释放它的句柄,释放非托管资源的最佳实践肯定是使用 Dispose模式,这个博客园有人讲过N次了。而一般要获取一个内核对象的引用,最好用SafeHandle来引用它,这个类可以帮你管理引用计数,而且用它引用内核对象,代码更健壮,如果用指针引用内核对象,在创建成功内核对象并复制给指针这个时间段,如果抛了 ThreadAbortException,这个内核对象就泄漏了,而用SafeHandle去应用内核对象就不会在赋值的时候发生 ThreadAbortException。另外SafeHandle类继承自CriticalFinalizerObject类,并实现了 IDispose接口,CLR对CriticalFinalizerObject及其子类有特殊照顾,比如说在编译的时候优先编译,在调用非 CriticalFinalizerObject类的Finalize方法后再调用CriticalFinalizerObject类的Finalize 类的Finalize方法等。在win32里,一般一个句柄是-1或者0的时候表示这个句柄是无效的,所以.net有一个SafeHandle的派生类 SafeHandleZeroOrMinusOneIsInvalid ,但是这个类是一个抽象类,你要引用自己使用的内核对象或者非托管对象,要从这个类派生一个类并重写Relseas方法。另外在.net框架里它有两个实现几乎一模一样的子类,一个是SafeFileHandle一个是SafeWaitHandle,前者表示文件句柄,后者表示等待句柄,我们这里为了方便就直接用SafeFileHandle来引用完成端口对象了。

CreateIoCompletionPort函数的原型如下
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern SafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey, uint NumberOfConcurrentThreads);

FileHandle参数表示要绑定在完成端口上的句柄,比如说一个已经accept的socket句柄。

ExistingCompletionPort 参数表示一个已有的完成端口句柄,第一次创建完成端口的时候显然随便传个值就行,所以这个参数直接定义成IntPtr类型了。当你创建了工作线程来为 I/O请求服务的时候,才会把句柄和完成端口关联在一起,而之前第一次创建完成端口的时候这个参数传一个zero指针就O了,而FileHandle参数传一个-1的指针就行了。

CompletionKey是完成键的意思,它可以是任意想传递给工作线程的数据,学名叫做单句柄数据,就是说跟随FileHandle参数走的一些状态数据,一般在socket的iocp 程序里是把socket传进去,以便在工作线程里拿到这个socket句柄,在收到异步操作完成的通知及处理后继续进行下一个异步操作的投递,如发送和接受数据等。

NumberOfConcurrentThreads 参数表示在一个完成端口上同时允许执行的最大线程数量。如果传0,就是说你有几个CPU,就是允许最大有几个线程,这也是最理想情况,因为一个CPU一个线程可以防止线程上下文切换。关于这个值要和创建工作线程的数量的关系,大家要理解清楚,不一定CPU有多少个,你的工作线程就创建多少个。因为你的工作线程有时候会阻塞或者等待,而如果你正好创建了CPU个数个工作线程,有一个等待的话,因为你分配了同时最多有CPU个数多个最大IOCP线程,这时候就不能效率最大化了。所以一般工作线程创建的要比CPU个数多一些,除非你保证你的工作线程不会阻塞。

PostQueuedCompletionStatus函数原型如下
 [DllImport("Kernel32", CharSet = CharSet.Auto)]
    private static extern bool PostQueuedCompletionStatus(SafeFileHandle CompletionPort, uint dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped);
该方法用于给完成端口投递自定义信息,一般情况下如果把某个句柄和完成端口绑定后,当有数据收发操作完成时会自动同时工作线程,工作线程里的 GetQueuedCompletionStatus就不会阻塞,而继续往下走,来进行接收到IO操作完成通知的流程。而有时候我们需要手工向工作者线程投递一些消息,比如说我们主线程知道所有的socket句柄都关闭了,工作线程可以退出了,我们就可以给工作线程发一个自定义数据,工作线程收到后判断是否是退出指令,然后退出。

CompletionPort参数表示向哪个完成端口对象投递信息,在这个完成端口上等待消息的工作线程就会收到消息了。
dwNumberOfBytesTransferred表示你投递的数据有多大,我们一般投递的是一个对象的指针,在32位系统里,int指针就是4个字节了,直接写4就O了,要不就用sizeof你传的数据,如sizeof(IntPtr)。

dwCompletionKey同CreateIoCompletionPort的解释,是单句柄数据,本示例用不到,不细说,直接用IntPtr.Zero填充了事。

lpOverlapped参数,本意是一个win32的overlapped结构的指针,本示例中不用,所以不详细讲。它叫单IO数据,是相对单据并拘束CompletionKey来讲的,前者是一个句柄的每次IO操作的上下文,比如单词IO操作的数据、操作类型等,后者是整个句柄的上下文。但这里我们表示你要投递的数据,可以是任何类型的数据(谁让它是个指针呢,所以传啥都行),值得注意的一点就是,这个数据传递到工作线程的时候,中间这个数据走的是非托管代码。所以不能直接传一个引用进去,这里要使用到GCHandle类。先大致介绍一下这个类吧。它有个静态方法Alloc来给把一个对象在GC句柄表里注册,GC句柄表示CLR为没个应用程序域提供的一个表,它允许你来监视和管理对象的生命周期,你可以往里加一个对象的引用,也可以从里面移除一个对象,往里加对象的时候,还可以指定一个标记来表示我们希望如何监视和控制这个对象。而加入一个条目的操作就是GCHandle的Alloc对象,它有两个参数,第一个参数是对象,第二参数是 GCHandleType类型的枚举,第二个参数表示我们如何来监视和控制这个对象的生命周期。当这个参数是GCHandleType.Normal时,表示我们告诉垃圾收集器,及时托管代码里没有该对象的根,也不要回收该对象,但垃圾收集器可以移动它,一般我们向非托管代码传递一个对象,而又从非托管代码传递回来的时候用这个类型非常好,它不会让垃圾收集器在非托管代码返回托管代码的时候回收掉该对象,还不怎么影响GC的性能,因为GC还可以移动它。 dwCompletionKey就是我们在托管-非托管-托管之间传递的一个很典型的场景。所以这里用它,另外还有 GCHandleType.Pinned,它和GCHandleType.Normal不同的一点就是GC除了在没有根的时候不能回收这个对象外,还不能移动它,应用场景是给非托管代码传递一个byte[]的buffer,让托管代码去填充,如果用GCHandleType.Normal有可能在非托管代码返回托管代码的时候写错内存位置,因为有可能GC移动了这个对象的内存地址。关于根、GC原理,大家可以参考相关资料。另外在你的数据从非托管代码传递会托管代码后,要调用GCHandle的实例方法free来在GC句柄表里移除该对象,这时候你的托管代码还有个该对象的引用,也就是根,GC也不会给你回收的,当你用完了后,GC就给你回收了。GCHandle的Target属性用来访问GCHandle指向的对象。其它两个GCHandleType的成员是关于弱引用的,和本文关系不大,就不介绍了。

GetQueuedCompletionStatus原型如下
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  public static extern bool GetQueuedCompletionStatus(SafeFileHandle CompletionPort,
      out uint lpNumberOfBytesTransferred, out IntPtr lpCompletionKey,
      out IntPtr lpOverlapped, uint dwMilliseconds);
前几个参数和PostQueuedCompletionStatus差不多,
CompletionPort表示在哪个完成端口上等待PostQueuedCompletionStatus发来的消息,或者IO操作完成的通知,

lpNumberOfBytesTransferred 表示收到数据的大小,这个大小不是说CompletionKey的大小,而是在单次I/O操作完成后(WSASend或者WSAReceve),实际传输的字节数,我在这里理解的不是很透彻,我觉得如果是接受PostQueuedCompletionStatus的消息的话,应该是收到 lpOverlapped的大小,因为它才是单IO数据嘛。

lpCompletionKey用来接收单据并数据,我们没传递啥,后来也没用,在socket程序里,一般接socket句柄。

lpOverlapped用来接收单IO数据,或者我们的自定义消息。

dwMilliseconds表示等待一个自定义消息或者IO完成通知消息在完成端口上出现的时间,传递INIFINITE(0xffffffff)表示无限等待下去。

好了,API大概介绍这么多,下面介绍代码
1、主线程创建一个完成端口对象,不和任何句柄绑定,前几个参数都写0,NumberOfConcurrentThreads参数我们写1,因为我们的示例就一个工作线程。
2、创建一个工作线程,把第一步创建的完成端口传进去
3、创建两个单IO数据,分别发投递给第一步创建的完成端口
4、在工作线程里执行一个死循环,循环在传递进来的完成端口上等待消息,没有消息的时候GetQueuedCompletionStatus处于休息状态,有消息来的时候把指针转换成对象,然后输出
5、如果收到退出指令,就退出循环,从而结束工作者线程。
下面是完整代码,需要打开不安全代码的编译选项。

using System;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Win32.SafeHandles;

[StructLayout(LayoutKind.Sequential)]
class PER_IO_DATA
{
    public string Data;
}

public class IOCPApiTest
{
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern SafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey, uint NumberOfConcurrentThreads);
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool GetQueuedCompletionStatus(SafeFileHandle CompletionPort,
        out uint lpNumberOfBytesTransferred, out IntPtr lpCompletionKey,
        out IntPtr lpOverlapped, uint dwMilliseconds);
    [DllImport("Kernel32", CharSet = CharSet.Auto)]
    private static extern bool PostQueuedCompletionStatus(SafeFileHandle CompletionPort, uint dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped);

    public static unsafe void TestIOCPApi()
    {
        var CompletionPort = CreateIoCompletionPort(new IntPtr(-1), IntPtr.Zero, IntPtr.Zero, 1);
        if(CompletionPort.IsInvalid)
        {
            Console.WriteLine("CreateIoCompletionPort 出错:{0}",Marshal.GetLastWin32Error());
        }
        var thread = new Thread(ThreadProc);
        thread.Start(CompletionPort);

        var PerIOData = new PER_IO_DATA() ;
        var gch = GCHandle.Alloc(PerIOData);
        PerIOData.Data = "hi,我是蛙蛙王子,你是谁?";
        Console.WriteLine("{0}-主线程发送数据",Thread.CurrentThread.GetHashCode());
        PostQueuedCompletionStatus(CompletionPort, (uint)sizeof(IntPtr), IntPtr.Zero, (IntPtr)gch);

        var PerIOData2 = new PER_IO_DATA();
        var gch2 = GCHandle.Alloc(PerIOData2);
        PerIOData2.Data = "关闭工作线程吧";
        Console.WriteLine("{0}-主线程发送数据", Thread.CurrentThread.GetHashCode());
        PostQueuedCompletionStatus(CompletionPort, 4, IntPtr.Zero, (IntPtr)gch2);
        Console.WriteLine("主线程执行完毕");
        Console.ReadKey();
    }
    static void ThreadProc(object CompletionPortID)
    {
        var CompletionPort = (SafeFileHandle)CompletionPortID;

        while (true)
        {
            uint BytesTransferred;
            IntPtr PerHandleData;
            IntPtr lpOverlapped;
            Console.WriteLine("{0}-工作线程准备接受数据",Thread.CurrentThread.GetHashCode());
            GetQueuedCompletionStatus(CompletionPort, out BytesTransferred,
                                      out PerHandleData, out lpOverlapped, 0xffffffff);
            if(BytesTransferred <= 0)
                continue;
            GCHandle gch = GCHandle.FromIntPtr(lpOverlapped);
            var per_HANDLE_DATA = (PER_IO_DATA)gch.Target;
            Console.WriteLine("{0}-工作线程收到数据:{1}", Thread.CurrentThread.GetHashCode(), per_HANDLE_DATA.Data);
            gch.Free();
            if (per_HANDLE_DATA.Data != "关闭工作线程吧") continue;
            Console.WriteLine("收到退出指令,正在退出");
            CompletionPort.Dispose();
            break;
        }
    }

    public static int Main(String[] args)
    {
        TestIOCPApi();
        return 0;
    }
}
posted on 2008-07-12 19:46 蛙蛙池塘 阅读(2678) 评论(19)  编辑 收藏 网摘


评论:
#1楼 [楼主]  蛙蛙池塘       Posted @ 2008-07-12 19:53
好想把文字的灰底儿去了,可是不会去,我复制上来就成这了,汗。   回复  引用  查看   

#2楼 [楼主]  蛙蛙池塘       Posted @ 2008-07-12 19:55
汗,发现一个历史上的今天的帖子
蛙蛙推荐:偶心目中的编程高手,大家也推荐一下
http://www.cnblogs.com/onlytiancai/archive/2005/07/12/191287.html   回复  引用  查看   

#3楼   henry       Posted @ 2008-07-12 20:30
c#的socket异步不是已经具备IOCP功能了吗?   回复  引用  查看   

#4楼 [楼主]  蛙蛙池塘       Posted @ 2008-07-12 21:02
@henry
异步socket是封装了iocp,呵呵。.net 2.0的有些问题,3.0的异步提高了不少据说,不过本文主要是介绍底层原理及让有兴趣的朋友自己重新实现IOCP。
另外看你的博客对emit和反射研很了解。   回复  引用  查看   

#5楼   airwolf2026       Posted @ 2008-07-12 22:21
c#的异步socket真的封装了IOCP了?俺对c#网络编程了解比较少.那是不是说
c#里面的一些异步操作.如果系统有提供IOCP功能的话,是不是也封装了?感觉自己问的这个问题好傻?   回复  引用  查看   

#6楼 [楼主]  蛙蛙池塘       Posted @ 2008-07-12 22:26
@airwolf2026
socket的异步操作是用的IOCP实现的,一般来说IO密集型的异步操作.NET都是用IOCP实现的,计算密集型的异步操作都是用ThreadPool实现的,当然只是一般来说。   回复  引用  查看   

#7楼   airwolf2026       Posted @ 2008-07-12 22:28
呵呵.多谢楼主.   回复  引用  查看   

#8楼 [楼主]  蛙蛙池塘       Posted @ 2008-07-12 22:43
@airwolf2026
:)   回复  引用  查看   

#9楼   黎叔 [未注册用户] Posted @ 2008-07-13 00:10
var只能让代码更难读懂。

有必要全篇使用么   回复  引用   

#10楼   黎叔 [未注册用户] Posted @ 2008-07-13 00:14
不过感谢楼主的好文章   回复  引用   

#11楼   Anders Liu       Posted @ 2008-07-13 02:24
历史上的今天:
2006-07-12 数据库范式俗话
http://www.cnblogs.com/AndersLiu/archive/2006/07/12/448595.html   回复  引用  查看   

#12楼 [楼主]  蛙蛙池塘       Posted @ 2008-07-13 23:11
哥们折腾一天IOCP也没写出个例子来,汗了。
Win32的WSAReceive函数,如下
WSARecv([In] SafeSocketHandle socketHandle, [In, Out] ref WSABuffer buffer, [In] int bufferCount, out int bytesTransferred, [In, Out] ref SocketFlags socketFlags, [In] IntPtr overlapped, [In] IntPtr completionRoutine);

倒数第二个参数只能传递overlapped对象指针,我想传递一个自定义对象,不知道咋办。
目前只能用以下方式调用
GCHandle gcHandle = GCHandle.Alloc(PerIoData.Overlapped, GCHandleType.Pinned);
WSARecv(Accept, ref PerIoData.DataBuf ,1, out RecvBytes, ref Flags, gcHandle.AddrOfPinnedObject(), IntPtr.Zero);
可是,我想把整个PerIoData传进去,而不是光一个overlapped对象,PerIoData类型如下。
[StructLayout(LayoutKind.Sequential)]
class PER_IO_OPERATION_DATA
{
public unsafe NativeOverlapped Overlapped;
public WSABuffer DataBuf;
public readonly byte[] Buffer = new byte[DATA_BUFSIZE];
public uint BytesSEND;
public uint BytesRECV;
}

在c++里很简单,只要传递PerIoData对象的首地址,其实也就是类型的第一个成员Overlapped的地址进去就行了,然后就可以在工作线程里用GetQueuedCompletionStatus取到了,代码大约如下
执行异步IO操作
WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,&(PerIoData->Overlapped), NULL);
接受异步IO完成通知
GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE)
灰常的简单,直接就取出了PerHandleData数据。

c#就不知道咋搞了,我用Marshal.StructureToPtr,Marshal.AllocHGlobal,Marshal.Copy折腾半天也没弄成。而直接用以下代码
GCHandle gcHandle2 = GCHandle.Alloc(PerIoData);
PerIoData.Overlapped = new NativeOverlapped();
WSARecv(Accept, ref PerIoData.DataBuf ,1, out RecvBytes, ref Flags, GCHandle.ToIntPtr(gcHandle2), IntPtr.Zero)
会说无效句柄的。
我用WSARecv(Accept, ref PerIoData.DataBuf ,1, out RecvBytes, ref Flags, new IntPtr(GCHandle.ToIntPtr(gcHandle2).ToInt32()+8), IntPtr.Zero)倒是可以,然后再工作线程里用以下语句接受自定义对象
gcHandle_per_io_data = GCHandle.FromIntPtr(new IntPtr(intptr_per_io_data.ToInt32()-8));
PER_IO_OPERATION_DATA PerIoData = (PER_IO_OPERATION_DATA)gcHandle_per_io_data.Target;
确实是可以,但是当第二个请求进来之后,GCHandle.ToIntPtr(gcHandle2).ToInt32()+8就不是overlapped的地址了,我也不知道为啥,可能这只是一个巧合。

要怎么才能在WSARecv里向工作线程传递一个自定义信息呢,我除了传overlapped外,怎么也得把异步操作类型、缓存等传给工作线程吧。
  回复  引用  查看   

#13楼 [楼主]  蛙蛙池塘       Posted @ 2008-07-13 23:12
除了这个问题,别的基本都解决了。   回复  引用  查看   

#14楼   曲滨*銘龘鶽       Posted @ 2008-07-14 00:14
HANDLE WINAPI CreateIoCompletionPort(
__in HANDLE FileHandle,
__in HANDLE ExistingCompletionPort,
__in ULONG_PTR CompletionKey,
__in DWORD NumberOfConcurrentThreads
);
*********************************
__in ULONG_PTR CompletionKey,
这个参数本来是一个 4 字节的整数,也就是说可以放入一个win32
指针;
********************************

C++ 代码中是在第2处 CreateIoCompletionPort 调用处
把 PerHandleData 传递进去的的并不是
WSARecv

if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,
0) == NULL)
{
printf("CreateIoCompletionPort failed with error %d/n", GetLastError());
return;
}

接收

if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
{
printf("GetQueuedCompletionStatus failed with error %d/n", GetLastError());
return 0;
}


WSARecv 本身参数是有类型大小限定的
乱传递也没用的;
  回复  引用  查看   

#15楼 [楼主]  蛙蛙池塘       Posted @ 2008-07-14 09:37
@曲滨*銘龘鶽
这可咋办,这可咋整,这可咋好,闪灵给搭救一下残喘吧。难道在c#里知道一个类的成员的内存地址,不能知道它所在类的内存地址吗?   回复  引用  查看   

#16楼   曲滨*銘龘鶽       Posted @ 2008-07-14 10:23
--引用--------------------------------------------------
蛙蛙池塘:

@曲滨*銘龘鶽这可咋办,这可咋整,这可咋好,闪灵给搭救一下残喘吧。难道在c#里知道一个类的成员的内存地址,不能知道它所在类的内存地址吗?
--------------------------------------------------------

.net 的 对象好像和 C++ 不同不是一片连续的内存地址所以和C++ API 交互只能用人家能识别的东西值类型、结构体等;

类不加 [StructLayout(LayoutKind.Sequential)] 标签的都不行好像;   回复  引用  查看   

#17楼   T.t.T!Ck.¢#       Posted @ 2008-07-14 10:35
笨蛙蛙
o(∩_∩)o   回复  引用  查看   

#18楼   overred       Posted @ 2009-01-05 16:35
...
.net 里的异步IO跟非阻塞 IOCP还是有区别的吧

比如在县城切换以及阻塞上
我感觉异步IO就是系统做完然后通知你(不能精确)
非阻塞的IOCP则是自己不停的看看我是可以做某个IO,只要是一个线程忙,在cpu上就不开其他的,如果阻塞则开启新线程
while (true)
{GetQueuedCompletionStatus。。。}

。。。

iocp在 vc里用得 多 。。。
蛙蛙今天没来上班 扣你工资。。。。   回复  引用  查看   

#19楼 [楼主]  蛙蛙池塘       Posted @ 2009-01-05 20:30
@overred
我明天不去公司,后天也不去,大后天也不去,大大后天也不去。。。。   回复  引用  查看 

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/zfrong/archive/2009/03/13/3987886.aspx

构建基于 IOCP 的高效 C# Socket 服务器 在计算机网络中,Socket是用于实现网络通信的端点,它提供了不同主机上运行的应用程序进行数据交换的接口。C#中的Socket编程允许开发者通过编程方式控制网络通信,从简单的HTTP请求到复杂的自定义协议通信。Socket编程模型主要涉及以下几个方面:Socket类型:常用的有流式Socket()和数据报Socket(地址族:定义地址格式和协议族,如IPv4()和IPv6(协议:用于Socket通信的具体协议,例如TCP()和UDP( 阅读详情

相关推荐

构建C#高性能并发Socket服务器:利用IO完成端口

C#网络通信通常依赖于System.Net和System.Net.Sockets命名空间下的类库。开发者可以利用这些类库提供的功能,编写客户端和服务器端应用程序,实现数据的发送和接收。然而,为了保证通信的效率和响应速度,我们需要对传统的网络通信方式加以优化。I/O完成端口(IOCP)是Windows平台下一种高效的并发I/O处理机制,它是通过将Socket注册到完成端口上,来异步处理大量的I/O操作。IOCP特性可以概括为以下几点:异步I/O操作。

weixin_36064575的博客 788

C# IOCP SocketAsyncEventArgs 使用流程

如果在 “4” 处采用异步,则初始化后将同步接收代码交由异步处理,后续操作则不会引发错误。从上图不难看出:如果极端情况下,Accept后的Socket会一直或较长一段时间内处于同步状态,此时将会占用主线程来执行相关数据处理工作,会影响后续Socket的Accept。在Windows系统下,该异步方法采用IOCP来实现,在Linux方法下通过epoll来实现,C#原生Socket仅有Select能够支持简单的“并发”,如果想在Linux平台实现高效率,可以采用此方法以达到epoll的效率。

少年的博客 3063

C#实现高效IOCP TCP服务器教程

在深入探讨完成端口之前,了解端口的基本概念至关重要。计算机网络中的端口是一个逻辑概念,可以类比为现实生活中的码头,数据包通过端口进出计算机。每个端口都对应着一个服务或应用,并由一个端口号标识。完成端口(I/O Completion Port,简称IOCP)是一种高效的数据交换机制,主要用于Windows平台下实现高效的异步I/O操作。完成端口利用线程池的概念,通过操作系统内核直接调度I/O操作,减少了上下文切换的开销,能够处理大量并发连接,是实现高性能服务器的基石之一。

weixin_35390379的博客 863

C#-IOCP-SAEA使用(解决“现在已经正在使用此 SocketAsyncEventArgs“问题)

C#-IOCP-SAEA使用(解决"现在已经正在使用此 SocketAsyncEventArgs"问题) 解决思路:在server端以下socket通讯各异步过程中,规避使用相同的saea对象; 接受连接(_listenSocket.AcceptAsync(acceptSaea)), 接收数据(token.UserSocket.ReceiveAsync(recvSaea)), 发送数据(token.UserSocket.SendAsync(sendSaea))

1277

c#写的iocp服务端和客户端代码

用c#语言写的iocp网络服务,和客户端,有注释,程序可直接运行

C# IOCP完成端口模型(简单实用高效)

最近有项目要做一个高性能网络服务器,去网络上搜到到的都是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秒。

蛙蛙推荐:在c#使用IOCP(完成端口)的简单示例

蛙蛙推荐:在c#使用IOCP(完成端口)的简单示例上次给大家发了利用winsock原生的api来做一个同步的socket服务器的例子,大致上只是贴了一些代码,相信大家这么冰雪聪明,已经研究的差不多了。因为winsock的api使用在msdn或者google上都能很方便的查到,所以我没太多罗嗦代码的原理。但是c#进行平台调用方面是有一些经验的,单靠google和msdn及社区的力量有时候不

蛙蛙池塘 1043

C#完成端口(IOCP)简单实例源码

使用版本VS2013,使用结束符处理粘包问题!(源码自己可以优化改进)!

C#实现一个简单的IOCP模式的服务端监听框架

本类直接继承了SocketAsyncEventArgs,重写其内部的OnCompleted方法来处理客户端连接。 只是一个基础的监听框架,具体的业务逻辑需要在子类实现NewClient方法 后续其 using System; using System.Net; using System.Net.Sockets; using System.Threading; namespace Socks5.Server { /// <summary> /// 直接继承SocketAsyncE

艾恩 2311

合宙Air780EP速连OneNET:AT+MQTT应用指南

本文详细讲解了合宙Air780EP使用AT指令通过MQTT接入OneNET的过程。

2401_82812461的博客 1388

实战演练 | ABAQUS显式动力学分析在金属切削仿真中的应用

本文详细介绍了如何利用ABAQUS显式动力学分析进行金属切削过程的二维仿真。通过从零开始的完整流程,包括几何建模、材料本构与失效定义、网格划分、接触设置、边界条件施加,到结果后处理与切削力提取,为工程师和研究人员提供了实用的仿真方法,以优化刀具设计和加工工艺。

purple的博客 929

c# IOCP模型与select模型

一、 IOCP IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型。它是应用程序使用线程池处理异步I/O请求的一种机制。在处理多个并发的异步I/O请求时,以往的模型都是在接收请求是创建一个线程来应答请求。这样就有很多的线程并行地运行在系统中。而这些线程都是可运行的,Windows内核花费大量的时间在进行线程的上下文切换,并没有多少时间花在线程运行上。再加上创建新线程的开销比较大,所以造成了效率的低下。 Windows Sockets应用程序在调用WSARecv()函

zyh_1988的博客 1336

Ferry工单平台部署

ferry工单平台部署

运维求生之路 1897

Managed I/O Completion Ports (IOCP)

Index Introduction - Native Win32 IOCP Introduction - Managed IOCP Using Managed IOCP in .NET applications Inside Managed IOCP Points of interest History Software Usage

优秀技术文章收录 2207

京东商品数据爬取实战:用Selenium+代理IP绕过最新反爬机制(附完整代码)

本文深入解析京东商品数据采集技术,详细介绍了如何利用Selenium与智能反反爬策略绕过京东最新的反爬机制。通过实战案例和完整代码,帮助开发者掌握高效采集京东商品数据的方法,包括动态参数加密、行为验证识别及分布式采集架构设计等关键技术。

cc789的博客 1058

基于YOLOv8的视频监控违规停车智能识别系统与应用研究

通过这个毕设,你已经从数据准备、模型训练到界面开发,完整走了一遍深度学习项目的流程。把多个停车场的监控数据整合起来,做一个区域级的停车管理平台;加入车牌识别功能,直接定位到违规车辆的车主;试试把YOLO模型再优化一下,让它在手机端也能运行,实现移动巡检。相信我,按照这个教程一步步做,你的毕设不仅能顺利完成,还能做出亮点,让答辩老师眼前一亮。要是过程中遇到什么问题,随时可以再琢磨琢磨,或者找我聊聊,咱们一起把这个毕设做成精品。

LXY_LOVE36的博客 117

完成端口IOCP详解

转载自:http://www.cnblogs.com/talenth/p/7068392.html 以下为转载的内容: 修改自:http://blog.csdn.net/piggyxp/article/details/6922277 ps: 原作者很厉害了, 把一个iocp模型讲解的这么形象,不过在实践过程中发现一些细节说得有问题, 在文中标红指出, 另外删掉了原文中一些我认为有些多...

hnzwx888的专栏 383

一文看懂EMMC原理及工作过程

CMD:用于设备初始化和命令传输的双向通道, CMD信号有2种工作模式:1#:漏极开路--->用于初始化模式2#:推拉--->用于快速命令传输其中命令从eMMC主机控制器发送到eMMC设备,响应从设备发送到主机。DAT [7:0]:双向数据信号,模式包括:1(DAT0);默认情况下,通电或重置后,只有DAT0用于数据传输。DAT信号在推挽模式下工作,eMMC设备包括数据线DAT1-DAT7的内部上拉。进入4位模式后,设备立即断开线路DAT1、DAT2和DAT3的内部上拉;

Airer_00的博客 2029

经典代码IOCP(完成端口)的C#.Net实现----解读

感谢原作者 经典代码IOCP(完成端口)的C#.Net实现 重复造轮子的意义在于深刻的理解和学习--- main.cs: /*using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace c2iocp { class Program {

legan的专栏 8593

用 SemanticObject 把字段变成可点击链接:Fiori Elements Navigation Using a Link 的工程化实践

SAP Fiori elements 通过 Common.SemanticObject 注解可将字段转为可点击链接,实现应用间跳转。当源字段名与目标属性名不一致时,需使用 Common.SemanticObjectMapping 进行映射。该功能依赖 Fiori Launchpad 的目标映射配置,确保正确解析 semantic object + action 并传递参数。常见误区包括未配置目标映射或字段映射错误,需逐一检查语义标注、intent 解析和参数传递链路。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 272

Codex 实战 Skills:如何用 Skill 让 AI 模拟人工点击,自动完成复杂的前端表单测试

本文介绍如何构建基于Selenium WebDriver的AI Agent技能,实现自动化UI测试流程。主要内容包括: 环境搭建:配置无头Chrome浏览器环境,使用WebDriver Manager自动管理驱动 核心技能开发:通过ID、CSS Selector、XPath等策略定位元素,实现显式等待机制 交互模拟:使用ActionChains模拟鼠标悬停等复杂操作 稳定性优化:解决异步加载问题,确保测试可靠性 Skill封装:将功能模块化,使AI能通过自然语言指令操作Web界面 该方案突破了传统自动化测试

yueyeguzhuo的专栏 147

完成端口学习笔记(一):完成端口+控制台 实现文件拷贝

最近在整理手里一个项目的后台服务端归档程序,重新梳理了一下有关“完成端口”的知识,发现还是有很多模棱两可的地方,下面记录一下再次学习的点滴,博文中以一个简单的控制台文件拷贝程序来演示了“完成端口”的内部机制和使用方法,该篇博文还会有后续的补充章节,不知道什么时间会再补充^_^。

只要踏出一步,路就在前方——zssure 6004

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

经典代码IOCP(完成端口)的C#.Net实现(转)

using System;using System.Threading;  // Included for the Thread.Sleep callusing Continuum.Threading;namespace Sample{    //============================================    /// Sample clas

Zfrong繁荣的IT技术、IT项目、解决方案专栏Blog-上海 2416

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

IOCP完成端口模拟线程池

IO完成端口的使用一,样例介绍 一,样例介绍 生产者-消费者模式是一种非常常见的设计模式,它可以很好地模拟实际编程中的大部分使用场景,而IO完成端口也是用来实现多线程之间的通信,因此,本实例基于该模式完成。 首先,我们需要创建IO完成端口,最后一个参数为0,默认创建的IO完成端口数目就是处理器数目,同时,和要创建一定数目的消费者线程,来处理数据。 //创建1个IO完成端口(最后一个参数为0表示创建...

bajianxiaofendui的博客 9315

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

[收藏]IOCP Thread Pooling in C#

IOCP Thread Pooling in C#By William KennedyContinuum Technology CenterIntroduction When building server based applications in C#, it is important to have the ability to create thread pools.  Thread

软件开发资料汇总 4069
上一篇: Discuz论坛验证码破解
下一篇: 使用SharpPcap发送虚假构造的伪IP的UDP数据包
jackfirst86
博客等级 码龄18年 28粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值