2012-08-14 86 views
6

這是服務器(sendfile的)部分:c。發送和接收文件

offset = 0; 
for (size_to_send = fsize; size_to_send > 0;){ 
    rc = sendfile(newsockd, fd, &offset, size_to_send); 
    if (rc <= 0){ 
    perror("sendfile"); 
    onexit(newsockd, sockd, fd, 3); 
    } 
    offset += rc; 
    size_to_send -= rc; 
} 
close(fd); /* la chiusura del file va qui altrimenti rischio loop infinito e scrittura all'interno del file */ 

memset(buffer, 0, sizeof(buffer)); 
strcpy(buffer, "226 File Successfully transfered\n"); 
if(send(newsockd, buffer, strlen(buffer), 0) < 0){ 
    perror("Errore durante l'invio 226"); 
    onexit(newsockd, sockd, 0, 2); 
} 
memset(buffer, 0, sizeof(buffer)); 

,這是客戶端(recv的文件)的部分部分:

fsize_tmp = fsize; 
    sInfo.filebuffer = malloc(fsize); 
    if(sInfo.filebuffer == NULL){ 
    perror("malloc"); 
    onexit(sockd, 0, fd, 4); 
    } 

    while(((uint32_t)total_bytes_read != fsize) && ((nread = read(sockd, sInfo.filebuffer, fsize_tmp)) > 0)){ 
    if(write(fd, sInfo.filebuffer, nread) != nread){ 
      perror("write RETR"); 
      onexit(sockd, 0, 0, 1); 
     } 
     total_bytes_read += nread; 
     fsize_tmp -= nread; 
    } 
    close(fd); /* la chiusura del file va qui altrimenti client entra in loop infinito e si scrive all'interno del file */ 

    memset(buffer, 0, sizeof(buffer)); 
    if(recv(sockd, buffer, 34, 0) < 0){ 
    perror("Errore ricezione 226"); 
    onexit(sockd, 0, 0, 1); 
    } 
    printf("%s", buffer); 
    memset(buffer, 0, sizeof(buffer)); 
    memset(dirpath, 0, sizeof(dirpath)); 
    free(sInfo.filebuffer); 

的問題是,字符串「226文件等」etc「被寫入裏面已發送的文件。
我試圖做一個小調試,所以我在for循環(服務器sendfile)和012循環(客戶端)後printf後添加printf,我注意到該文件發送,但在客戶端它不會退出,因爲printf未被打印...
爲什麼我得到這個奇怪的行爲?
BR>

編輯:
服務器發送的文件大小到客戶端絲毫此代碼:


fd = open(filename, O_RDONLY); 
    if(fd < 0){ 
    error!! 
    } 

    if(fstat(fd, &fileStat) < 0){ 
     perror("Errore fstat"); 
     onexit(newsockd, sockd, fd, 3); 
    } 
    fsize = fileStat.st_size; 
    if(send(newsockd, &fsize, sizeof(fsize), 0) < 0){ 
     perror("Errore durante l'invio della grandezza del file\n"); 
     onexit(newsockd, sockd, fd, 3); 
    } 

客戶端從與此代碼接收服務器的FSIZE

if(read(sockd, &fsize, sizeof(fsize)) < 0){ 
    perror("Errore durante ricezione grandezza file\n"); 
    onexit(sockd, 0 ,0 ,1); 
} 
fd = open(sInfo.filename, O_CREAT | O_WRONLY, 0644); 
if (fd < 0) { 
    perror("open"); 
    onexit(sockd, 0 ,0 ,1); 
} 
fsize_tmp = fsize; 

fsize被宣佈爲uint32_t ...

+0

