如何使用命名管道(C++服务器,C#客户端)

4

我正在尝试开始使用命名管道,因为将来我的项目需要使用它们。

目前我有一个C++服务器等待客户端连接并发送测试消息。我大致遵循了这个教程来入门。相关代码如下:

    #define MESSAGE L"TestMessage"

HANDLE hnamedPipe = INVALID_HANDLE_VALUE;

hnamedPipe = CreateNamedPipe(
    L"\\\\.\\pipe\\testpipe",
    PIPE_ACCESS_DUPLEX,
    PIPE_TYPE_MESSAGE|
    PIPE_READMODE_MESSAGE|
    PIPE_WAIT,
    PIPE_UNLIMITED_INSTANCES,
    1024,
    1024,
    NMPWAIT_USE_DEFAULT_WAIT,
    NULL);

if(hnamedPipe == INVALID_HANDLE_VALUE)
{
        cout << "Failed" << endl;
}

while(true)
{
    cout<< "Waiting for client"<< endl;

    if(!ConnectNamedPipe(hnamedPipe,NULL))
    {
        if(ERROR_PIPE_CONNECTED != GetLastError())
        {
        cout << "FAIL"<< endl;
        }
    }

    cout<<"Connected!"<<endl;

    //Send over the message
    wchar_t chResponse[] = MESSAGE;
    DWORD cbResponse,cbWritten;
    cbResponse = sizeof(chResponse);

    if(!WriteFile(
    hnamedPipe,
    chResponse,
    cbResponse,
    &cbWritten,
    NULL))
    {
        wprintf(L"failiure w/err 0x%08lx\n",GetLastError);
    }
    cout<<"Sent bytes :)" << endl;
}

客户端代码(C#)如下:
        using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "testpipe", PipeDirection.InOut))
        {
            while (true)
            {
                Console.WriteLine("Connecting to server...");
                pipeClient.Connect();

                Console.WriteLine("Connected :)");
                Console.WriteLine(pipeClient.ReadByte());
                pipeClient.Close();
                Console.WriteLine("Closed");
            }

        }

目前我已经成功地使客户端连接到服务器并打印出第一个字节。我想知道如何做两个事情:
  1. 读取整个消息 - 我尝试使用StreamReader在pipeClient上读取消息,但它会无限期地挂起在ReadLine()。
  2. 连续发送消息 - 我希望服务器将一个接一个地向客户端发送消息,客户端将逐一读取并打印它们。我对IPC一知半解,所以一开始我尝试让客户端在while(true)循环中断开并重新连接到服务器,而服务器则在while true循环中等待新的客户端连接才会在顶部发送另一条消息。我的尝试在上面的代码中。
如果能给予任何帮助,将不胜感激。最终的目标是从服务器向客户端发送图像。然后客户端将实时将其打印到屏幕上。我想先通过简单的字符串消息来使其工作,然后再尝试图像数据。
编辑:
最终,我希望能够从客户端向服务器发送消息,指示它想要获取最新的图像帧,然后服务器将发送最新的帧,客户端将在屏幕上显示它们。因此流程如下:
  1. 客户端 -> 服务器: 指示客户端想要获取最新的帧信息。(某些简单内容,可能是具有值1的无符号整数)
  2. 服务器 -> 客户端: 最新的帧信息。(640x480图像存储在带有RGB字节值的字节数组中)
  3. 客户端: 在显示器上显示帧。

对于你的第一个问题,可以使用类似 StreamReader 的东西(只有在你的消息是文本时才使用)。StreamReader.ReadToEnd 将从流中读取所有可用数据。 - M.Babcock
嗨,我尝试过了,但它会等到C++服务器关闭后才一次性打印出所有消息。最终,我想能够从客户端向服务器发送一条消息,指示它想获取最新的图像帧,然后服务器将发送最新的帧,客户端将在屏幕上显示它。 - Jkh2
2个回答

5

ReadLine会挂起,因为它在等待换行符,而你的测试消息中并没有包含换行符。

如果你想让服务器不断发送消息,只需将WriteFile调用放入循环中即可。你不需要多次连接。同样,在客户端中,将ReadLine放入循环中。

