通过 SunRPC 发送带缓冲区的链表 (c)

Posted

技术标签:

【中文标题】通过 SunRPC 发送带缓冲区的链表 (c)【英文标题】:Sending linked list with buffers over SunRPC (c) 【发布时间】:2015-10-19 09:31:19 【问题描述】:

在过去的几天里,我一直在为以下问题头疼:我想通过 SunRPC 发送任何大小/类型的文件。在客户端,我将文件读入一个链表,每个节点包含一个大小为 1024 的缓冲区。如果我在通过 RPC 发送之前打印所有缓冲区,我会得到正确的输出(即打印整个文件)。但是,当我通过 RPC 发送它并在服务器端打印所有缓冲区时,最后一个节点打印不正确,就好像它没有正确传输一样。

我用一个大的 .txt 文件 (100,5kB) 测试了我的代码,在这里它似乎确实有效。但是对于 .pdf 文件 (7,9kB) 则不然。

这是我的(相关)代码:

.x 文件:

struct node 
    string buf<>;
    long bytes;
    struct node* next;
;

struct paper 
    string author<>;
    string title<>;
    struct node* file;
    struct paper* next;
;

typedef long add_out;

program PAPERSERVER_PROG 
    version PAPERSERVER_VERS 
        add_out ADDPAPER(paper) = 1;
     = 1;
 = 0x20001234;

paperclient.c:(当我在这个函数的末尾打印所有缓冲区时,输出是正确的)

#define BUF_SIZE 1024

void add_paper(char** argv, CLIENT* cl) 
    struct paper in;
    add_out *out;
    struct node* tmp;
    struct node* tmp2;
    struct node start;
    char buf[BUF_SIZE];
    size_t read;

    // Open the file
    FILE *fp = fopen(argv[5], "rb");
    if (fp == NULL) 
        perror("fopen");
        exit(EXIT_FAILURE);
    

    in.author = strdup(argv[3]);
    in.title = strdup(argv[4]);
    in.file = &start;
    in.next = NULL;

    bzero(buf, BUF_SIZE);
    read = fread(buf, 1, BUF_SIZE, fp);
    if (ferror(fp)) 
        perror("fread error");
        exit(2);
    

    tmp = &start;
    tmp->buf = calloc(BUF_SIZE, 1);
    if (tmp->buf == NULL) 
        perror("malloc tmp->buf");
        exit(2);
    

    tmp->bytes = read;
    tmp->buf = memcpy(tmp->buf, buf, read);

    // Cycle until end of file
    while (!feof(fp)) 
        bzero(buf, BUF_SIZE);

        tmp->next = (struct node*) calloc(sizeof(struct node), 1);
        if (tmp->next == NULL) 
            perror("malloc tmp->next");
            exit(2);
        

        tmp = tmp->next;

        read = fread(buf, 1, BUF_SIZE, fp);
        if (ferror(fp)) 
            perror("fread error");
            exit(2);
        

        printf("read:%zu\n", read);

        tmp->buf = calloc(BUF_SIZE+1, 1);
        if (tmp->buf == NULL) 
            perror("malloc tmp->buf");
            exit(2);
        

        tmp->bytes = read;
        tmp->buf = memcpy(tmp->buf, buf, read);      
    
    tmp->next = NULL;

    out = addpaper_1(&in, cl);
    if (out == NULL) 
        printf("Error: %s\n", clnt_sperror(cl, argv[1]));
     else 
        printf("%ld\n", *out);
    

    clnt_destroy(cl);

paperserver.c(这里输出错误)

add_out* addpaper_1_svc(paper* in, struct svc_req* rqstp)  
    static add_out out;
    struct node* file;
    long written;

    file = in->file;
    char* buf;

    while (file != NULL) 
        buf = calloc(BUF_SIZE + 1, 1);
        buf = memcpy(buf, file->buf, file->bytes);

        written = fwrite(buf, 1, file->bytes, stdout);

        if (ferror(stdout)) 
            perror("fwrite error");
            exit(2);
         

        file = file->next;
    

    out = 0;

    return &out;

编辑:

