在.Net中使用完成端口编写TCP服务器的问题

CodeProject-Jerry 2009-11-18 11:30:48
我在CodeProject上google了很久,发现没人在.Net中使用过 AcceptEx 这个API


BOOL AcceptEx(
__in SOCKET sListenSocket,
__in SOCKET sAcceptSocket,
__in PVOID lpOutputBuffer,
__in DWORD dwReceiveDataLength,
__in DWORD dwLocalAddressLength,
__in DWORD dwRemoteAddressLength,
__out LPDWORD lpdwBytesReceived,
__in LPOVERLAPPED lpOverlapped
);

sListenSocket是已经与IOCP关联的服务器listening Socket
sListenSocket是提前创建的与客户端通信的Socket
lpOutputBuffer是输出缓存区,用于接收地址信息等

最后一个参数是重叠结构体,在.Net中对应的是 NativeOverlapped结构, 通过Overlapped.Pack方法可以得到 NativeOverlapped指针:

[CLSCompliantAttribute(false)]
[ComVisibleAttribute(false)]
public NativeOverlapped* Pack(
IOCompletionCallback iocb,
Object userData
)

对于参数 userData的解释是:An object or array of objects representing the input or output buffer for the operation. Each object represents a buffer, for example an array of bytes.

那么调用这个函数的时候,如果是在C里面,一般会使用扩展的Overlapped结构传递到最后一个参数lpOverlapped,用于存储Per Socket Data. 而.Net中的Overlapped.Pack方法是如何传递这个数据的呢?

IOCompletionCallback的原型是:

[ComVisibleAttribute(true)]
[CLSCompliantAttribute(false)]
public delegate void IOCompletionCallback(
uint errorCode,
uint numBytes,
NativeOverlapped* pOVERLAP
)

是不是我将Socket关联到了 IOCP后,如果有 事件边缘触发此回调就能够被自动调用?就算被调用了,我如何来获得和此Socket的关联数据?

实在想不通.Net中如何调用这个API,难道是使用Marshal.AllocHGlobal来完全地复制C的方式?传递内存地址?

在Native Win32中,PE文件的Optional Header规定了一个进程能够使用的最大堆空间、栈空间,保留堆空间、栈空间。
如果我使用Marshal.AllocHGlobal是不是也会受此影响?

对于.Net对象,可以使用GCHandle.Alloc做到对象被Pinned,防止GC对.Net对象收集整理导致地址的改变,

public static GCHandle Alloc(
Object value,
GCHandleType type
)

请问这个是线程安全的么?MSDN说得好模糊~~

各位高手做IOCP TCP server的时候是如何来做的?是完全复制Win32的做法?
...全文
265 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
CodeProject-Jerry 2009-11-24
  • 打赏
  • 举报
回复
http://www.intel.com/cd/ids/developer/apac/zho/recent/289648.htm?page=1

对于高负荷应用,肯定是要指定CPU,针对CPU优化的

使用2个不同的线程池来分别来处理 网络数据包的发送接收 以及 消息的处理.
这样可以避免繁重的业务处理导致网络数据包接收的阻塞.

根据服务器CPU情况创建线程. 服务器是2*4核心. 即双CPU, 每CPU上有4个核心.
在逻辑上就有8个处理单元.

在第1个CPU上的每个核心上创建x个线程用于发送和接收.
即: 发送接收线程池有线程数 x*4个,位于第1个CPU上.

在第2个CPU上的每个核心上创建y个线程,用于业务处理
即: 业务处理线程池有线程数 y*4个,位于第2个CPU上

具体的x,y应该按照实际的系统设置.设置的原则是:
1. 尽量小的线程上下文切换开销
2. 尽量高的CPU利用率(注意,是利用率,不是占用率)
一般来说,y>x


BOOL WINAPI SetProcessAffinityMask(
__in HANDLE hProcess,
__in DWORD_PTR dwProcessAffinityMask
);
// 此API用于设置进程的CPU亲缘属性,第2个参数是"位或"表示. 对于2*4核系统,则设置位0xFF

DWORD_PTR WINAPI SetThreadAffinityMask(
__in HANDLE hThread,
__in DWORD_PTR dwThreadAffinityMask
);
// 此API用于设置线程的CPU亲缘属性,第2个参数是"位或"表示. 需要注意的是,dwThreadAffinityMask必须是dwProcessAffinityMask的子集

