2012-06-28 10 views
10

Próbuję wykonać transfer plików między serwerem a klientem, ale działa bardzo źle. Zasadniczo, co musi się stać:
1) Klient wysyła plik txt na serwer (nazwałem go "quotidiani.txt")
2) Serwer zapisuje go w innym pliku txt ("receive.txt")
3) Serwer uruchamia skrypt, który go modyfikuje i zapisuje pod inną nazwą ("output.txt").
4) Serwer odsyła plik do klienta, który zapisuje go (w tym samym gnieździe) z name (final.txt)Serwer przesyłania plików/klient za pomocą gniazda

Problem polega na tym, że pierwszy plik (quotidiani.txt) jest czytany tylko na niewielką część, a następnie pojawiają się błędy. Chciałbym, aby ktoś pomógł mi zrozumieć i poprawić moje błędy.

Oto mój kod:

client.c:

#include <stdlib.h> 
#include <stdio.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/wait.h> 
#include <sys/socket.h> 
#include <signal.h> 
#include <ctype.h>   
#include <arpa/inet.h> 
#include <netdb.h> 

#define PORT 20000 
#define LENGTH 512 


void error(const char *msg) 
{ 
    perror(msg); 
    exit(1); 
} 

int main(int argc, char *argv[]) 
{ 
    /* Variable Definition */ 
    int sockfd; 
    int nsockfd; 
    char revbuf[LENGTH]; 
    struct sockaddr_in remote_addr; 

    /* Get the Socket file descriptor */ 
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
    { 
     fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor! (errno = %d)\n",errno); 
     exit(1); 
    } 

    /* Fill the socket address struct */ 
    remote_addr.sin_family = AF_INET; 
    remote_addr.sin_port = htons(PORT); 
    inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr); 
    bzero(&(remote_addr.sin_zero), 8); 

    /* Try to connect the remote */ 
    if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1) 
    { 
     fprintf(stderr, "ERROR: Failed to connect to the host! (errno = %d)\n",errno); 
     exit(1); 
    } 
    else 
     printf("[Client] Connected to server at port %d...ok!\n", PORT); 

    /* Send File to Server */ 
    //if(!fork()) 
    //{ 
     char* fs_name = "/home/aryan/Desktop/quotidiani.txt"; 
     char sdbuf[LENGTH]; 
     printf("[Client] Sending %s to the Server... ", fs_name); 
     FILE *fs = fopen(fs_name, "r"); 
     if(fs == NULL) 
     { 
      printf("ERROR: File %s not found.\n", fs_name); 
      exit(1); 
     } 

     bzero(sdbuf, LENGTH); 
     int fs_block_sz; 
     while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0) 
     { 
      if(send(sockfd, sdbuf, fs_block_sz, 0) < 0) 
      { 
       fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno); 
       break; 
      } 
      bzero(sdbuf, LENGTH); 
     } 
     printf("Ok File %s from Client was Sent!\n", fs_name); 
    //} 

    /* Receive File from Server */ 
    printf("[Client] Receiveing file from Server and saving it as final.txt..."); 
    char* fr_name = "/home/aryan/Desktop/progetto/final.txt"; 
    FILE *fr = fopen(fr_name, "a"); 
    if(fr == NULL) 
     printf("File %s Cannot be opened.\n", fr_name); 
    else 
    { 
     bzero(revbuf, LENGTH); 
     int fr_block_sz = 0; 
     while((fr_block_sz = recv(sockfd, revbuf, LENGTH, 0)) > 0) 
     { 
      int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr); 
      if(write_sz < fr_block_sz) 
      { 
       error("File write failed.\n"); 
      } 
      bzero(revbuf, LENGTH); 
      if (fr_block_sz == 0 || fr_block_sz != 512) 
      { 
       break; 
      } 
     } 
     if(fr_block_sz < 0) 
     { 
      if (errno == EAGAIN) 
      { 
       printf("recv() timed out.\n"); 
      } 
      else 
      { 
       fprintf(stderr, "recv() failed due to errno = %d\n", errno); 
      } 
     } 
     printf("Ok received from server!\n"); 
     fclose(fr); 
    } 
    close (sockfd); 
    printf("[Client] Connection lost.\n"); 
    return (0); 
} 

server.c

#include <stdlib.h> 
#include <stdio.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/wait.h> 
#include <sys/socket.h> 
#include <signal.h> 
#include <ctype.h>   
#include <arpa/inet.h> 
#include <netdb.h> 

#define PORT 20000 
#define BACKLOG 5 
#define LENGTH 512 