详细说明我的预期输出。我希望我通过服务器发送的文件的输出与我调用cat &lt;filename&gt; 完全相同。但是,输出似乎顺序错误。最后一个缓冲区似乎被文件开头的某些内容覆盖。这是使用一个 .pdf 文件。使用另一个 .pdf 可以正常工作,使用 .txt 可以正常工作,但使用 .jpg 就不行。当我得到输出的md5sum 时,它会随着每次调用而改变。这让我觉得它打印的字节实际上没有被我的程序初始化。此外,当我使用 valgrind 时,会出现以下错误(仅适用于无法正常工作的文件):

==2375== Invalid read of size 8
==2375==    at 0x4C2F790: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2375==    by 0x400D3E: addpaper_1_svc (paperserver.c:38)
==2375==    by 0x40128D: paperserver_prog_1 (paperserver_svc.c:76)
==2375==    by 0x4F626D0: svc_getreq_common (svc.c:534)
==2375==    by 0x4F6281D: svc_getreq_poll (svc.c:460)
==2375==    by 0x4F65BFE: svc_run (svc_run.c:96)
==2375==    by 0x401437: main (paperserver_svc.c:114)
==2375==  Address 0x527af48 is 392 bytes inside a block of size 394 alloc'd
==2375==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2375==    by 0x4F64D04: xdr_string (xdr.c:788)
==2375==    by 0x401487: xdr_node (paperserver_xdr.c:13)
==2375==    by 0x4F656B7: xdr_pointer (xdr_ref.c:84)
==2375==    by 0x40155C: xdr_paper (paperserver_xdr.c:31)
==2375==    by 0x401268: paperserver_prog_1 (paperserver_svc.c:72)
==2375==    by 0x4F626D0: svc_getreq_common (svc.c:534)
==2375==    by 0x4F6281D: svc_getreq_poll (svc.c:460)
==2375==    by 0x4F65BFE: svc_run (svc_run.c:96)
==2375==    by 0x401437: main (paperserver_svc.c:114)
==2375== 
==2375== Invalid read of size 8
==2375==    at 0x4C2F79E: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2375==    by 0x400D3E: addpaper_1_svc (paperserver.c:38)
==2375==    by 0x40128D: paperserver_prog_1 (paperserver_svc.c:76)
==2375==    by 0x4F626D0: svc_getreq_common (svc.c:534)
==2375==    by 0x4F6281D: svc_getreq_poll (svc.c:460)
==2375==    by 0x4F65BFE: svc_run (svc_run.c:96)
==2375==    by 0x401437: main (paperserver_svc.c:114)
==2375==  Address 0x527af50 is 6 bytes after a block of size 394 alloc'd
==2375==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2375==    by 0x4F64D04: xdr_string (xdr.c:788)
==2375==    by 0x401487: xdr_node (paperserver_xdr.c:13)
==2375==    by 0x4F656B7: xdr_pointer (xdr_ref.c:84)
==2375==    by 0x40155C: xdr_paper (paperserver_xdr.c:31)
==2375==    by 0x401268: paperserver_prog_1 (paperserver_svc.c:72)
==2375==    by 0x4F626D0: svc_getreq_common (svc.c:534)
==2375==    by 0x4F6281D: svc_getreq_poll (svc.c:460)
==2375==    by 0x4F65BFE: svc_run (svc_run.c:96)
==2375==    by 0x401437: main (paperserver_svc.c:114)  

【问题讨论】:

当您说“输出错误”时,您能说得更具体一点吗?你期望输出是什么?实际输出是多少? 顺便说一句,您可能想阅读"Why is “while ( !feof (file) )” always wrong?" 【参考方案1】:

您正在使用string buf&lt;&gt; 来包含文件数据。字符串将由第一个零字节终止,因此如果数据中间有任何零字节(PDF 文件可能会,但文本文件不会),则不会写入完整数据。

(请注意,RPC 无法知道 node 中有多少数据 - 您碰巧将这些数据放入 bytes,但 RPC 基础架构不知道...)。

要解决此问题,您需要将buf 改为opaque

opaque buf<>;

...并对 C 代码进行相应的更改,类似于

buf.buf_val = calloc(...);
buf.buf_len = num_bytes_read;

【讨论】:

以上是关于通过 SunRPC 发送带缓冲区的链表 (c)的主要内容,如果未能解决你的问题,请参考以下文章

[ 链表OJ题--C语言实现 ] 复制带随机指针的链表(带视频讲解哦)

LeetCode 138. 复制带随机指针的链表c++/java详细题解

带方括号的链表

利用IAT导入表进行代码的注入

利用IAT导入表进行代码的注入

105. 复制带随机指针的链表