我正试图开始使用命名管道,因为我将来需要在我的一个项目中使用它们。
目前,我有一个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件事。
读取整个消息 - 我试过在管道客户端上使用StreamReader来读取消息,但它在ReadLine()上无限期地挂起。
连续发送消息--我想让服务器发送一条又一条的消息到客户端,客户端一次读取一条并打印出来。我对IPC有点不了解,所以一开始我试着让客户端断开连接,然后在while(true)循环中重新连接到服务器,而服务器则在while true循环中,在发送另一条消息之前总是等待新的客户端连接。我的尝试就在上面的代码中。
如果有任何帮助,我将非常感激。最终的目的是将图片从服务器发送到客户端。客户端将它们实时打印到屏幕上。在尝试图像数据之前,我想用简单的字符串消息来实现这个功能。
EDIT:
最终我希望能够从客户端向服务器发送一条消息 表示它想获得最新的图像帧,然后服务器将发送最新的帧 客户端将在屏幕上显示。所以流程是
ReadLine
挂起,因为它正在等待一个换行,而你的测试消息并不包括这个换行。
如果你想让服务器连续发送消息,只需在你的测试消息中绕一个 WriteFile
呼叫。 你不需要连接多次。 同样,在客户端中,将循环放在 ReadLine
.
如果每条消息都是由一个换行结束的文本组成,那么这就足够了,但如果你真的想让管道客户端在消息模式下工作,你需要调用。
pipeClient.ReadMode = PipeTransmissionMode.Message;
然而,我怀疑这是否能与一个... StreamReader
. 相反,你应该使用 pipeClient.Read
.
更新
回答你的新问题。
在服务器上,一旦客户机连接完毕,就进入一个循环,其中:
在客户端,一旦连接到服务器,进入一个循环,其中:客户端发送一个 "请发送一个帧"。
我不会使用消息模式的管道。 如果帧的大小是固定的,那么客户端知道从服务器读取多少数据。 否则,在帧前加上一个包含其长度的uint。
这是一个简单的例子,使用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();
}