void error(const char *msg) 
{ 
    perror(msg); 
    exit(1); 
} 

int main() 
{ 
    /* Defining Variables */ 
    int sockfd; 
    int nsockfd; 
    int num; 
    int sin_size; 
    struct sockaddr_in addr_local; /* client addr */ 
    struct sockaddr_in addr_remote; /* server addr */ 
    char revbuf[LENGTH]; // Receiver buffer 

    /* Get the Socket file descriptor */ 
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
    { 
     fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor. (errno = %d)\n", errno); 
     exit(1); 
    } 
    else 
     printf("[Server] Obtaining socket descriptor successfully.\n"); 

    /* Fill the client socket address struct */ 
    addr_local.sin_family = AF_INET; // Protocol Family 
    addr_local.sin_port = htons(PORT); // Port number 
    addr_local.sin_addr.s_addr = INADDR_ANY; // AutoFill local address 
    bzero(&(addr_local.sin_zero), 8); // Flush the rest of struct 

    /* Bind a special Port */ 
    if(bind(sockfd, (struct sockaddr*)&addr_local, sizeof(struct sockaddr)) == -1) 
    { 
     fprintf(stderr, "ERROR: Failed to bind Port. (errno = %d)\n", errno); 
     exit(1); 
    } 
    else 
     printf("[Server] Binded tcp port %d in addr 127.0.0.1 sucessfully.\n",PORT); 

    /* Listen remote connect/calling */ 
    if(listen(sockfd,BACKLOG) == -1) 
    { 
     fprintf(stderr, "ERROR: Failed to listen Port. (errno = %d)\n", errno); 
     exit(1); 
    } 
    else 
     printf ("[Server] Listening the port %d successfully.\n", PORT); 

    int success = 0; 
    while(success == 0) 
    { 
     sin_size = sizeof(struct sockaddr_in); 

     /* Wait a connection, and obtain a new socket file despriptor for single connection */ 
     if ((nsockfd = accept(sockfd, (struct sockaddr *)&addr_remote, &sin_size)) == -1) 
     { 
      fprintf(stderr, "ERROR: Obtaining new Socket Despcritor. (errno = %d)\n", errno); 
      exit(1); 
     } 
     else 
      printf("[Server] Server has got connected from %s.\n", inet_ntoa(addr_remote.sin_addr)); 

     /*Receive File from Client */ 
     char* fr_name = "/home/aryan/Desktop/receive.txt"; 
     FILE *fr = fopen(fr_name, "a"); 
     if(fr == NULL) 
      printf("File %s Cannot be opened file on server.\n", fr_name); 
     else 
     { 
      bzero(revbuf, LENGTH); 
      int fr_block_sz = 0; 
      while((fr_block_sz = recv(nsockfd, revbuf, LENGTH, 0)) > 0) 
      { 
       int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr); 
       if(write_sz < fr_block_sz) 
       { 
        error("File write failed on server.\n"); 
       } 
       bzero(revbuf, LENGTH); 
       if (fr_block_sz == 0 || fr_block_sz != 512) 
       { 
        break; 
       } 
      } 
      if(fr_block_sz < 0) 
      { 
       if (errno == EAGAIN) 
       { 
        printf("recv() timed out.\n"); 
       } 
       else 
       { 
        fprintf(stderr, "recv() failed due to errno = %d\n", errno); 
        exit(1); 
       } 
      } 
      printf("Ok received from client!\n"); 
      fclose(fr); 
     } 

     /* Call the Script */ 
     system("cd ; chmod +x script.sh ; ./script.sh"); 

     /* Send File to Client */ 
     //if(!fork()) 
     //{ 
      char* fs_name = "/home/aryan/Desktop/output.txt"; 
      char sdbuf[LENGTH]; // Send buffer 
      printf("[Server] Sending %s to the Client...", fs_name); 
      FILE *fs = fopen(fs_name, "r"); 
      if(fs == NULL) 
      { 
       fprintf(stderr, "ERROR: File %s not found on server. (errno = %d)\n", fs_name, errno); 
       exit(1); 
      } 

      bzero(sdbuf, LENGTH); 
      int fs_block_sz; 
      while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs))>0) 
      { 
       if(send(nsockfd, sdbuf, fs_block_sz, 0) < 0) 
       { 
        fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno); 
        exit(1); 
       } 
       bzero(sdbuf, LENGTH); 
      } 
      printf("Ok sent to client!\n"); 
      success = 1; 
      close(nsockfd); 
      printf("[Server] Connection with Client closed. Server will wait now...\n"); 
      while(waitpid(-1, NULL, WNOHANG) > 0); 
     //} 
    } 
} 
+1

