2

ネイティブの名前付きパイプとSystem.IOの名前付きパイプの間で複数のメッセージを送信する必要があります。この通信の両端のコードは、オールインワンコードフレームワーク(IPCおよびRPC)から取得しました。

サーバ:

SafePipeHandle hNamedPipe = null;

try { SECURITY_ATTRIBUTES sa = null;
sa = CreateNativePipeSecurity();

// Create the named pipe.
hNamedPipe = NativeMethod.CreateNamedPipe(
    Constants.FullPipeName,             // The unique pipe name.
    PipeOpenMode.PIPE_ACCESS_DUPLEX,    // The pipe is duplex
    PipeMode.PIPE_TYPE_MESSAGE |        // Message type pipe 
    PipeMode.PIPE_READMODE_MESSAGE |    // Message-read mode 
    PipeMode.PIPE_WAIT,                 // Blocking mode is on
    PIPE_UNLIMITED_INSTANCES,           // Max server instances
    1024,                 // Output buffer size
    1024,                 // Input buffer size
    NMPWAIT_USE_DEFAULT_WAIT,           // Time-out interval
    sa                                  // Pipe security attributes
);

if (hNamedPipe.IsInvalid)
{
    throw new Win32Exception();
}

Console.WriteLine("The named pipe ({0}) is created.", Constants.FullPipeName);

// Wait for the client to connect.
Console.WriteLine("Waiting for the client's connection...");
if (!NativeMethod.ConnectNamedPipe(hNamedPipe, IntPtr.Zero))
{
    if (Marshal.GetLastWin32Error() != ERROR_PIPE_CONNECTED)
    {
        throw new Win32Exception();
    }
}
Console.WriteLine("Client is connected.");

// 
// Receive a request from client.
// 

string message;
bool finishRead = false;
do
{
    byte[] bRequest = new byte[1024];
    int cbRequest = bRequest.Length, cbRead;

    finishRead = NativeMethod.ReadFile(
        hNamedPipe,             // Handle of the pipe
        bRequest,               // Buffer to receive data
        cbRequest,              // Size of buffer in bytes
        out cbRead,             // Number of bytes read 
        IntPtr.Zero             // Not overlapped 
        );

    if (!finishRead &&
        Marshal.GetLastWin32Error() != ERROR_MORE_DATA)
    {
        throw new Win32Exception();
    }

    // Unicode-encode the received byte array and trim all the 
    // '\0' characters at the end.
    message = Encoding.Unicode.GetString(bRequest).TrimEnd('\0');
    Console.WriteLine("Receive {0} bytes from client: \"{1}\"", cbRead, message);
}
while (!finishRead);  // Repeat loop if ERROR_MORE_DATA

// 
// Send a response from server to client.
// 

message = "Goodbye\0";
byte[] bResponse = Encoding.Unicode.GetBytes(message);
int cbResponse = bResponse.Length, cbWritten;

if (!NativeMethod.WriteFile(
    hNamedPipe,                 // Handle of the pipe
    bResponse,                  // Message to be written
    cbResponse,                 // Number of bytes to write
    out cbWritten,              // Number of bytes written
    IntPtr.Zero                 // Not overlapped
    ))
{
    throw new Win32Exception();
}

Console.WriteLine("Send {0} bytes to client: \"{1}\"",
    cbWritten, message.TrimEnd('\0'));

// Flush the pipe to allow the client to read the pipe's contents 
// before disconnecting. Then disconnect the client's connection.
NativeMethod.FlushFileBuffers(hNamedPipe);
NativeMethod.DisconnectNamedPipe(hNamedPipe); 

} catch(Exception ex){Console.WriteLine( "サーバーがエラーをスローします:{0}"、ex.Message); }最後に{if(hNamedPipe!= null){hNamedPipe.Close(); hNamedPipe = null; }}

