Invia più messaggi tra una named pipe nativa e una pipa denominata System.IO
-
11-10-2019 - |
Domanda
ho bisogno di inviare più messaggi tra una named pipe nativa e un tubo System.IO di nome. Ho ottenuto il codice per entrambe le estremità di questa comunicazione della All-In-One Codice quadro (IPC e RPC).
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 ( "Il server genera l'errore: {0}", ex.Message); } finalmente { if (hNamedPipe! = null) { hNamedPipe.Close (); hNamedPipe = null; } }
Cliente:
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;
}
}
}
Come si può vedere dal ciclo for nella sezione "Invia una richiesta dal client al server" di cui sopra, sto cercando di capire come inviare più messaggi al server. Vedo che il codice del server scorre fino a quando il metodo NativeMethod.ReadFile () restituisce true. Il mio problema è che si è sempre tornando vero dopo il primo messaggio viene letto, e ignorando il secondo messaggio Quindi la mia domanda, in particolare, è quello che ho bisogno di fare nel codice client in modo che il metodo restituisce false in modo da allora andrà get il secondo messaggio.
Soluzione 2
Grazie, Chris, per me indicando la documentazione. I up-votato la tua risposta dal momento che la citazione che mi Incluso portato alla risposta che cercavo.
Si scopre che sono stato solo confuso dal do / while nel codice del server "Ricevi una richiesta da parte del cliente" sezione. Cioè, sembrava a me come è stato recuperando più messaggi dal client. Ma in realtà, è stato recuperando parti consecutivi del singolo messaggio. Ho aggiornato quella sezione codice come segue.
// 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 );
Per quanto riguarda la delimitazione delle molteplici "messaggi" all'interno la richiesta del client al server, io probabilmente basta usare caratteri di nuova riga.
Altri suggerimenti
Non c'è niente il cliente può fare altro che inviare tutti i suoi "messaggi" in una singola scrittura al tubo. Questo perché, in modalità di messaggio, i messaggi sono delimitati dal completamento di chiamate di scrittura presso il mittente, e il codice server legge esplicitamente un solo messaggio (in modalità di senso messaggio pipe). Vedere CreateNamedPipe e ReadFile API documentazione:
I dati vengono scritti al tubo come flusso di messaggi. I tubi tratta i byte scritti nel corso di ogni scrittura funzionamento come unità di messaggio.
Se una named pipe viene letto in modalità messaggio e il messaggio successivo è più lungo del nNumberOfBytesToRead specifica dei parametri, resi ReadFile restituisce FALSE e GetLastError ERROR_MORE_DATA. Il resto della messaggio può essere letto da un successivo chiamare al ReadFile o PeekNamedPipefunction.
I possibili approcci al lavoro con più messaggi sono:
- definire alcuni inquadramento di livello superiore protocollo con il quale il cliente può dire il server quanti messaggi da leggere in ogni interscambio messaggio. Il cliente avrebbe quindi inviare una serie di messaggi qualcosa di simile [intestazione inquadratura: count = 3] [messaggio 1] [messaggio 2] [messaggio 3], o, in alternativa [messaggio 1] [messaggio 2] [messaggio 3] [trailer inquadratura: Niente più messaggi];
- un server multithread in cui vi è un filo dedicato continuamente la lettura dei messaggi da parte del cliente, altre operazioni come la scrittura messaggi avanti all'essere cliente fatto su altri thread;