2010-01-17 2 views
9

이진 파일을 원격 서버로 전송하려고합니다. 내 코드에 SUN/ONC RPC (Linux의 경우 rpcgen)를 사용하고 있습니다. 나는 서버와 클라이언트에 대한 코드를 작성했으며 텍스트 파일에도 사용할 수 있지만 바이너리 파일을 전송하려고하면 전송 후 파일이 손상되었다고한다. 문자 배열 또는 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

감사합니다. 나는이 포스트를보기 전에이 작품을 얻었다. 어쨌든가는 길, 나는 지금 정수 배열을 사용하고있다! –

0

이 문제가 어디에 당신을 말할 것이다, 전에 전송 후 파일을 비교 . hexdiff을 사용할 수 있습니다.

+0

루프가 반복 될 때마다 파일의 작은 조각이 나오는 것처럼 보입니다. 서버 측에서는 파일을 읽을 때 크기가 완벽하지만 클라이언트 측에서는 크기가 엉망입니다. 데이터의 약 절반 만 정확합니다! –

3

다소 늦었지만 문제의 해결책은 다음과 같습니다. Juste는 파일의 청크 저장 유형을 임의 바이트의 고정 길이 배열로 변경합니다. "불투명 한 filechunk [MAXLEN] typedef에 ,"(사실의 문제, 그것은 문자의 단지 고정 된 배열입니다 : 당신의 불투명 한 데이터를 사용할 수는, 그래서 당신의 IDL에서 대신 선언의 "타입 정의 문자열 MAXLEN > <을 filechunk")

추신 : 이런 종류의 데이터 (고정 배열)를 사용하면 변수를 파일로 읽거나 쓸 버퍼로 사용할 수 없습니다. 예를 들어, 함수 * retrieve_file_1_svc * 서버에서, 문

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

는 Madhusudan.CS 줄 것 정수를 사용하여 "솔루션"을 소유하고 그냥 나중에 참조 할 수 있도록

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

로 변경해야 다른 엔디 언을 가진 기계를 사용할 때 모든 종류의 재미를 느낄 수 있습니다. RPC는이 경우 정수를 변환하여 문자열 또는 이진 데이터를 망가 뜨려야합니다.

올바른 해결책은 '불투명'XDR 데이터 형식을 사용하는 것입니다. 바이트의 양에 대해 _len unsigned int 및 데이터를 가리킬 수있는 _var 포인터가있는 구조체를 만듭니다.