Envoyer plusieurs messages entre un tube nommé natif et un tube nommé System.IO
-
11-10-2019 - |
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.
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;