0

我编写了一个 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 并且编码仍然失败。

4

0 回答 0