2010-01-17 65 views
9

我想將二進制文件傳輸到遠程服務器。我正在使用SUN/ONC RPC(Linux上的rpcgen)代碼。我正在使用C.我已經編寫了代碼服務器和客戶端,它適用於文本文件,但是當我嘗試傳輸二進制文件時,它說傳輸後文件已損壞。我將數據塊存儲在字符數組或XDR字符串中。我認爲將數據存儲爲字符數組存在一些問題。有人可以告訴我問題是什麼嗎?有人可以幫幫我嗎?Sun RPC:傳輸二進制文件

我附上我的代碼片段供參考,如果有人想看看我在做什麼。

我的IDL:

const MAXLEN = 1024; 

/* 
* Type for storing path 
*/ 
typedef string filename<MAXLEN>; 

/* 
* Structure for sending request. Expects the path of the file 
* and the byte number at which to start reading the file from 
*/ 
struct request { 
    filename name; 
    int start; 
}; 

/* 
* Type that represents the structute for request 
*/ 
typedef struct request request; 

/* 
* Type for storing a chunk of the file that is being 
* sent from the server to the client in the current 
* remote procedure call 
*/ 
typedef string filechunk<MAXLEN>; 

/* 
* Response sent by the server to the client as a response 
* to remote procedure call, containing the filechunk for 
* the current call and number of bytes actually read 
*/ 
struct chunkreceive { 
    filechunk data; 
    int bytes; 
}; 

/* 
* Type that represents the structure for file's chunks 
* to be received from the server 
*/ 
typedef struct chunkreceive chunkreceive; 

/* 
* File data sent by the server from client to store 
* it on the server along with the filename and the 
* number of bytes in the data 
*/ 
struct chunksend { 
    filename name; 
    filechunk data; 
    int bytes; 
}; 

/* 
* Type that represents the structure for file's chunks 
* to be sent to the server 
*/ 
typedef struct chunksend chunksend; 

/* 
* union for returning from remote procedure call, returns 
* the proper chunkdata response if everything worked fine 
* or will return the error number if an error occured 
*/ 
union readfile_res switch (int errno) { 
    case 0: 
     chunkreceive chunk; 
    default: 
     void; 
}; 

/* 
* Remote procedure defined in the Interface Definition Language 
* of SUN RPC, contains PROGRAM and VERSION name definitions and 
* the remote procedure signature 
*/ 
program FTPPROG { 
    version FTPVER { 
     readfile_res retrieve_file(request *) = 1; 
     int send_file(chunksend *) = 2; 
    } = 1; 
} = 0x20000011; 

我的服務器:

#include <rpc/rpc.h> 
#include <stdio.h> 
#include "ftp.h" 

extern __thread int errno; 

readfile_res* retrieve_file_1_svc(request *req, struct svc_req *rqstp) 
{ 
    FILE *file; 
    char data[1024]; 
    int bytes; 
    static readfile_res res; 

    file = fopen(req->name, "rb"); 
    if (file == NULL) { 
     res.errno = errno; 
     return (&res); 
    } 

    fseek (file, req->start, SEEK_SET); 
    bytes = fread(data, 1, 1024, file); 

    res.readfile_res_u.chunk.data = data; 
    res.readfile_res_u.chunk.bytes = bytes; 

    /* 
    * Return the result 
    */ 
    res.errno = 0; 
    fclose(file); 
    return (&res); 
} 

int* send_file_1_svc(chunksend *rec, struct svc_req *rqstp) 
{ 
    FILE *file; 
    int write_bytes; 
    static int result; 

    file = fopen(rec->name, "a"); 
    if (file == NULL) { 
     result = errno; 
     return &result; 
    } 

    write_bytes = fwrite(rec->data, 1, rec->bytes, file); 
    fclose(file); 

    result = 0; 
    return &result; 
} 

我的客戶:

#include <rpc/rpc.h> 
#include <stdio.h> 
#include <string.h> 
#include "ftp.h" 

extern __thread int errno; 

int get_file(char *host, char *name) 
{ 
    CLIENT *clnt; 
    int total_bytes = 0, write_bytes; 
    readfile_res *result; 
    request req; 
    FILE *file; 

    req.name = name; 
    req.start = 0; 

    /* 
    * Create client handle used for calling FTPPROG on 
    * the server designated on the command line. Use 
    * the tcp protocol when contacting the server. 
    */ 
    clnt = clnt_create(host, FTPPROG, FTPVER, "tcp"); 
    if (clnt == NULL) { 
     /* 
     * Couldn't establish connection with server. 
     * Print error message and stop. 
     */ 
     clnt_pcreateerror(host); 
     exit(1); 
    } 

    file = fopen(name, "wb"); 

    /* 
    * Call the remote procedure readdir on the server 
    */ 
    while (1) { 
     req.start = total_bytes; 
     result = retrieve_file_1(&req, clnt); 
     if (result == NULL) { 
      /* 
      * An RPC error occurred while calling the server. 
      * Print error message and stop. 
      */ 
      clnt_perror(clnt, host); 
      exit(1); 
     } 

     /* 
     * Okay, we successfully called the remote procedure. 
     */ 
     if (result->errno != 0) { 
      /* 
      * A remote system error occurred. 
      * Print error message and stop. 
      */ 
      errno = result->errno; 
      perror(name); 
      exit(1); 
     } 

     /* 
     * Successfully got a chunk of the file. 
     * Write into our local file. 
     */ 
     write_bytes = fwrite(result->readfile_res_u.chunk.data, 1, result->readfile_res_u.chunk.bytes, file); 
     total_bytes += result->readfile_res_u.chunk.bytes; 
     if (result->readfile_res_u.chunk.bytes < MAXLEN) 
      break; 
    } 

    fclose(file); 

    return 0; 
} 

