我正在尝试用C ++实现Windows命名管道客户端,它将RPC请求发送到用Go编写的命名管道服务器。所有这些都适用于较短的服务器响应长度。但是,如果服务器响应的长度超过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)
}
}
答案 0 :(得分:0)
我实现的解决方案如下:
cbReadPeek
)。我发现一次调用PeekNamedPipe并不是100%可靠的(即使管道中有更多数据,有时也会返回读取的0字节)。为此,我将其实现为do {...} while ( fSuccess && cbReadPeek == 0 )
,因此它将循环运行,直到有要读取的数据,或者fSuccess失败并出现错误(管道中没有更多数据)。 if (GetLastError() == ERROR_BROKEN_PIPE) { break; }
无论如何,它似乎工作正常。我仍然很想知道为什么ReadFile一次只能从管道读取最多4096个字节。我在MS文档中找不到用于解释此行为的函数的任何内容,但也许我缺少明显的内容。也许这个问题可以搁置一段时间,让某人有机会看到它并提供一些见识?