我编写了一个 C 服务器,它应该使用缓冲区范例将它通过 XDR 库请求的文件发送给客户端。该程序使用自定义结构。
enum tagtype {
GET = 0,
OK = 1,
QUIT = 2,
ERR = 3
};
struct file {
opaque contents<>;
unsigned int last_mod_time;
};
union message switch (tagtype tag) {
case GET:
string filename<256>;
case OK:
struct file fdata;
case QUIT:
void;
case ERR:
void;
};
这是 XDR 服务器的核心部分。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <rpc/xdr.h>
#include "xdr_file.h"
//main() and call to manage_clixrequest()
int manage_clixrequest(int clisocket_fd) {
FILE *req_file;
struct stat metadata;
char rcv_message[CHUNK_SIZE] = "";
char *buffer, buffer2[2048];
size_t nbytes = 0;
int sent, n;
XDR xdrs_r, xdrs_w; // Pointer to XDR streams
message msg, send_msg;
/* Create codification stream */
xdrmem_create(&xdrs_r, rcv_message, sizeof(rcv_message), XDR_DECODE);
memset(&msg, 0, sizeof(msg));
memset(&send_msg, 0, sizeof(send_msg));
msg.tag = ERR;
msg.message_u.filename = NULL;
msg.message_u.fdata.contents.contents_len = 0;
msg.message_u.fdata.contents.contents_val = NULL;
msg.message_u.fdata.last_mod_time = 0;
/* The server waits for data */
n = recv(clisocket_fd, rcv_message, sizeof(rcv_message), 0);
/* Check if the server received some data */
if (n < 0) {
printf("Errno %d in recv(): %s\n", errno, strerror(errno));
}
if(!xdr_message(&xdrs_r, &msg)){
printf("Error in xdr_message()\n");
return -1;
}
xdr_destroy(&xdrs_r);
/* For a GET message the function extracts the filename
* and send the corresponding file to the client */
if (msg.tag == GET) {
req_file = fopen(msg.message_u.filename, "rb");
/* Check if the file is valid */
if (req_file == NULL) {
if (errno == 2){
xdrmem_create(&xdrs_w, buffer, sizeof(buffer), XDR_ENCODE);
/* The requested file does not exist, so an error message is sent
* and the connection is going to be shutdown */
send_msg.tag = ERR;
if(!xdr_message(&xdrs_w, &send_msg)){
printf("Error in xdr_message()\n");
return -1;
}
sent = send(clisocket_fd, buffer, sizeof(buffer), 0);
/* Check if the server sent the data */
if (sent < 0) {
printf("Errno %d in send(): %s\n", errno, strerror(errno));
}
xdr_destroy(&xdrs_r);
xdr_destroy(&xdrs_w);
return 0;
} else {
printf("Errno %d in fopen(): %s\n", errno, strerror(errno));
return -1;
}
}
/* Check if the file metadata are correctly retrieved */
if (fstat(fileno(req_file), &metadata) < 0) {
printf("Errno %d in fstat(): %s\n", errno, strerror(errno));
return -1;
}
/* File is read and memorized in a temporary buffer */
buffer = (char *) malloc(sizeof(char)*metadata.st_size);
memset(buffer, 0, metadata.st_size);
nbytes = fread(buffer, sizeof(char), metadata.st_size, req_file);
memset(buffer2, 0, sizeof(buffer2));
/* Create codification stream */
xdrmem_create(&xdrs_w, buffer2, sizeof(buffer2), XDR_ENCODE);
/* This is the beginning of the message for the client
* when the file is going to be sent */
send_msg.tag = OK;
/* The file size and last modification time are retrieved */
send_msg.message_u.fdata.contents.contents_len= metadata.st_size;
send_msg.message_u.fdata.last_mod_time = metadata.st_mtime;
send_msg.message_u.filename = msg.message_u.filename;
send_msg.message_u.fdata.contents.contents_val = buffer;
if(!xdr_message(&xdrs_w, &send_msg)){
printf("Error in xdr_message()\n");
return -1;
}
/* The XDR struct is sent with the send() function */
sent = send(clisocket_fd, buffer2, nbytes, 0);
/* Check if the server sent the data */
if (sent < 0) {
printf("Errno %d in send(): %s\n", errno, strerror(errno));
return -1;
}
free(buffer);
xdr_destroy(&xdrs_w);
} else if (msg.tag == QUIT) {
/* The client asked to shutdown the connection.
* Unused if the client itself closes the connection */
return 0;
} else {
/* The client sent a request with a not expected tag, so an error message is sent
* and the connection is going to be shutdown*/
xdrmem_create(&xdrs_w, buffer, sizeof(buffer), XDR_ENCODE);
send_msg.tag = ERR;
if(!xdr_message(&xdrs_w, &send_msg)){
printf("Errno %d in xdr_message(): %s\n", errno, strerror(errno));
return -1;
}
sent = send(clisocket_fd, buffer, sizeof(buffer), 0);
/* Check if the server sent the data */
if (sent < 0) {
printf("Errno %d in send(): %s\n", errno, strerror(errno));
}
xdr_destroy(&xdrs_w);
return 0;
}
return 1;
}
不幸的是,消息的编码会导致分段错误。
Program received signal SIGSEGV, Segmentation fault.
xdrmem_putlong (xdrs=0x7fffffffd050, lp=0x7fffffffcf58) at xdr_mem.c:124
124 xdr_mem.c: No such file or directory.
我用 GDB 检查了程序,我得到了这个回溯
(gdb) backtrace
#0 xdrmem_putlong (xdrs=0x7fffffffd050, lp=0x7fffffffcf58)
at xdr_mem.c:124
#1 0x00007ffff7b4d99c in __GI_xdr_enum (xdrs=<optimized out>,
ep=0x7fffffffd000) at xdr.c:500
#2 0x000055555555617e in xdr_tagtype (xdrs=0x7fffffffd050,
objp=0x7fffffffd000) at xdr_file.c:13
#3 0x0000555555556214 in xdr_message (xdrs=0x7fffffffd050,
objp=0x7fffffffd000) at xdr_file.c:35
#4 0x0000555555555847 in manage_clixrequest (clisocket_fd=4)
at server3-4.c:273
#5 0x00005555555552a6 in main (argc=3, argv=0x7fffffffdd78)
at server3-4.c:129
其中 manage_clixrequest(socket_fd) 是我编写的函数。我不知道我是否对 XDR 库使用不当,或者我是否忘记包含某些内容以致 xdr_mem.c 未正确链接。你有什么想法/提示吗?
编辑 我报告了整个服务器功能和 xdr_file.c。
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "xdr_file.h"
bool_t
xdr_tagtype (XDR *xdrs, tagtype *objp)
{
register int32_t *buf;
if (!xdr_enum (xdrs, (enum_t *) objp))
return FALSE;
return TRUE;
}
bool_t
xdr_file (XDR *xdrs, file *objp)
{
register int32_t *buf;
if (!xdr_bytes (xdrs, (char **)&objp->contents.contents_val, (u_int *) &objp->contents.contents_len, ~0))
return FALSE;
if (!xdr_u_int (xdrs, &objp->last_mod_time))
return FALSE;
return TRUE;
}
bool_t
xdr_message (XDR *xdrs, message *objp)
{
register int32_t *buf;
if (!xdr_tagtype (xdrs, &objp->tag))
return FALSE;
switch (objp->tag) {
case GET:
if (!xdr_string (xdrs, &objp->message_u.filename, 256))
return FALSE;
break;
case OK:
if (!xdr_file (xdrs, &objp->message_u.fdata))
return FALSE;
break;
case QUIT:
break;
case ERR:
break;
default:
return FALSE;
}
return TRUE;
}
我发现我使用相同的缓冲区来存储文件字节并创建 XDR 流,这是一个错误。现在没有分段错误并且message
正确填充了 XDR 结构,但是现在对生成的函数的调用xdr_bytes()
返回 false 并且编码仍然失败。