クライアント:

            NamedPipeClientStream pipeClient = null;

        try
        {
            // Try to open the named pipe identified by the pipe name.

            pipeClient = new NamedPipeClientStream(
                ".",         // The server name
                Constants.PipeName,           // The unique pipe name
                PipeDirection.InOut,        // The pipe is duplex
                PipeOptions.None            // No additional parameters
            );

            pipeClient.Connect(5000);
            MessageBox.Show(
                string.Format( "The named pipe ({0}) is connected.", Constants.PipeName )
            );

            pipeClient.ReadMode = PipeTransmissionMode.Message;

            // 
            // Send a request from client to server
            // 

            for ( int i = 0; i < 2; i++ ) {

                string message = "hello my pipe dream\0";
                byte[] bRequest = Encoding.Unicode.GetBytes( message );
                int cbRequest = bRequest.Length;

                pipeClient.Write( bRequest, 0, cbRequest );

                MessageBox.Show(
                    string.Format( "Send {0} bytes to server: \"{1}\"", cbRequest, message.TrimEnd( '\0' ) )
                );
            }

            //
            // Receive a response from server.
            // 

            do
            {
                byte[] bResponse = new byte[1024];
                int cbResponse = bResponse.Length, cbRead;

                cbRead = pipeClient.Read(bResponse, 0, cbResponse);

                // Unicode-encode the received byte array and trim all the 
                // '\0' characters at the end.
                string message = Encoding.Unicode.GetString(bResponse).TrimEnd('\0');
                Console.WriteLine("Receive {0} bytes from server: \"{1}\"",
                    cbRead, message);
            }
            while (!pipeClient.IsMessageComplete);

        }
        catch (Exception ex)
        {
            new ErrorDialog( ex ).ShowDialog();
        }
        finally
        {
            // Close the pipe.
            if (pipeClient != null)
            {
                pipeClient.Close();
                pipeClient = null;
            }
        }
    }

上記の「クライアントからサーバーにリクエストを送信する」セクションのforループからわかるように、サーバーに複数のメッセージを送信する方法を理解しようとしています。NativeMethod.ReadFile()メソッドがtrueを返すまで、サーバーコードがループすることがわかります。私の問題は、最初のメッセージが読み取られた後は常にtrueを返し、2番目のメッセージを無視することです。したがって、私の質問は、具体的には、このメソッドがfalseを返すように、クライアントコードで何をする必要があるかということです。 2番目のメッセージ。

4

2 に答える 2

1

パイプへの 1 回の書き込みですべての「メッセージ」を送信する以外に、クライアントができることは何もありません。これは、メッセージ モードでは、送信側での書き込み呼び出しの完了によってメッセージが区切られ、サーバー コードが (パイプ メッセージ モードの意味で) 1 つのメッセージだけを明示的に読み取るためです。CreateNamedPipeおよびReadFile APIドキュメントを参照してください。

データは、メッセージのストリームとしてパイプに書き込まれます。パイプは、各書き込み操作中に書き込まれたバイトをメッセージ ユニットとして扱います。

名前付きパイプがメッセージ モードで読み取られており、次のメッセージが nNumberOfBytesToRead パラメータで指定された長さよりも長い場合、ReadFile は FALSE を返し、GetLastError は ERROR_MORE_DATA を返します。メッセージの残りの部分は、その後 ReadFile または PeekNamedPipe 関数を呼び出すことで読み取ることができます。

複数のメッセージを処理するための可能なアプローチは次のとおりです。

  • クライアントが各メッセージ交換で読み取るメッセージの数をサーバーに伝えることができる、より高いレベルのフレーミングプロトコルを定義します。クライアントは、[framing header:count=3][message 1][message 2][message 3]、または [message 1][message 2][message 3][framing Trailer:もうメッセージはありません];
  • クライアントからメッセージを継続的に読み取る専用スレッドがあり、クライアントにメッセージを書き戻すなどの他の操作は他のスレッドで実行されるマルチスレッドサーバー。
于 2010-12-19T17:51:01.573 に答える
0

クリス、ドキュメントを教えてくれてありがとう。あなたが含めた引用が私が探していた答えに私を導いたので、私はあなたの答えに賛成票を投じました.

サーバーコードの「クライアントからリクエストを受信する」セクションの do/while ループに混乱していたことがわかりました。つまり、クライアントから複数のメッセージを取得しているように見えました。しかし実際には、単一のメッセージの連続した部分を取得していました。そのコード セクションを次のように更新しました。

// Receive a request from client.

string message = string.Empty;
bool finishRead = false;
do
{
    byte[] bRequest = new byte[1024];
    int cbRequest = bRequest.Length, cbRead;

    finishRead = NativeMethod.ReadFile(
        hNamedPipe,             // Handle of the pipe
        bRequest,               // Buffer to receive data
        cbRequest,              // Size of buffer in bytes
        out cbRead,             // Number of bytes read 
        IntPtr.Zero             // Not overlapped 
        );

    if (!finishRead &&
        Marshal.GetLastWin32Error() != ERROR_MORE_DATA)
    {
        throw new Win32Exception();
    }

    // Unicode-encode the received byte array and trim all the 
    // '\0' characters at the end.
    message += Encoding.Unicode.GetString(bRequest).TrimEnd('\0');
}
while (!finishRead);  // Repeat loop if ERROR_MORE_DATA

Console.WriteLine( "Message received from client: \"{0}\"", message );

クライアントからサーバーへのリクエスト内の複数の「メッセージ」の区切りについては、おそらく改行文字を使用します。

于 2010-12-19T23:52:31.773 に答える