[英]C++ named pipe client won't read more than 4096 bytes
I am trying to implement a Windows named pipe client in C++, that will send RPC requests to a named pipe server written in Go.我正在尝试用 C++ 实现一个 Windows 命名管道客户端,它将向用 Go 编写的命名管道服务器发送 RPC 请求。 It all works for short server response lengths.
这一切都适用于较短的服务器响应长度。 However if the length of the server response exceeds 4096 bytes, the client will not read past 4096 bytes and the response is cut short.
但是,如果服务器响应的长度超过 4096 字节,客户端将不会读取超过 4096 字节并且响应被缩短。 I have included a minimal reproducible example of the client and server code below, with most of the error handling removed for brevity.
我在下面包含了客户端和服务器代码的最小可重现示例,为简洁起见,删除了大部分错误处理。 To reproduce the error, change "some large data string" in the server code into a string of ~5000 characters.
要重现该错误,请将服务器代码中的“一些大数据字符串”更改为约 5000 个字符的字符串。
I have tried the following without any luck:我尝试了以下方法但没有任何运气:
Any advice would be most appreciated.任何建议将不胜感激。
C++ client code: C++客户端代码:
//Minimal implementation of C++ named pipe client. Most error handling removed for brevity.
//Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-client
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#define BUFSIZE 1048576
int _tmain(int argc, TCHAR *argv[])
{
HANDLE hPipe;
const char *lpvMessage="POST / HTTP/1.0\r\nHost: localhost\r\nContent-Length: 33\r\n\r\n{\"method\":\"test\",\"params\":[\"\"]}\r\n\n";
char chBuf[BUFSIZE];
BOOL fSuccess = FALSE;
DWORD cbRead, cbToWrite, cbWritten, dwMode;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mypipe.ipc");
// Try to open a named pipe then close it - attempt 1.
while (1)
{
hPipe = CreateFile(
lpszPipename, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
// Break if the pipe handle is valid.
if (hPipe != INVALID_HANDLE_VALUE)
break;
// Exit if an error occurs.
_tprintf( TEXT("Could not open pipe. GLE=%d\n"), GetLastError() );
return -1;
}
CloseHandle(hPipe);
// If successful, open pipe again for use. For some reason, pipe must be opened and closed once (attempt 1) before actually using.
hPipe = CreateFile(
lpszPipename, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
// The pipe connected; change to message-read mode.
dwMode = PIPE_READMODE_MESSAGE; //PIPE_READMODE_BYTE doesn't solve the problem either;
fSuccess = SetNamedPipeHandleState(
hPipe, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
if ( ! fSuccess)
{
_tprintf( TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError() );
return -1;
}
// Send a message to the pipe server.
cbToWrite = (lstrlen(lpvMessage)+1)*sizeof(char);
fSuccess = WriteFile(
hPipe, // pipe handle
lpvMessage, // message
cbToWrite, // message length
&cbWritten, // bytes written
NULL); // not overlapped
do
{
// Read from the pipe.
fSuccess = ReadFile(
hPipe, // pipe handle
chBuf, // buffer to receive reply
BUFSIZE*sizeof(char), // size of buffer
&cbRead, // number of bytes read
NULL); // not overlapped
if ( ! fSuccess && GetLastError() != ERROR_MORE_DATA )
break;
printf(chBuf);
} while ( ! fSuccess); // repeat loop if ERROR_MORE_DATA
printf("\n<End of message, press ENTER to terminate connection and exit>");
_getch();
CloseHandle(hPipe);
return 0;
}
Go server code:转到服务器代码:
//Minimal implementation of Golang named pipe server. Most error handling removed for brevity.
// +build windows
package main
import (
"github.com/Microsoft/go-winio"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
)
func main() {
log.Print("Starting IPC server...")
StartIPCServer()
}
func HandleDefault(w http.ResponseWriter, req *http.Request) {
body, _ := ioutil.ReadAll(io.LimitReader(req.Body, 1048576))
defer req.Body.Close()
log.Printf("Received: '%q'", string(body))
response:= "some large data string" //If length of response plus http headers >4096 bytes, client will not read past 4096.
io.WriteString(w, response)
}
func serve(l net.Listener) error {
http.HandleFunc("/", HandleDefault)
return http.Serve(l, nil)
}
func StartIPCServer() {
var c winio.PipeConfig
c.SecurityDescriptor = ""
c.MessageMode = true //changing to false (byte mode) does not solve the problem.
c.InputBufferSize = 1048576
c.OutputBufferSize = 1048576
path:= `\\.\pipe\mypipe.ipc`
listener, err := winio.ListenPipe(path, &c)
log.Print("IPC server running!")
defer listener.Close()
err = serve(listener)
if err != nil {
log.Fatalf("Serve: %v", err)
os.Exit(1)
}
}
The solution I have implemented is as follows:我已经实施的解决方案如下:
cbReadPeek
in my case) for the next call to ReadFile.cbReadPeek
)。 I have found that PeekNamedPipe is not 100% reliable when called once (it sometimes returns 0 bytes read, even when there is more data in the pipe).do {...} while ( fSuccess && cbReadPeek == 0 )
, so it loops until there is either data to read, or fSuccess fails with an error (no more data in the pipe).do {...} while ( fSuccess && cbReadPeek == 0 )
,因此它会循环直到有数据要读取,或者 fSuccess 因错误而失败(管道中没有更多数据)。if (GetLastError() == ERROR_BROKEN_PIPE) { break; }
if (GetLastError() == ERROR_BROKEN_PIPE) { break; }
if (GetLastError() == ERROR_BROKEN_PIPE) { break; }
Anyway, it appears to be working fine.无论如何,它似乎工作正常。 I would still really like to know why ReadFile only reads a maximum of 4096 bytes at a time from the pipe.
我仍然很想知道为什么 ReadFile 一次最多只能从管道中读取 4096 个字节。 I could not find anything in the MS documentation for the function to explain this behaviour, but maybe I'm missing something obvious.
我在 MS 文档中找不到任何解释这种行为的函数的内容,但也许我遗漏了一些明显的东西。 Perhaps this question could be left open for a while to give someone a chance to see it and provide some insight?
也许这个问题可以暂时悬而未决,让某人有机会看到它并提供一些见解?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.