5

Linux で作業したい C# で書かれた BBS ドアがあります。.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;

私は 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;
    }
}

SocketInformation が入力されたので、これを実行できるはずです。

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

リモート ユーザーには ABCD が表示されます。しかし、代わりに Send を呼び出すと、「記述子はソケットではありません」というメッセージとともに例外がスローされます。

そのため、最初は自分がやりたいことは不可能だと思っていましたが、send() の呼び出しをピンボークしてみました。

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

send() は次のように宣言されています。

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

そして、それはうまく機能します!

したがって、send() への pinvoked 呼び出しが記述子で問題ない場合、マネージ S.Send() 呼び出しがソケット記述子ではないと言って失敗するのは何が間違っているのでしょうか? SocketInformation の入力方法と関係があると思いますが、pinvoked send() で使用できるため、ハンドルが正しく入力されているように見えます...

編集:Monoソースをさらに掘り下げた後、問題を突き止めた可能性があります:

S.Send() は Send_internal() を呼び出し、Send_internal() は _wapi_send() を呼び出します。最終的に send() の呼び出しが行われる場所ですが、その前に次のことが起こります。

if (_wapi_handle_type (handle) != WAPI_HANDLE_SOCKET) {
    WSASetLastError (WSAENOTSOCK);
    return(SOCKET_ERROR);
}

したがって、_wapi_handle_type は WAPI_HANDLE_SOCKET を返さないと推測しています (おそらく、ハンドルが渡され、プロセスによって作成されていないため、ハンドルの種類がわからないためです)。 "エラーが発生しています。

これはロングショットだと思いますが、他の誰かがこの問題に遭遇しましたか? ソケット I/O で完全に管理対象外になる以外に、解決策はありますか?

4

0 に答える 0