如何使用命名管道(C++服务器、C#客户端)
本文关键字:服务器 客户端 C++ 何使用 管道 | 更新日期: 2023-09-27 18:27:38
我正在尝试开始使用命名管道,因为我将来需要在我的项目中使用它们。
目前,我有一个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");
}
}
目前,我已经让客户端成功连接到服务器,它打印出第一个字节。我想知道如何做两件事:
读取整个消息-我尝试通过pipeClient使用StreamReader读取消息,但它无限期地挂在ReadLine()上。
持续发送消息-我希望服务器向客户端发送一条又一条消息,客户端将一次读取一条消息并打印出来。我对IPC有点不了解,所以一开始我试图在while(true)循环中使客户端断开连接并重新连接到服务器,而服务器处于while true循环中,该循环在顶部总是等待新的客户端连接,然后再发送另一条消息。我的尝试在上面的代码中。
如有任何帮助,我们将不胜感激。最终目标是将图像从服务器发送到客户端。然后客户端会将它们实时打印到屏幕上。在尝试图像数据之前,我想用简单的字符串消息来实现这一点。
编辑:
最终,我希望能够从客户端向服务器发送一条消息,指示它想要获得最新的图像帧,然后服务器将发送最新的帧,然后客户端将在屏幕上显示该帧。所以流程是:
- 客户端->服务器:指示客户端需要最新的帧信息。(一些简单的东西,可能是一个值为1的无符号int)
- 服务器->客户端:最新帧信息。(640x480图像存储在具有RGB字节值的字节阵列中)
- 客户端:在显示器上显示框架
ReadLine
挂起,因为它正在等待测试消息中不包含的换行符。
如果您希望服务器连续发送消息,只需在WriteFile
调用周围设置一个循环。您不需要连接多次。类似地,在客户端中,将循环放在ReadLine
周围。
如果每条消息都由换行符终止的文本组成,那么这就足够了,但如果你真的想让管道客户端以消息模式工作,你需要调用:
pipeClient.ReadMode = PipeTransmissionMode.Message;
然而,我怀疑这是否会与StreamReader
很好地交互。相反,您应该使用pipeClient.Read
阅读单个消息。
更新
回答您的新问题:
在服务器上,一旦客户端连接,就进入一个循环,其中:
- 服务器从客户端进行读取。这将被阻止,直到客户端请求帧为止
- 服务器发送一个帧
在客户端上,一旦连接到服务器,就进入一个循环,其中:
- 客户端发送"请发送帧"消息
- 客户端从服务器读取帧
- 客户端显示框架
我不会使用消息模式管道。如果帧的大小是固定的,那么客户端就知道要从服务器读取多少数据。否则,在帧之前添加一个包含其长度的uint。
这是一个使用命名管道将字符串从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();
}