我在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的做法?
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的做法?
//好奇怪啊,我下面的代码老是抛异常,说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;
}
可定制性不行啊, 如果是2*4核处理器,我想指定2个接受者、发送者线程工作在前面2个核上,如果我想使用自己的内存管理,这些都没有提供啊有没有人真正比对过 P/Invoke 和 Socket的 AIO的性能差异呢???
P/Invoke看你怎么实现,也有相当的性能损耗,但比.net自带的好,为什么好,都一样的函数?因为.net各类检查太多,不是函数本身问题,而是为什么执行这个函数所附带的其他检查代价
内存跟 CPU 你都要干预?还不如直接用 C++ 搞掂,然后把开放接口,简单百倍。
是吗,我们做的服务端,在最普通的双核至强上面都可以挂上千把个活跃连接,而且还很顺畅。满足通常的要求应该没有问题了。至于 P/Invoke 的实现,我认为九成程序员的代码都写得比 Microsoft 烂....同时,我亦不认同的说的类型检查拖慢速度的讲法。难道自己写的代码就不需要类型检查了?还是 Microsoft 的程序员都是白痴,净是做一些无用的检查
并发 - 吞吐率 曲线是怎样的? 如果我上2W的并发能够保证多少吞吐率?
这样可以避免繁重的业务处理导致网络数据包接收的阻塞.根据服务器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核系统,则设置位0xFFDWORD_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之间切换带来的损失.上面所说的这些都只是理论,到实际的系统中,必须经过反复的性能对比试验来确定最佳方案