的[發送,並用C/C++(GCC/G ++)接收在插座編程一個文件在Linux中(可能的複製https://stackoverflow.com/questions/2014033/send-and-receive-a- linux-with-cc-gcc-g) – 2017-06-15 05:40:01

回答

8

試試這個代碼:

客戶端:

/* Client code */ 
/* TODO : Modify to meet your need */ 
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <netinet/in.h> 

#define PORT_NUMBER  5000 
#define SERVER_ADDRESS "192.168.1.7" 
#define FILENAME  "/home/toc/foo.c" 

int main(int argc, char **argv) 
{ 
     int client_socket; 
     ssize_t len; 
     struct sockaddr_in remote_addr; 
     char buffer[BUFSIZ]; 
     int file_size; 
     FILE *received_file; 
     int remain_data = 0; 

     /* Zeroing remote_addr struct */ 
     memset(&remote_addr, 0, sizeof(remote_addr)); 

     /* Construct remote_addr struct */ 
     remote_addr.sin_family = AF_INET; 
     inet_pton(AF_INET, SERVER_ADDRESS, &(remote_addr.sin_addr)); 
     remote_addr.sin_port = htons(PORT_NUMBER); 

     /* Create client socket */ 
     client_socket = socket(AF_INET, SOCK_STREAM, 0); 
     if (client_socket == -1) 
     { 
       fprintf(stderr, "Error creating socket --> %s\n", strerror(errno)); 

       exit(EXIT_FAILURE); 
     } 

     /* Connect to the server */ 
     if (connect(client_socket, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1) 
     { 
       fprintf(stderr, "Error on connect --> %s\n", strerror(errno)); 

       exit(EXIT_FAILURE); 
     } 

     /* Receiving file size */ 
     recv(client_socket, buffer, BUFSIZ, 0); 
     file_size = atoi(buffer); 
     //fprintf(stdout, "\nFile size : %d\n", file_size); 

     received_file = fopen(FILENAME, "w"); 
     if (received_file == NULL) 
     { 
       fprintf(stderr, "Failed to open file foo --> %s\n", strerror(errno)); 

       exit(EXIT_FAILURE); 
     } 

     remain_data = file_size; 

     while (((len = recv(client_socket, buffer, BUFSIZ, 0)) > 0) && (remain_data > 0)) 
     { 
       fwrite(buffer, sizeof(char), len, received_file); 
       remain_data -= len; 
       fprintf(stdout, "Receive %d bytes and we hope :- %d bytes\n", len, remain_data); 
     } 
     fclose(received_file); 

     close(client_socket); 

     return 0; 
} 

服務器端:

/* Server code */ 
/* TODO : Modify to meet your need */ 
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <netinet/in.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <sys/sendfile.h> 

#define PORT_NUMBER  5000 
#define SERVER_ADDRESS "192.168.1.7" 
#define FILE_TO_SEND "hello.c" 

int main(int argc, char **argv) 
{ 
     int server_socket; 
     int peer_socket; 
     socklen_t  sock_len; 
     ssize_t len; 
     struct sockaddr_in  server_addr; 
     struct sockaddr_in  peer_addr; 
     int fd; 
     int sent_bytes = 0; 
     char file_size[256]; 
     struct stat file_stat; 
     int offset; 
     int remain_data; 

     /* Create server socket */ 
     server_socket = socket(AF_INET, SOCK_STREAM, 0); 
     if (server_socket == -1) 
     { 
       fprintf(stderr, "Error creating socket --> %s", strerror(errno)); 

       exit(EXIT_FAILURE); 
     } 

     /* Zeroing server_addr struct */ 
     memset(&server_addr, 0, sizeof(server_addr)); 
     /* Construct server_addr struct */ 
     server_addr.sin_family = AF_INET; 
     inet_pton(AF_INET, SERVER_ADDRESS, &(server_addr.sin_addr)); 
     server_addr.sin_port = htons(PORT_NUMBER); 

     /* Bind */ 
     if ((bind(server_socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))) == -1) 
     { 
       fprintf(stderr, "Error on bind --> %s", strerror(errno)); 

       exit(EXIT_FAILURE); 
     } 

     /* Listening to incoming connections */ 
     if ((listen(server_socket, 5)) == -1) 
     { 
       fprintf(stderr, "Error on listen --> %s", strerror(errno)); 

       exit(EXIT_FAILURE); 
     } 

     fd = open(FILE_TO_SEND, O_RDONLY); 
     if (fd == -1) 
     { 
       fprintf(stderr, "Error opening file --> %s", strerror(errno)); 

       exit(EXIT_FAILURE); 
     } 

     /* Get file stats */ 
     if (fstat(fd, &file_stat) < 0) 
     { 
       fprintf(stderr, "Error fstat --> %s", strerror(errno)); 

       exit(EXIT_FAILURE); 
     } 

     fprintf(stdout, "File Size: \n%d bytes\n", file_stat.st_size); 

     sock_len = sizeof(struct sockaddr_in); 
     /* Accepting incoming peers */ 
     peer_socket = accept(server_socket, (struct sockaddr *)&peer_addr, &sock_len); 
     if (peer_socket == -1) 
     { 
       fprintf(stderr, "Error on accept --> %s", strerror(errno)); 

       exit(EXIT_FAILURE); 
     } 
     fprintf(stdout, "Accept peer --> %s\n", inet_ntoa(peer_addr.sin_addr)); 

     sprintf(file_size, "%d", file_stat.st_size); 

     /* Sending file size */ 
     len = send(peer_socket, file_size, sizeof(file_size), 0); 
     if (len < 0) 
     { 
       fprintf(stderr, "Error on sending greetings --> %s", strerror(errno)); 

       exit(EXIT_FAILURE); 
     } 

     fprintf(stdout, "Server sent %d bytes for the size\n", len); 

     offset = 0; 
     remain_data = file_stat.st_size; 
     /* Sending file data */ 
     while (((sent_bytes = sendfile(peer_socket, fd, &offset, BUFSIZ)) > 0) && (remain_data > 0)) 
     { 
       fprintf(stdout, "1. Server sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data); 
       remain_data -= sent_bytes; 
       fprintf(stdout, "2. Server sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data); 
     } 

     close(peer_socket); 
     close(server_socket); 

     return 0; 
} 

編輯:從人添加解釋有關offset

的人發送文件的頁面說:

如果偏移不爲空,則它指向一個變量保持從其中的sendfile偏移文件 ()將開始從in_fd讀取數據。
當sendfile()返回時,該變量將被設置爲最後一個讀取的字節後面的字節偏移量

+0

我解決了我的問題:D非常感謝!我的錯誤是我用「&fsize」而不是char數組來接收文件的大小:) – polslinux 2012-08-16 06:03:18

+0

@polslinux:歡迎: - 。好吧,關於在服務器端增加偏移量也有一個問題。 – TOC 2012-08-16 06:11:26

+0

真的嗎?爲什麼??你能給我解釋一下嗎?謝謝:) – polslinux 2012-08-16 06:54:01

0

客戶端不知道文件何時結束。它只是讀取,直到它收到fsize字節。 在您當前的實現中,客戶端僅適用於正好爲fsize字節的文件。

我建議你改變你的協議並添加一個包含文件大小的頭文件。 爲什麼不使用http協議?

+0

我不知道該怎麼做才能使用http xD – polslinux 2012-08-14 15:46:54

+0

我已閱讀您的更新。似乎唯一缺少的是文件大小的實際'send'命令。 – 2012-08-14 20:48:18

+0

你是對的!我更新了我的問題!我也發現這個問題:服務器發送正確的fsize,但客戶端收到一個非常大的fsize :(:(例如,如果服務器上的fsize是1048,客戶端收到20209320 Oo – polslinux 2012-08-14 21:28:33

0

我想你應該至少提供一個更詳細的代碼,使用變量聲明和初始化。

客戶端部分如何獲取fsize值?

您也應該檢查這樣的條件時:

while(((uint32_t)total_bytes_read < fsize) && (..... 

不要使用「!=」,因爲如果(未知原因)total_bytes_read變得比FSIZE時,你會直到套接字連接被關閉(並且讀取返回一個錯誤)爲止,在無限循環中被塞住。

我也覺得(但不知道),你應該使用的recv的代替在您的客戶端部分閱讀

+0

之間沒有區別在這種情況下,'recv(2)'和'read(2)'。 – 2012-08-14 13:56:15

+0

'fstat(fd,&fileStat); fsize = fileStat.st_size;'得到一個更簡單的例子我沒有檢查錯誤 – polslinux 2012-08-14 15:49:14

0

您應該始終確定作爲協議一部分發送的文件的大小,例如,您可以將文件大小作爲前4個(或更多,取決於您希望處理的文件大小)字節發送,在實際流之前。

如果你想使用恆定大小的文件,你的實現應該可以工作,在這種情況下,請爲fsize和total_bytes_read添加打印。

0

您正在錯誤地使用sendfile API。由於您在第三個參數中傳入了非NULL值,因此sendfile將爲您更新偏移量。但是因爲您的發送循環也在更新偏移量,所以在sendfile無法在一次調用中發送整個文件的情況下,您將跳過一些字節。

man page

如果offset不是NULL,則它指向一個變量保持的文件偏移從中sendfile()將開始從in_fd讀取數據。當sendfile()返回時,此變量將被設置爲最後一個讀取字節後面的字節偏移量。

您應該從發送循環刪除此行:

offset += rc; 

編輯:在您的更新,您可以使用fpl從您的文件中得到fstat信息,但在你的sendfile代碼,您使用fd。我會確保這些是你期望的。(這是在另一個更新中修復的)。無論如何,我寫了一個test program using the code you provided (with my suggested fix),它看起來可以在小於2KB的文件和3MB的文件中正常工作。

+0

不工作...字符串「226 etc etc」打印在文件裏面:( – polslinux 2012-08-14 15:45:24

+0

也我認爲使用var是正確的,因爲'man sendfile'報告:_如果偏移量不是NULL,那麼它指向一個變量,其中包含文件 偏移量,sendfile()將開始從in_fd._ – polslinux 2012-08-14 15:47:43

+0

@polslinux讀取數據:引用來自手冊頁 – jxh 2012-08-14 15:52:08