2010-01-17 23 views
9

Chcę przenieść pliki binarne na serwer zdalny. Używam SUN/ONC RPC (rpcgen w systemie Linux) dla mojego kodu. Używam C. Mam napisany kod dla serwera i klienta i działa on dla plików tekstowych, ale kiedy próbuję przesłać pliki binarne, oznacza to, że plik jest uszkodzony po przesłaniu. Przechowuję fragmenty danych w łańcuchach znaków lub ciągach XDR. Myślę, że istnieje pewien problem z przechowywaniem danych jako tablicy znaków. Czy ktoś może mi powiedzieć, jaki jest problem? Czy ktoś może mi pomóc?Sun RPC: przenoszenie plików binarnych

Załączam tu swoje fragmenty kodu w celach informacyjnych, jeśli ktoś chce rzucić okiem na to, co robię.

Moja 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; 

mój serwer:

#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; 
} 

Mój Klient:

#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; 
} 

Odpowiedz

4

Łańcuchy XDR są zakończone znakiem null. Aby przesłać dane binarne, musisz użyć innego rodzaju danych - prawdopodobnie "tablicy bajtów". Zobacz na przykład ten document w Słońcu.

+0

Dzięki. Miałem to działa, zanim zobaczyłem ten post. Tak czy inaczej to jest droga, teraz używam tablicy liczb całkowitych! –

0

Porównaj plików przed i po transferze, który powie Ci, gdzie jest problem . Możesz użyć do tego celu hexdiff.

+0

Wygląda na to, że tylko mała porcja pliku przychodzi podczas każdej iteracji pętli. Po stronie serwera, gdy czyta plik, rozmiary są idealne, ale gdy są odbierane po stronie klienta, wszystko jest pomieszane. Tylko około połowa danych jest poprawna! –

3

Trochę za późno, ale tutaj jest rozwiązanie twojego problemu: Juste zmienić typ przechowywania fragmentu pliku na stałą długość tablicy z dowolnych bajtów . Więc w IDL, zamiast deklaracji „typedef ciąg filechunk <MaxLen>;” Twój może korzystać z danych matowe: „typedef nieprzezroczystego filechunk [MaxLen];” (W rzeczywistości, to jest po prostu stałą tablicę char)

PS: Tego rodzaju dane (stałe tablice) uniemożliwiają użycie zmiennej jako bufora do odczytu lub zapisu z pliku. Na przykład w funkcji * retrieve_file_1_svc * z serwera, oświadczenia

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

muszą być zmienione na

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

Tak na przyszłość, Madhusudan.CS właścicielem „rozwiązanie” za pomocą liczb całkowitych da masz mnóstwo zabawy podczas używania maszyn o różnej endianness. RPC powinien tłumaczyć liczby całkowite w tym przypadku, fałszując dane ciągowe lub binarne.

Prawidłowym rozwiązaniem jest użycie "nieprzejrzystego" typu danych XDR. Tworzy strukturę z _ unsigned int dla ilości bajtów i wskaźnikiem _var, który możesz wskazać na swoje dane.

Powiązane problemy