如果每条消息由以换行符结尾的文本组成,则应该足够了,但如果你真的想让管道客户端在消息模式下工作,你需要调用:

pipeClient.ReadMode = PipeTransmissionMode.Message;

然而,我怀疑这与StreamReader不兼容。相反,您应该使用pipeClient.Read读取单个消息。
更新:
回答您的新问题:
在服务器上,一旦客户端连接后进入一个循环,在此循环中:
- 服务器从客户端读取。这将阻塞,直到客户端请求帧。 - 服务器发送帧。
在客户端上,一旦连接到服务器,请进入以下循环:
- 客户端发送“请发送帧”消息。 - 客户端从服务器读取以获取帧。 - 客户端显示帧。
我不会使用消息模式管道。如果帧大小固定,则客户端知道从服务器读取多少数据。否则,请在帧前加上包含其长度的uint。

谢谢,我已经将这个逻辑融入到我的代码中,它运行得非常好 :) - Jkh2

1
这是一个使用命名管道将字符串从C#应用程序(客户端)发送到C++应用程序(服务器)并在C++应用程序的控制台中显示接收到的消息的简单示例,两个应用程序都是Visual Studio中的控制台应用程序。
C#客户端应用程序代码
using System.IO.Pipes;
using System.Text;

namespace CSclient
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create Named Pipes
            using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "mynamedpipe", PipeDirection.InOut))
            {
                string message = "Test message from C# client!";
                // Connect Named Pipes
                pipeClient.Connect();
                byte[] messageBytes = Encoding.UTF8.GetBytes(message);
                // Send the message to the server
                pipeClient.Write(messageBytes, 0, messageBytes.Length);
            }
        }
    }
}

这是一份关于C++服务器应用程序代码的内容,以下链接被用作指南:管道服务器

#include <windows.h> 
#include <stdio.h> 
#include <tchar.h>
#include <strsafe.h>
#include <iostream>
#include <string>

#define BUFSIZE 512

DWORD WINAPI InstanceThread(LPVOID);
VOID GetAnswerToRequest(char*, LPTSTR, LPDWORD);

int _tmain(VOID)
{
    BOOL   fConnected = FALSE;
    DWORD  dwThreadId = 0;
    HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
    LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");

    // Create Named Pipe
    hPipe = CreateNamedPipe(
        lpszPipename,             // pipe name 
        PIPE_ACCESS_DUPLEX,       // read/write access 
        PIPE_TYPE_MESSAGE |       // message type pipe 
        PIPE_READMODE_MESSAGE |   // message-read mode 
        PIPE_WAIT,                // blocking mode 
        PIPE_UNLIMITED_INSTANCES, // max. instances  
        BUFSIZE,                  // output buffer size 
        BUFSIZE,                  // input buffer size 
        0,                        // client time-out 
        NULL);                    // default security attribute 

    // Connect Named Pipe
    fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

    if (fConnected)
    {
        HANDLE hHeap = GetProcessHeap();
        char* pchRequest = (char*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(char));
        TCHAR* pchReply = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(TCHAR));

        DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
        BOOL fSuccess = FALSE;

        // Read client requests from the pipe. This simplistic code only allows messages
        // up to BUFSIZE characters in length.
        fSuccess = ReadFile(
            hPipe,        // handle to pipe 
            pchRequest,    // buffer to receive data 
            BUFSIZE * sizeof(char), // size of buffer 
            &cbBytesRead, // number of bytes read 
            NULL);        // not overlapped I/O 

        if (!fSuccess || cbBytesRead == 0)
        {
            std::cout << "Reading error!";
        }

        // Process the incoming message.
        GetAnswerToRequest(pchRequest, pchReply, &cbReplyBytes);
    }

    return 0;
}

// This routine is a simple function to print the client request to the console
VOID GetAnswerToRequest(char* pchRequest, LPTSTR pchReply, LPDWORD pchBytes)
{
    std::string requestMessage = pchRequest;
    // Show the message in the console
    std::cout << requestMessage.c_str();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接