DWORD WINAPI SetThreadIdealProcessor(
__in HANDLE hThread,
__in DWORD dwIdealProcessor
);
// 此API用于设置线程的首选CPU,操作系统在调度线程时优先考虑首选核心, 第2个参数是以0为基数的处理器ID




上述3个API都可以用来设置线程的执行单元是哪个. 一般来说,线程调度是由操作系统负责.人为的控制有时候反而会降低效率.但针对高负荷的线程处理,完全可以指定独立的CPU来优化.
比如,设定dwThreadAffinityMask=0xF,表示此线程在1-4核上执行,具体是哪个核还是由操作系统调度.这样可以将不同用途的线程分配到不同的CPU上,因为每个CPU有自己独立的L2 Cache,这样做可以避免不同类型线程在不同CPU之间切换带来的损失.

上面所说的这些都只是理论,到实际的系统中,必须经过反复的性能对比试验来确定最佳方案
CodeProject-Jerry 2009-11-24
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 cgabriel 的回复:]
引用 5 楼 wangjia184 的回复:
引用 3 楼 cgabriel 的回复:
Microsoft 已经把网络用的完成端口完整地封装在 Socket 里面,不明白咋的那么多人还去重写的,是觉得自己的写的代码比 Microsoft 的漂亮,还是高效?


可定制性不行啊, 如果是2*4核处理器,我想指定2个接受者、发送者线程工作在前面2个核上,如果我想使用自己的内存管理,这些都没有提供啊

有没有人真正比对过 P/Invoke 和 Socket的 AIO的性能差异呢???


内存跟 CPU 你都要干预?还不如直接用 C++ 搞掂,然后把开放接口,简单百倍。
[/Quote]
我曾经设计的一个服务器就是用 Native C++ 和 Managed C# 混合写的,并发可以达到5W,当然,吞吐率下降得很厉害。[url=http://www.cppblog.com/wangjia184/archive/2008/05/21/50612.html]

我真的很想知道有没有人真正比对过 P/Invoke 和 Socket的 AIO 的性能差异?
并发 - 吞吐率 曲线是怎样的? 如果我上2W的并发能够保证多少吞吐率?
CGabriel 2009-11-22
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 zhujiechang 的回复:]
引用 3 楼 cgabriel 的回复:
Microsoft 已经把网络用的完成端口完整地封装在 Socket 里面,不明白咋的那么多人还去重写的,是觉得自己的写的代码比 Microsoft 的漂亮,还是高效?

说实在的.net自带的socket效率在很多时候不行,可能你没有觉得,当然主要看你应用环境,如果你只是做一个普通的网络连接,或者web service之类那是够用了,如果是一个大型的在线连接,例如网游服务端,大型对局室等等,远远不够。
P/Invoke看你怎么实现,也有相当的性能损耗,但比.net自带的好,为什么好,都一样的函数?因为.net各类检查太多,不是函数本身问题,而是为什么执行这个函数所附带的其他检查代价
[/Quote]

是吗,我们做的服务端,在最普通的双核至强上面都可以挂上千把个活跃连接,而且还很顺畅。满足通常的要求应该没有问题了。

至于 P/Invoke 的实现,我认为九成程序员的代码都写得比 Microsoft 烂....

同时,我亦不认同的说的类型检查拖慢速度的讲法。难道自己写的代码就不需要类型检查了?还是 Microsoft 的程序员都是白痴,净是做一些无用的检查。。。
CGabriel 2009-11-22
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 wangjia184 的回复:]
引用 3 楼 cgabriel 的回复:
Microsoft 已经把网络用的完成端口完整地封装在 Socket 里面,不明白咋的那么多人还去重写的,是觉得自己的写的代码比 Microsoft 的漂亮,还是高效?


可定制性不行啊, 如果是2*4核处理器,我想指定2个接受者、发送者线程工作在前面2个核上,如果我想使用自己的内存管理,这些都没有提供啊

有没有人真正比对过 P/Invoke 和 Socket的 AIO的性能差异呢???
[/Quote]

