如何在System.Net.Sockets中实现WSARecv的MSG_PUSH_IMMEDIATE即时接收功能?
Great question! The .NET Socket class doesn’t expose the MSG_PUSH_IMMEDIATE Winsock2 flag directly as a public API, but you absolutely can replicate the same functionality to ignore the PSH flag and forward buffer data immediately. Here are two reliable approaches:
Approach 1: Call WSARecv Directly via PInvoke
Since the .NET Socket wraps a native Winsock socket, you can use its Handle property to access the underlying socket descriptor and call WSARecv directly with the MSG_PUSH_IMMEDIATE flag. This matches your original Winsock2 workflow exactly.
Here’s a simplified implementation:
using System; using System.Net.Sockets; using System.Runtime.InteropServices; public class ImmediateSocketReceiver { // Define the MSG_PUSH_IMMEDIATE flag (matches Winsock2's value) private const int MSG_PUSH_IMMEDIATE = 0x20000; private const int SOCKET_ERROR = -1; [StructLayout(LayoutKind.Sequential)] private struct WSABUF { public int len; public IntPtr buf; } [DllImport("ws2_32.dll", SetLastError = true)] private static extern int WSARecv( IntPtr socketHandle, [In, Out] WSABUF[] buffers, int bufferCount, out int bytesReceived, ref int flags, IntPtr overlapped, IntPtr completionRoutine); public int ReceiveImmediate(Socket socket, byte[] buffer, int offset, int size) { if (socket == null || buffer == null) throw new ArgumentNullException(); if (offset < 0 || size < 0 || offset + size > buffer.Length) throw new ArgumentOutOfRangeException(); // Wrap the input buffer in a WSABUF var wsabuf = new WSABUF { len = size, buf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset) }; int flags = MSG_PUSH_IMMEDIATE; int bytesReceived; // Call the native WSARecv function int result = WSARecv(socket.Handle, new[] { wsabuf }, 1, out bytesReceived, ref flags, IntPtr.Zero, IntPtr.Zero); if (result == SOCKET_ERROR) { int errorCode = Marshal.GetLastWin32Error(); throw new SocketException(errorCode); } return bytesReceived; } }
Approach 2: Use Socket.ReceiveAsync with a Custom SocketFlag
The .NET SocketFlags enum doesn’t include MSG_PUSH_IMMEDIATE, but it’s just a wrapper around integer values. You can cast the flag’s integer value directly to SocketFlags and pass it to asynchronous receive methods like ReceiveAsync. This fits better with .NET’s async programming model.
Example code for async usage:
using System; using System.Net.Sockets; using System.Threading.Tasks; public async Task<int> ReceiveImmediateAsync(Socket socket, byte[] buffer, int offset, int size) { const int MSG_PUSH_IMMEDIATE = 0x20000; var args = new SocketAsyncEventArgs(); args.SetBuffer(buffer, offset, size); // Cast the flag value to SocketFlags args.SocketFlags = (SocketFlags)MSG_PUSH_IMMEDIATE; // Wrap the async operation in a Task await Task.Factory.FromAsync( callback => socket.ReceiveAsync(args), ar => args.BytesTransferred, null); return args.BytesTransferred; }
Key Notes
- Both approaches achieve the same goal: forcing immediate reception of buffer data regardless of the PSH flag, which is critical for low-latency requirements.
- Choose the PInvoke method if you need synchronous, direct control over the Winsock call. Use the
ReceiveAsyncapproach for a more idiomatic .NET async workflow.
内容的提问来源于stack exchange,提问作者musli

