首页 新闻 会员 周边 捐助

c++ 命名管道有问题?

0
悬赏园豆:30 [已解决问题] 解决于 2014-02-28 10:37

我就是想实现,客户端连接上后,无论客户端发送多少数据我都可以存下来,比如发送1G的数据,但我网上找到的例子都是两端约定发送多长字节,不知道有没有办法实现?我的服务端代码如下:

#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <ctime>
using namespace std;

#define BUFSIZE 5

void start(string pipeName)
{
    BOOL fSuccess = false;
    char buffer[BUFSIZE];
    DWORD len = 0;
    string result = "";

    HANDLE hPipe = CreateNamedPipe(pipeName.c_str(),PIPE_ACCESS_DUPLEX,PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT
        ,PIPE_UNLIMITED_INSTANCES,0,0,NMPWAIT_WAIT_FOREVER,0);

    //waiting to be connected
    if(ConnectNamedPipe(hPipe, NULL) == NULL)
        return;

    len = 0;

    do
    {
        fSuccess = ReadFile(hPipe ,buffer ,BUFSIZE*sizeof(char) ,&len ,NULL);

        cout<<buffer;

        if ( ! fSuccess && GetLastError() != ERROR_MORE_DATA )
         break; 

    } while(fSuccess);

    cout<<"read completed.";

    CloseHandle(hPipe);
}

int main(int argc, char* argv[])
{
    start("\\\\.\\Pipe\\mypipe");
    int c = getchar();
    return 0;
}

上面的代码中我虽然设置了BUFSIZE=5,但如果客户端发送过来的数据长度为8,它第一次循环中buffer接收到的数据长度竟然大于5,buffer[5]中装了5个有效字符,后面几个乱码字符,转换成string也确实有乱码,前5位正常,后几位是乱码。每次调用ReadFile后len的值都为5,按我的理解第二次len的值应该是3才对,所以我根本没办法区分哪此才是有效数据。

如果要接收的数据大于1G,岂非要定义一个大于1G的变量才能搞定吗? 主要是上面的循环如何把接收到的数据累加到result中,如何判断循环的终结等,c++中的字符串是我十几年来最搞不太懂的地方。

空明流光的主页 空明流光 | 初学一级 | 园豆:111
提问于:2014-02-27 21:27
< >
分享
最佳答案
0

http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx

 

If a named pipe is being read in message mode and the next message is longer than the        nNumberOfBytesToRead parameter specifies,        ReadFile returns FALSE and        GetLastError returns        ERROR_MORE_DATA. The remainder of the message can be read by a subsequent call to the        ReadFile or        PeekNamedPipefunction.

If the lpNumberOfBytesRead parameter is zero when        ReadFile returns TRUE on a pipe,       the other end of the pipe called the WriteFile function with        nNumberOfBytesToWrite set to zero.

 

除此之外,关于如何使用协议切割数据流,可以参照 HTTP 协议的定义方式。

 

关于 C++ 中字符串的问题,char buffer[5],表示长度为 5 的字符数组,可以和 C# 中的长度为 5 的 byte 数组同等对待。如果你对 C# 中的 Encoding 比较熟悉的话,理解起来不是问题。

收获园豆:30
Launcher | 高人七级 |园豆:45050 | 2014-02-28 09:32

那比如我上面循环中的第一次循环中在接收到buffer[5]后, 我执行了 string str = string(buffer)后,str的长度大于5,它的前5位是有效的,后三位是乱码。

请问怎么能把buffer[5]转换成可输出的正确可靠的数据?

空明流光 | 园豆:111 (初学一级) | 2014-02-28 09:54

@沧海一杰: 我假定你是使用的 std::string 类型,那么它其中一个构造函数的形参是:const char * _Ptr,那么你会发现这里有个问题,就是没有指定 _Ptr 指向的字符串的长度。这是很危险的操作,会造成缓冲区溢出。因为在这里,有个默认的约定,就是假设 _Ptr 是指向的以 '\0' 结束的字符串。那么 std::string 在构造的时候,会从 _Ptr 开始识别,直到遇到 '\0' 后,将前面的所有字符识别为一个字符串。

你可以在内存窗口中观察 buffer 变量的内容,你会发现几乎在所有情况下,buffer[6] 的值都不等于 '\0',因此你才会得到一个错误的字符串,正确的做法是,在你认为 buffer 里内容表示的是一个字符串时,你需要追加 '\0',如下:

char buff[6]={0};

memcpy(buff,buffer,5);

std::string str(buff);

也就是说容纳字符串的数组总是字符串长度加 1 的大小。

关于正确可靠的数据的问题,这和你调用双方的约定有关,也就是说你的 buffer 中存储的到底是以什么编码方式编码的字符串。

Launcher | 园豆:45050 (高人七级) | 2014-02-28 10:24

@Launcher: 我明白了,我原也是知道这些,但C++字符串类型经常多达十几种,char* char[] LPCSTR LPWCSTR string Cstring TChar 经常被弄晕。 我也发现了,刚才我接收时发现循环走了四次,是因为我的发送端计算发送数据长度的语句有问题,导致多发了两次无效数据导致。

空明流光 | 园豆:111 (初学一级) | 2014-02-28 10:33
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册