通过 SunRPC 发送带缓冲区的链表 (c)
Sending linked list with buffers over SunRPC (c)
过去几天我一直在为以下问题伤脑筋:我想通过 SunRPC 发送任何 size/type 的文件。在客户端,我将文件读入一个链表,每个节点包含一个大小为 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 <filename>
时完全相同。但是,输出的顺序似乎错误。最后一个缓冲区似乎被文件开头的一些内容覆盖了。这是使用一个 .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)
您正在使用 string buf<>
来包含文件数据。字符串将由第一个零字节终止,因此如果数据中间有任何零字节(PDF 文件可能会有,但文本文件不会),则不会写入完整数据。
(请注意,RPC 无法知道 node
中有多少数据 - 您碰巧将该数据放入 bytes
,但 RPC 基础结构不知道。 ..).
要解决问题,您需要将buf
改为opaque
:
opaque buf<>;
...并根据
对 C 代码进行相应的更改
buf.buf_val = calloc(...);
buf.buf_len = num_bytes_read;
过去几天我一直在为以下问题伤脑筋:我想通过 SunRPC 发送任何 size/type 的文件。在客户端,我将文件读入一个链表,每个节点包含一个大小为 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 <filename>
时完全相同。但是,输出的顺序似乎错误。最后一个缓冲区似乎被文件开头的一些内容覆盖了。这是使用一个 .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)
您正在使用 string buf<>
来包含文件数据。字符串将由第一个零字节终止,因此如果数据中间有任何零字节(PDF 文件可能会有,但文本文件不会),则不会写入完整数据。
(请注意,RPC 无法知道 node
中有多少数据 - 您碰巧将该数据放入 bytes
,但 RPC 基础结构不知道。 ..).
要解决问题,您需要将buf
改为opaque
:
opaque buf<>;
...并根据
对 C 代码进行相应的更改buf.buf_val = calloc(...);
buf.buf_len = num_bytes_read;