Question

Je dois envoyer plusieurs messages entre un tube nommé natif et un tube nommé System.IO. Je suis le code pour les deux extrémités de cette communication du All-In-One Code de cadre (IPC et RPC).

Serveur:

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 ( "Le serveur lance l'erreur: {0}", ex.Message); } enfin {     si (hNamedPipe! = null)     {         hNamedPipe.Close ();         hNamedPipe = null;     } }

Client:

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

Comme vous pouvez le voir sur la boucle dans la section « Envoyer une demande du client au serveur » ci-dessus, je suis en train de comprendre comment envoyer plusieurs messages au serveur. Je vois que le code du serveur par des boucles jusqu'à ce que le retour de la méthode NativeMethod.ReadFile () vrai. Mon problème est qu'il retourne toujours vrai après le premier message est lu, et en ignorant le second message Ma question, en particulier, est ce que dois-je faire dans le code client de sorte que cette méthode retourne faux donc il va aller chercher le deuxième message.

Était-ce utile?

La solution 2

Merci, Chris, me montrant la documentation. Je mis voté votre réponse puisque la citation que vous me comprenait CONDUIT à la réponse que je cherchais.

Il se trouve que je ne faisais que confus par la boucle do / while dans la section « Recevoir une demande du client » est le code serveur. Autrement dit, il me semblait qu'il récupérait plusieurs messages du client. Mais en fait, il récupérait des parties consécutives du message unique. Je mis à jour cette section de code comme suit.

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

En ce qui concerne la délimitation des multiples « messages » dans la demande du client au serveur, je vais probablement utiliser simplement des sauts de ligne.

Autres conseils

Il n'y a rien que le client peut faire d'autre que d'envoyer tous ses « messages » en une seule écriture à la conduite. En effet, en mode message, les messages sont délimités par l'achèvement des appels d'écriture à l'expéditeur et le code de votre serveur explicitement lit un seul message (au sens du mode de message de la conduite). Voir CreateNamedPipe et documentation de l'API ReadFile:

  

Les données sont écrites à la conduite en tant que   flux de messages. Les tuyaux friandises   les octets écrits au cours de chaque écriture   le fonctionnement en tant qu'unité de message.

     

Si est lu un tube nommé dans   mode message et le message suivant est   plus longue que la nNumberOfBytesToRead   paramètre indique, les retours ReadFile   retourne FALSE et GetLastError   ERROR_MORE_DATA. Le reste de la   message peut être lu par une suite   appel à la ReadFile ou   PeekNamedPipefunction.

Les approches possibles de travailler avec plusieurs messages sont:

  • définir un certain cadrage de niveau supérieur protocole par lequel le client peut dire le serveur le nombre de messages à lire dans chaque échange de messages. Le client envoie ensuite une série de messages quelque chose comme [en-tête de cadrage: count = 3] [message 1] [message 2] [message 3], ou bien [message 1] [message 2] [message 3] [bande-annonce de cadrage: pas plus de messages];
  • un serveur multithread dans lequel il est un fil dédié en continu la lecture des messages du client, d'autres opérations comme l'écriture messages à l'être client fait à d'autres fils;
scroll top