Dlaczego pisanie własnego demona transferu plików? Dlaczego nie używasz protokołu SSH, a może nawet FTP? – steveha

+4

To projekt, muszę to zrobić w ten sposób. – AscaL

+0

Możliwy duplikat opcji [Wyślij i odbierz plik w programowaniu gniazd w systemie Linux z C/C++ (GCC/G ++)] (http://stackoverflow.com/questions/2014033/send-and-receive-a-file-in- socket-programming-in-linux-with-cc-gcc-g) –

Odpowiedz

7

Niektóre komentarze w przypadkowej kolejności:

  • Jesteś przegapienie okazji do poznania dokładne błędy zbyt często:

    if(listen(sockfd,BACKLOG) == -1) 
    { 
        printf("ERROR: Failed to listen Port %d.\n", PORT); 
        return (0); 
    } 
    

    Ten blok powinien zdecydowanie zawierać perror("listen") lub coś podobnego. Zawsze włączaj perror() lub strerror() do każdego bloku obsługi błędów, gdy szczegóły błędu zostaną zgłoszone przez errno. Uwzględnienie dokładnych przyczyn niepowodzenia pozwoli zaoszczędzić godziny podczas programowania i pozwoli zaoszczędzić Tobie i Twoim użytkownikom godzin, gdy sytuacja nie będzie działać zgodnie z oczekiwaniami w przyszłości.

  • Twój obsługa błędów potrzebuje dalszej standaryzacji:

    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
    { 
        printf("ERROR: Failed to obtain Socket Descriptor.\n"); 
        return (0); 
    } 
    

    ten powinien niereturn 0 ponieważ zasygnalizuje do powłoki że program prowadził do zakończenia bez błędu. Powinieneś return 1 (lub użyć EXIT_SUCCESS i EXIT_FAILURE), aby zasygnalizować anormalne wyjście.

    else 
        printf("[Server] Server has got connected from %s.\n", inet_ntoa(addr_remote.sin_addr)); 
    
    /*Receive File from Client */ 
    

    W poprzednim bloku wystąpił błąd, ale mimo to kontynuuj wykonywanie. To szybki sposób na bardzo niepożądane zachowanie. Powinno to albo ponownie uruchomić główną pętlę serwera, albo zakończyć proces potomny lub coś podobnego. (Zależy jeśli trzymać serwer wieloprocesową.)

    if(!fork()) 
    { 
    

    poprzedniego bloku zapomniał uwzględnić fork()braku. fork() może i nie udaje się - zwłaszcza w dzielonych środowiskach hostingu wspólnych na uniwersytetach - więc powinieneś być przygotowany na pełne, skomplikowane trzy możliwe wartości: fork(): niepowodzenie, dziecko, rodzic.

  • Wygląda na to, że używasz bez różnicy: fork(); Twój klient i serwer są bardzo proste, a sposób, w jaki są zaprojektowane do działania, oznacza, że ​​nie można ich używać do obsługi wielu klientów jednocześnie. Powinieneś prawdopodobnie trzymać się dokładnie jednego procesu dla każdego, przynajmniej dopóki algorytm nie zostanie idealnie zdebugowany i wymyślisz sposób na jednoczesne uruchamianie wielu klientów. Spodziewam się, że to jest źródło problemu, z którym teraz się spotykasz.

  • Musisz użyć funkcji do enkapsulacji szczegółów; Napisz funkcję łączenia się z serwerem, funkcję wysyłania pliku, funkcję zapisu pliku itp. Napisz funkcję do obsługi skomplikowanych zapisów częściowych. (Szczególnie polecam kradzież funkcji writen z kodu źródłowego książki Advanced Programming in the Unix Environment. Plik lib/writen.c.) Jeśli prawidłowo napiszesz funkcje, możesz je ponownie wykorzystać zarówno w kliencie, jak i na serwerze. (Coś jak umieszczenie ich w utils.c i kompilowania programów jak gcc -o server server.c utils.c).

    Mając mniejsze funkcje, które każdy zrobić jedną rzecz pozwoli Ci skupić się na mniejszej ilości kodu w czasie i napisać małe testy dla każdego, który będzie pomóc ci zawęzić sekcje kodu, które wciąż wymagają poprawy.

+0

Hej, dziękuję za porady. Nie lubię programowania, prawdopodobnie dlatego nie jestem w tym dobry. W każdym razie chcę się poprawić, więc postaram się podążać za wskazówkami. Jeśli nie masz nic przeciwko, możesz rozwinąć część "Wygląda na to, że używasz fork() bezkrytycznie ...."? Nie wydaje mi się, że obsługuję wielu klientów. W tej chwili zadzwonię ./client i powinno to nastąpić samo. I jak mogę trzymać się jednego procesu? Jeszcze raz dziękuję! PS: Jest 4 rano tutaj, więc jeśli odpowiedź jest możliwa, nie odpowiem, nie bierz tego osobiście! – AscaL

+0

Pomyśl o tym, co robi 'fork()' - daje kopię programu. Dlaczego Twój klient potrzebuje kopii samego siebie? Dlaczego twój serwer potrzebuje kopii samego siebie? Prawdopodobnie można całkowicie usunąć 'fork()' z obu programów bez złych efektów. (To znacznie ułatwiłoby pisanie o programach). – sarnold

+0

Zacznę od przypomnienia, że ​​znalazłem ten kod. nie napisałem tego i nie myślałem o tym zbyt wiele (to znaczy, że to, co mówię, prawdopodobnie będzie złe), ale jak mogę dostać się do dziecka, jeśli nie używam widelca ()? Z tego, co wiem (i wiem, że to niewiele), gdy serwer akceptuje połączenie, tworzy nowe gniazdo (czyli nowy proces), aby zarządzać połączeniem klienta, więc wykorzystam wynik widełek, aby to osiągnąć. A może po prostu się pomyliłem, tak naprawdę nie wiem. – AscaL

6

Brakuje jednego punktu dyskusji, więc pomyślałem o tym tutaj.

Pozwól nam bardzo szybko zrozumieć transfer danych TCP. Istnieją trzy etapy: a) Ustanowienie połączenia, b) Przesyłanie danych, c) Zakończenie połączenia

