在本地命名管道和System.IO命名管道之间发送多个消息

3
我可以帮你翻译。以下是翻译结果,保留html标签:

我需要在本地命名管道和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); 

} 捕获(Exception ex) { 控制台.WriteLine("服务器抛出错误: {0}", ex.Message); } 最后 { 如果(hNamedPipe != null) { hNamedPipe.关闭(); 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,并忽略第二条消息。所以我的问题是,具体来说,我需要在客户端代码中做什么,使得该方法返回false,然后它就可以获取第二条消息了。请注意保留HTML标签。
2个回答

2

除了将所有的“消息”以单个写入管道的方式发送,客户端无法做任何事情。这是因为在消息模式下,消息是由发送方写入调用完成而分隔的,而您的服务器代码明确只读取一个消息(按照管道消息模式的意义)。请参见CreateNamedPipeReadFile API文档:

数据被写入管道作为一组消息流。 管道处理每个写操作期间写入的字节作为消息单元。

如果正在以消息模式读取命名管道并且下一条消息长于nNumberOfBytesToRead参数指定的长度,则ReadFile返回FALSE,GetLastError返回ERROR_MORE_DATA。剩余的消息可以通过后续调用ReadFile或PeekNamedPipe函数来读取。

处理多个消息的可能方法包括:

  • 定义一些更高级别的框架协议,客户端可以通过该协议告诉服务器每个消息交换中要读取多少条消息。然后客户端将发送一系列类似于 [框架头:计数=3] [消息1] [消息2] [消息3] 的消息,或者是 [消息1] [消息2] [消息3] [框架尾:没有更多消息];
  • 一个多线程服务器,在其中有一个专用线程不断地从客户端读取消息,其他操作如向客户端写回消息则在其他线程上完成;

0
谢谢你,Chris,指引我去查阅文档。我给你的答案点了赞,因为你引用的那段话帮助我找到了我想要的答案。
事实证明,我只是对服务器代码中的“从客户端接收请求”部分的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 );

至于在客户端请求中分隔多个“消息”的方法,我可能会使用换行符。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接