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

问题描述 投票:3回答:2

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

目前,我有一个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");
            }

        }

目前,我已经让客户端成功连接到服务器,并打印出第一个字节。我想知道如何做2件事。

  1. 读取整个消息 - 我试过在管道客户端上使用StreamReader来读取消息,但它在ReadLine()上无限期地挂起。

  2. 连续发送消息--我想让服务器发送一条又一条的消息到客户端,客户端一次读取一条并打印出来。我对IPC有点不了解,所以一开始我试着让客户端断开连接,然后在while(true)循环中重新连接到服务器,而服务器则在while true循环中,在发送另一条消息之前总是等待新的客户端连接。我的尝试就在上面的代码中。

如果有任何帮助,我将非常感激。最终的目的是将图片从服务器发送到客户端。客户端将它们实时打印到屏幕上。在尝试图像数据之前,我想用简单的字符串消息来实现这个功能。

EDIT:

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

  1. 客户端 -> 服务器 : 客户端想要最新帧信息的指示器。(简单的东西,也许是一个值为1的无符号int)
  2. 服务器 -> 客户端 。最新帧信息。(640x480图像存储在一个RGB字节值的字节数组中)
  3. 客户端 。在显示器上显示框架。
c# c++ windows ipc named-pipes
2个回答
4
投票

ReadLine 挂起,因为它正在等待一个换行,而你的测试消息并不包括这个换行。

如果你想让服务器连续发送消息,只需在你的测试消息中绕一个 WriteFile 呼叫。 你不需要连接多次。 同样,在客户端中,将循环放在 ReadLine.

如果每条消息都是由一个换行结束的文本组成,那么这就足够了,但如果你真的想让管道客户端在消息模式下工作,你需要调用。

pipeClient.ReadMode = PipeTransmissionMode.Message;

然而,我怀疑这是否能与一个... StreamReader. 相反,你应该使用 pipeClient.Read.

更新

回答你的新问题。

在服务器上,一旦客户机连接完毕,就进入一个循环,其中:

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

在客户端,一旦连接到服务器,进入一个循环,其中:客户端发送一个 "请发送一个帧"。

  • 客户端发送一个 "请发送一帧 "的消息。
  • 客户端从服务器进行读取,以获取帧。
  • 客户端显示该帧。

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


0
投票

这是一个简单的例子,使用Named Pipes从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();
}
© www.soinside.com 2019 - 2024. All rights reserved.