Teraz klient wysyła plik na serwer za pośrednictwem gniazda TCP.

Serwer wykonuje przetwarzanie pliku i odsyła go do klienta.

Teraz wszystkie 3 kroki trzeba wykonać. Nawiązanie połączenia odbywa się poprzez wywołanie połączenia. Odczyt/zapis danych odbywa się przez recv/send tutaj, a zakończenie połączenia następuje przez zamknięcie.

Serwer tutaj odczytuje dane w pętli przy pomocy recv. Teraz, gdy pętla zakończy się? Gdy recv zwraca 0 lub może być mniej niż 0 w przypadku błędu. Kiedy recv zwraca 0? -> Kiedy druga strona zamknie połączenie. (Kiedy segment TCP FIN został odebrany z tej strony).

Tak więc w tym kodzie, gdy klient zakończył wysyłanie pliku, użyłem funkcji wyłączania, która wysyła segment FIN od strony klienta, a recv serwera może teraz zwrócić 0, a program jest kontynuowany. (Half way close , ponieważ klient również musi później czytać dane).

(Tylko dla zrozumienia proszę zauważyć połączenie TCP jest Establisment jest 3-way handshake i zakończenie połączenia jest 4-way handshake).

Podobnie jeśli zapomnisz zamknąć gniazdo przyłączeniowe po stronie serwera, recv klienta będzie również blokuj na zawsze. Myślę, że to był powód użycia ctrl c do zatrzymania klienta czasami, o którym wspomniałeś.

Możesz pl.odnoszą standardowej książki Networking lub RFC http://www.ietf.org/rfc/rfc793.txt do nauki więcej o TCP

mam wklejony zmodyfikowanego kodu, a także trochę dodano kilka uwag,

Nadzieja to wyjaśnienie pomoże.

Zmodyfikowany kod klienta:

while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0) 
    { 
     if(send(sockfd, sdbuf, fs_block_sz, 0) < 0) 
     { 
      fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno); 
      exit(1); 
     } 
     bzero(sdbuf, LENGTH); 
    } 

/*Now we have sent the File's data, what about server's recv? 
Recv is blocked and waiting for data to arrive or if the protocol 
stack receives a TCP FIN segment ..then the recv will return 0 and 
the server code can continue */ 
/*Sending the TCP FIN segment by shutdown and this is half way 
close, since the client also needs to read data subsequently*/ 

shutdown(sockfd, SHUT_WR); 
printf("Ok File %s from Client was Sent!\n", fs_name); 
+0

Wielkie dzięki za pomoc! Dodam to do mojej strony klienta i zobaczę, jak to działa. – AscaL

Powiązane problemy