int put_file(char *host, char *name) 
{ 
    CLIENT *clnt; 
    char data[1024]; 
    int total_bytes = 0, read_bytes; 
    int *result; 
    chunksend chunk; 
    FILE *file; 

    /* 
    * Create client handle used for calling FTPPROG on 
    * the server designated on the command line. Use 
    * the tcp protocol when contacting the server. 
    */ 
    clnt = clnt_create(host, FTPPROG, FTPVER, "tcp"); 
    if (clnt == NULL) { 
     /* 
     * Couldn't establish connection with server. 
     * Print error message and stop. 
     */ 
     clnt_pcreateerror(host); 
     exit(1); 
    } 

    file = fopen(name, "r"); 

    chunk.name = name; 

    /* 
    * Call the remote procedure readdir on the server 
    */ 
    while (1) { 
     read_bytes = fread(data, 1, MAXLEN, file); 
     total_bytes += read_bytes; 

     chunk.data = data; 
     chunk.bytes = read_bytes; 
     result = send_file_1(&chunk, clnt); 

     if (result == NULL) { 
      /* 
      * An RPC error occurred while calling the server. 
      * Print error message and stop. 
      */ 
      clnt_perror(clnt, host); 
      exit(1); 
     } 

     /* 
     * Okay, we successfully called the remote procedure. 
     */ 
     if (*result != 0) { 
      /* 
      * A remote system error occurred. 
      * Print error message and stop. 
      */ 
      errno = *result; 
      perror(name); 
      exit(1); 
     } 

     /* 
     * Successfully got a chunk of the file. 
     * Write into our local file. 
     */ 
     if (read_bytes < MAXLEN) 
      break; 
    } 

    fclose(file); 

    return 0; 
} 

int read_command(char *host) 
{ 
    char command[MAXLEN], filepath[MAXLEN]; 

    printf("> "); 
    fflush(stdin); 
    scanf("%s %s", command, filepath); 

    if (strcmp(command, "get") == 0) { 
     return get_file(host, filepath); 
    } else if(strcmp(command, "put") == 0){ 
     return put_file(host, filepath); 
    } else if(strcmp(command, "exit") == 0){ 
     exit(0); 
    } else { 
     return -1; 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    int result; 

    if (argc != 2) { 
     fprintf(stderr, "usage: %s host\n", argv[0]); 
     exit(1); 
    } 

    while(TRUE) { 
     result = read_command(argv[1]); 
    } 

    return 0; 
} 

回答

4

XDR字符串以null結尾。您需要使用不同的數據類型來傳輸二進制數據 - 可能是「字節數組」。例如,參見Sun的document

+0

謝謝。在看到這篇文章之前,我已經完成了這項工作。無論如何這就是要走的路,我現在使用整數數組! – 2010-01-18 17:37:41

0

比較文件之前和轉移後,會告訴你哪裏出了問題。 您可以使用hexdiff

+0

似乎只有一小塊文件即將在循環的每次迭代期間到來。在服務器端,當它讀取文件時,尺寸是完美的,但是當它在客戶端被接收時,它們都會搞砸。只有大約一半的數據是正確的! – 2010-01-17 19:08:44

3

有點晚了,我GESS但這裏是你的問題的解決方案: 中庸之道更改類型用於存儲文件的大塊的 任意字節固定長度的數組。 所以在你的IDL,而不是聲明「類型定義字符串filechunk <MAXLEN>;」您可以使用不透明的數據:「的typedef不透明filechunk [MAXLEN];」(實際上的問題,它是焦炭的只是一個固定陣列)

PS:這種數據(固定數組)阻止您使用變量作爲緩衝區來讀取或寫入文件。舉例來說,在功能* retrieve_file_1_svc *從你的服務器,報表

*bytes = fread(data, 1, 1024, file); 
res.readfile_res_u.chunk.data = data;* 

必須改變以

*bytes = fread(res.readfile_res_u.chunk.data, 1, 1024, file);* 
1

只是以供將來參考,Madhusudan.CS自己的「解決方案」使用整數會給您在使用不同字母順序的機器時可享受各種樂趣。在這種情況下,RPC應該轉換整數,把你的字符串或二進制數據弄糟。

正確的解決方案是使用'不透明'XDR數據類型。它將創建一個帶有_len無符號整數的字節數的結構,以及一個可以指向數據的_var指針。