使用Mono的另一个进程使用套接字描述符

Ric*_*ick 5 .net c# sockets linux mono

我有一个用C#编写的BBS门,我想在Linux上工作..NET框架的Socket类不支持打开现有的套接字句柄,因此需要为Windows和Linux实现不同的解决方法.

对于Linux,我查看了Socket.cs文件,看到这发生在DuplicateAndClose()中:

var si = new SocketInformation ();
si.Options =
    (islistening ? SocketInformationOptions.Listening : 0) |
    (connected ? SocketInformationOptions.Connected : 0) |
    (blocking ? 0 : SocketInformationOptions.NonBlocking) |
    (useoverlappedIO ? SocketInformationOptions.UseOnlyOverlappedIO : 0);

si.ProtocolInformation = Mono.DataConverter.Pack ("iiiil", 
    (int)address_family, 
    (int)socket_type, 
    (int)protocol_type, 
    isbound ? 1 : 0, 
    (long)socket);

socket = (IntPtr) (-1);

return si;
Run Code Online (Sandbox Code Playgroud)

由于我无法从Windows访问Mono.DataConverter,因此我也查看了它的来源,并提出了以下建议:

SocketInformation SI = new SocketInformation();
SI.Options = SocketInformationOptions.Connected;
SI.ProtocolInformation = new byte[24];

Int32 AF = (Int32)AddressFamily.InterNetwork;
Int32 ST = (Int32)SocketType.Stream;
Int32 PT = (Int32)ProtocolType.Tcp;
Int32 Bound = 0;
Int64 Socket = (Int64)ASocketHandle;
unsafe
{
    fixed (byte* target = &SI.ProtocolInformation[0])
    {
        uint* source = (uint*)⁡
        *((uint*)target) = *source;
    }
    fixed (byte* target = &SI.ProtocolInformation[4])
    {
        uint* source = (uint*)&ST;
        *((uint*)target) = *source;
    }
    fixed (byte* target = &SI.ProtocolInformation[8])
    {
        uint* source = (uint*)&PT;
        *((uint*)target) = *source;
    }
    fixed (byte* target = &SI.ProtocolInformation[12])
    {
        uint* source = (uint*)&Bound;
        *((uint*)target) = *source;
    }
    fixed (byte* target = &SI.ProtocolInformation[16])
    {
        long* source = (long*)&Socket;
        *((long*)target) = *source;
    }
}
Run Code Online (Sandbox Code Playgroud)

所以现在我已经填充了SocketInformation,我应该可以这样做:

Socket S = new Socket(SI);
S.Send(new byte[] { 65, 66, 67, 68 });
Run Code Online (Sandbox Code Playgroud)

远程用户应该看到ABCD.但是调用Send会抛出异常,并显示消息"描述符不是套接字".

所以起初我以为我想要做的事情是不可能的,但后来我试着对send()进行调用:

Socket S = new Socket(SI);
send(S.Handle, new byte[] { 65, 66, 67, 68 }, 4, SocketFlags.None);
Run Code Online (Sandbox Code Playgroud)

其中send()声明为:

[DllImport("libc")]
private extern static int send(IntPtr sock, byte[] buf, int count, SocketFlags flags);
Run Code Online (Sandbox Code Playgroud)

它工作得很好!

因此,如果对send()的pinvoked调用对描述符没问题,那么让托管的S.Send()调用失败说它不是套接字描述符我做错了什么?我假设它与我如何填充SocketInformation有关,但是如果没有别的东西,那么句柄似乎正确填充,因为我能够使用它与pinvoked send()...

编辑:在进一步深入了解Mono源之后,我可能已经找到了问题:

S.Send()将调用Send_internal(),然后调用_wapi_send().这是最终调用send()的地方,但在此之前发生这种情况:

if (_wapi_handle_type (handle) != WAPI_HANDLE_SOCKET) {
    WSASetLastError (WSAENOTSOCK);
    return(SOCKET_ERROR);
}
Run Code Online (Sandbox Code Playgroud)

所以我猜_wapi_handle_type不会返回WAPI_HANDLE_SOCKET(可能因为句柄被传入而不是由进程创建,因此它不知道句柄类型),所以这就是我神秘的"描述符不是套接字"错误来自.

猜猜这是一个很长的镜头,但还有其他人遇到这个问题吗?有没有解决方案,除了完全不受套接字I/O管理?