【问题标题】:How to work with named pipes (C++ server , C# client)如何使用命名管道(C++ 服务器、C# 客户端)
【发布时间】:2012-03-29 09:35:57
【问题描述】:

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

目前我有一个 C++ 服务器,它一直等到客户端连接并通过测试消息发送。我大致按照这个tutorial 开始。相关代码如下:

    #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. 阅读整个消息 - 我尝试在 pipeClient 上使用 StreamReader 来阅读消息,但它无限期地挂在 ReadLine() 上。

  2. 连续发送消息 - 我希望服务器将消息一个接一个地发送到客户端,客户端将一次读取它们并打印出来。我对 IPC 有点一无所知,所以起初我试图让客户端断开连接并在 while(true) 循环中重新连接到服务器,而服务器处于 while true 循环中,它在顶部总是等待新的客户端连接之前发送另一条消息。我的尝试是在上面的代码中。

对此的任何帮助将不胜感激。最终目标是通过图像从服务器发送到客户端。然后客户端将它们实时打印到屏幕上。在我尝试图像数据之前,我想用简单的字符串消息来解决这个问题。

编辑:

最终我希望能够从客户端向服务器发送一条消息,表明它想要获取最新的图像帧,然后服务器将发送最新的帧,然后客户端将显示在屏幕上。所以流程是:

  1. Client -> Server : 客户端需要最新帧信息的指示符。 (很简单,可能是一个值为 1 的无符号整数)
  2. 服务器 -> 客户端:最新帧信息。 (以 RGB 字节值存储在字节数组中的 640x480 图像)
  3. 客户端:在显示器上显示框架。

【问题讨论】:

  • 对于您的第一个问题,请使用StreamReader 之类的内容(仅当您的消息是文本时才使用此选项)。 StreamReader.ReadToEnd 将从流中读取所有可用数据。
  • 嗨,我试过了,但它会等到 C++ 服务器关闭后,才会一次打印出所有消息。最终,我希望能够从客户端向服务器发送一条消息,表明它想要获取最新的图像帧,然后服务器将发送最新的帧,然后客户端将显示在屏幕上。

标签: c# c++ windows ipc named-pipes


【解决方案1】:

ReadLine 挂起,因为它正在等待换行符,而您的测试消息不包括该换行符。

如果您希望服务器连续发送消息,只需在 WriteFile 调用周围放置一个循环。您无需多次连接。同样在客户端中,将循环放在ReadLine 周围。

如果每条消息都包含由换行符终止的文本,那么这就足够了,但是如果你真的希望管道客户端在消息模式下工作,你需要调用:

pipeClient.ReadMode = PipeTransmissionMode.Message;

但是,我怀疑这会与StreamReader 很好地交互。相反,您应该使用 pipeClient.Read 阅读单个消息。

更新

回答你的新问题:

在服务器上,一旦客户端连接,进入一个循环:

  • 服务器从客户端读取。这将一直阻塞,直到客户端请求一个帧。
  • 服务器发送一个帧。

在客户端上,一旦连接到服务器,就会进入一个循环,其中:

  • 客户端发送“请发送帧”消息。
  • 客户端从服务器读取帧以获取帧。
  • 客户端显示框架。

我不会使用消息模式管道。如果帧的大小是固定的,那么客户端就知道要从服务器读取多少数据。否则,在帧之前加上一个包含其长度的 uint。

【讨论】:

  • 谢谢,我已经将这个逻辑应用到我的代码中,效果很好:)
【解决方案2】:

这是一个使用命名管道将字符串从 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++ 服务器应用程序代码,以下链接被用作指南Pipe Server

#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();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-30
    • 2013-11-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多