2010-12-18 15 views
2

Ich muss mehrere Nachrichten zwischen einer systemeigenen Named Pipe und einer System.IO Named Pipe senden. Ich habe den Code für beide Enden dieser Kommunikation aus dem All-In-One Code Framework (IPC und RPC) erhalten.Senden Sie mehrere Nachrichten zwischen einer systemeigenen Named Pipe und einer System.IO Named Pipe

Server:

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 ("Der Server führt den Fehler: {0}", ex.Message); } schließlich { if (hNamedPipe! = Null) { hNamedPipe.Close(); hNamedPipe = null; } }

Auftraggeber:

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

Wie Sie oben im Abschnitt sehen von der for-Schleife in dem „eine Anforderung vom Client zum Server senden“, ich versuche, herauszufinden, wie mehrere senden Nachrichten an den Server. Ich sehe, dass der Server-Code durchläuft, bis die Methode NativeMethod.ReadFile() True zurückgibt. Mein Problem ist, dass es immer wahr zurückgibt, nachdem die erste Nachricht gelesen wurde, und die zweite Nachricht ignorierend. Also ist meine Frage speziell, was ich im Clientcode tun muss, damit diese Methode false zurückgibt, dann wird es gehen die zweite Nachricht.

Antwort

0

Danke, Chris, dass Sie mich auf die Dokumentation verwiesen haben. Ich habe deine Antwort neu gewählt, weil das Zitat, das du eingefügt hast, zu der Antwort geführt hat, nach der ich gesucht habe.

Es stellte sich heraus, ich war nur verwirrt von der Do/While-Schleife im Server-Code "Request from Client" Abschnitt erhalten. Das heißt, es sah so aus, als würde es mehrere Nachrichten vom Client abrufen. Aber tatsächlich wurden aufeinanderfolgende Teile der einzelnen Nachricht abgerufen. Ich habe diesen Codeabschnitt wie folgt aktualisiert.

// 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); 

Wie für die mehrfachen „Botschaften“ innerhalb der Client-Anfrage an den Server begrenzen, werde ich wahrscheinlich nur Zeilenumbrüche verwenden.

1

Es gibt nichts, was der Client tun kann, außer all seine "Nachrichten" in einem einzigen Schreibvorgang an die Pipe zu senden. Dies liegt daran, dass die Nachrichten im Nachrichtenmodus durch die Beendigung von Schreibaufrufen beim Absender begrenzt sind und der Servercode nur eine Nachricht explizit liest (im Sinne des Pipe-Nachrichtenmodus). Siehe CreateNamedPipe und ReadFile API Dokumentation:

Data is written to the pipe as a stream of messages. The pipe treats the bytes written during each write operation as a message unit.

If a named pipe is being read in message mode and the next message is longer than the nNumberOfBytesToRead parameter specifies, ReadFile returns FALSE and GetLastError returns ERROR_MORE_DATA. The remainder of the message can be read by a subsequent call to the ReadFile or PeekNamedPipefunction.

Mögliche Ansätze mit mehreren Nachrichten zu arbeiten, ist:

  • etwas höhere Ebene definieren Framing Protokoll, mit dem der Client den Server mitteilen kann, wie viele Nachrichten zu lesen in jedem Nachrichtenaustausch. Der Client würde dann eine Reihe von Nachrichten wie [framing header: count = 3] [Nachricht 1] [Nachricht 2] [Nachricht 3] oder alternativ [Nachricht 1] [Nachricht 2] [Nachricht 3] [Framing-Trailer: keine weiteren Nachrichten];
  • ein multithreaded Server, in dem ein dedizierter Faden kontinuierlich Lesen von Nachrichten von dem Client, andere Operationen, wie das Schreiben Nachrichten zurück an den Client wird auf andere Threads durchgeführt;
Verwandte Themen