Отправка нескольких сообщений между собственным именованным каналом и именованным каналом System.IO. - PullRequest
2 голосов
/ 19 декабря 2010

Мне нужно отправить несколько сообщений между собственным именованным каналом и именованным каналом 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); 

} catch (Exex ex) { Console.WriteLine («Сервер выдает ошибку: {0}», ex.Message); } в конце концов { if (hNamedPipe! = null) { hNamedPipe.Close (); 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. Моя проблема в том, что он всегда возвращает истину после прочтения первого сообщения, и игнорирует второе сообщение второе сообщение.

Ответы [ 2 ]

1 голос
/ 19 декабря 2010

Клиент ничего не может сделать, кроме как посылать все свои "сообщения" за одну запись в канал. Это связано с тем, что в режиме сообщений сообщения ограничиваются завершением вызовов записи у отправителя, а код сервера явно считывает только одно сообщение (в смысле режима сообщений канала). См. CreateNamedPipe и ReadFile API документацию:

Данные записываются в канал как поток сообщений. Труба лечит байты, записанные во время каждой записи работа в качестве блока сообщений.

Если именованный канал читается в режим сообщения и следующее сообщение длиннее, чем nNumberOfBytesToRead параметр указывает, ReadFile возвращает FALSE и GetLastError возвращает ERROR_MORE_DATA. Остальная часть сообщение может быть прочитано последующим вызовите файл ReadFile или PeekNamedPipefunction.

Возможные подходы для работы с несколькими сообщениями:

  • определить более высокий уровень кадрирования протокол, по которому клиент может сказать сервер сколько сообщений читать в каждом обмен сообщениями. Затем клиент отправит серию сообщений, например, [заголовок фрейма: count = 3] [сообщение 1] [сообщение 2] [сообщение 3] или, альтернативно, [сообщение 1] [сообщение 2] [сообщение 3] [трейлер кадра: больше сообщений нет];
  • многопоточный сервер, в котором есть это выделенная тема постоянно чтение сообщений от клиента, другие операции, такие как письмо сообщения обратно клиенту сделано в других темах;
0 голосов
/ 20 декабря 2010

Спасибо, Крис, что указал мне на документацию.Я проголосовал за ваш ответ, поскольку приведенная вами цитата привела меня к ответу, который я искал.

Оказывается, меня просто смутил цикл 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 );

Что касается разделения нескольких "сообщений" в запросе клиента к серверу, я, вероятно, просто буду использовать символы новой строки.

...