[英]C++ named pipe client won't read more than 4096 bytes
我正在尝试用 C++ 实现一个 Windows 命名管道客户端,它将向用 Go 编写的命名管道服务器发送 RPC 请求。 这一切都适用于较短的服务器响应长度。 但是,如果服务器响应的长度超过 4096 字节,客户端将不会读取超过 4096 字节并且响应被缩短。 我在下面包含了客户端和服务器代码的最小可重现示例,为简洁起见,删除了大部分错误处理。 要重现该错误,请将服务器代码中的“一些大数据字符串”更改为约 5000 个字符的字符串。
我尝试了以下方法但没有任何运气:
任何建议将不胜感激。
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;
}
转到服务器代码:
//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)
}
}
我已经实施的解决方案如下:
cbReadPeek
)。 我发现 PeekNamedPipe 在调用一次时不是 100% 可靠的(它有时返回 0 字节读取,即使管道中有更多数据)。 因此,我将它实现为do {...} while ( fSuccess && cbReadPeek == 0 )
,因此它会循环直到有数据要读取,或者 fSuccess 因错误而失败(管道中没有更多数据)。if (GetLastError() == ERROR_BROKEN_PIPE) { break; }
if (GetLastError() == ERROR_BROKEN_PIPE) { break; }
无论如何,它似乎工作正常。 我仍然很想知道为什么 ReadFile 一次最多只能从管道中读取 4096 个字节。 我在 MS 文档中找不到任何解释这种行为的函数的内容,但也许我遗漏了一些明显的东西。 也许这个问题可以暂时悬而未决,让某人有机会看到它并提供一些见解?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.