内存跟 CPU 你都要干预?还不如直接用 C++ 搞掂,然后把开放接口,简单百倍。
lextm 2009-11-21
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 zhujiechang 的回复:]
说实在的.net自带的socket效率在很多时候不行,可能你没有觉得,当然主要看你应用环境,如果你只是做一个普通的网络连接,或者web service之类那是够用了,如果是一个大型的在线连接,例如网游服务端,大型对局室等等,远远不够。
P/Invoke看你怎么实现,也有相当的性能损耗,但比.net自带的好,为什么好,都一样的函数?因为.net各类检查太多,不是函数本身问题,而是为什么执行这个函数所附带的其他检查代价
[/Quote]
假如一个大型应用不使用分布式的多台机器来处理,而是仅仅靠优化一台机器上的代码,我觉得那也是走了一个极端。毕竟花时间写自己的Socket,还不如多买几台服务器来的快。

对于一个大企业的大应用来说,可扩展性和性能的平衡更加重要。你自己写一个Socket,首先就费时费钱,后面维护起来也不是省心的事情。算总账的时候并不比多买几台服务器便宜那。
abiao1421 2009-11-20
  • 打赏
  • 举报
回复
1
zhujiechang 2009-11-20
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 cgabriel 的回复:]
Microsoft 已经把网络用的完成端口完整地封装在 Socket 里面,不明白咋的那么多人还去重写的,是觉得自己的写的代码比 Microsoft 的漂亮,还是高效?
[/Quote]
说实在的.net自带的socket效率在很多时候不行,可能你没有觉得,当然主要看你应用环境,如果你只是做一个普通的网络连接,或者web service之类那是够用了,如果是一个大型的在线连接,例如网游服务端,大型对局室等等,远远不够。
P/Invoke看你怎么实现,也有相当的性能损耗,但比.net自带的好,为什么好,都一样的函数?因为.net各类检查太多,不是函数本身问题,而是为什么执行这个函数所附带的其他检查代价
yangyanli 2009-11-20
  • 打赏
  • 举报
回复
http://b.qzone.qq.com/cgi-bin/blognew/blog_output_data?uin=66594958&blogid=1234945044&imgdm=imgcache.qq.com&bdm=b.qzone.qq.com&mode=2&numperpage=15&blogseed=0.4411217309458841&property=GoRE×tamp=1258702373
yangyanli 2009-11-20
  • 打赏
  • 举报
回复
http://user.qzone.qq.com/66594958/infocenter?ptlang=2052
CodeProject-Jerry 2009-11-20
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 cgabriel 的回复:]
Microsoft 已经把网络用的完成端口完整地封装在 Socket 里面,不明白咋的那么多人还去重写的,是觉得自己的写的代码比 Microsoft 的漂亮,还是高效?
[/Quote]

可定制性不行啊, 如果是2*4核处理器,我想指定2个接受者、发送者线程工作在前面2个核上,如果我想使用自己的内存管理,这些都没有提供啊

有没有人真正比对过 P/Invoke 和 Socket的 AIO的性能差异呢???
kennie_190602169 2009-11-20
  • 打赏
  • 举报
回复
帮你顶
CGabriel 2009-11-19
  • 打赏
  • 举报
回复
Microsoft 已经把网络用的完成端口完整地封装在 Socket 里面,不明白咋的那么多人还去重写的,是觉得自己的写的代码比 Microsoft 的漂亮,还是高效?
CodeProject-Jerry 2009-11-19
  • 打赏
  • 举报
回复



//好奇怪啊,我下面的代码老是抛异常,说optValue错误
clientSocket.SetSocketOption(SocketOptionLevel.Socket
, SocketOptionName.UpdateAcceptContext
, m_Socket.Handle
);

// 不管怎么写它都说 optValue错误
clientSocket.SetSocketOption(SocketOptionLevel.Socket
, SocketOptionName.UpdateAcceptContext
, m_Socket
);

// 我现在只能使用p/invoke了, 这样就可以,哪位可以给出不是p/invoke的写法??
{
IntPtr ptr = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteIntPtr( ptr, socket.Handle);
int ret = setsockopt(clientSocket.Handle
, SOL_SOCKET
, SO_UPDATE_ACCEPT_CONTEXT
, ptr
, IntPtr.Size
);
Marshal.FreeHGlobal(ptr);
return ret;
}
LutzMark 2009-11-18
  • 打赏
  • 举报
回复
先帮顶

110,548

社区成员

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

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

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