16

这是服务器(发送文件)部分:

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 File etc etc”已发送的文件中。
我试图做一个小调试,所以我printf在 for 循环(服务器printf发送文件)和 while 循环(客户端)之后添加了一个,我注意到文件已发送但在客户端它没有因为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...

4

5 回答 5

25

试试这个代码:

客户端:

/* 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 ((remain_data > 0) && ((len = recv(client_socket, buffer, BUFSIZ, 0)) > 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

发送文件的手册页说:

如果 offset 不为 NULL,则它指向一个保存文件偏移量的变量,sendfile() 将从该文件偏移量开始从 in_fd 读取数据。
当 sendfile() 返回时,此变量将设置为读取的最后一个字节之后的字节的偏移量

于 2012-08-15T07:19:49.300 回答
0

客户端不知道文件何时结束。它只是继续读取,直到它收到fsize字节。在您当前的实现中,客户端仅适用于完全为fsize字节的文件。

我建议您更改协议并添加包含文件大小的标头。为什么不使用http协议?

于 2012-08-14T13:50:36.970 回答
0

我认为您应该提供更详细的代码,至少包含使用的变量声明和初始化。

客户端部分如何获得 fsize 值?

您还应该像这样检查 while 条件:

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

不要使用“ != ”,因为如果(由于未知原因)total_bytes_read 变得大于 fsize,您将陷入无限循环,直到套接字连接关闭(并且 read 返回错误)。

我还认为(但不确定)您应该在客户端部分使用recv而不是read

于 2012-08-14T13:53:38.013 回答
0

您应该始终确定您作为协议的一部分发送的文件大小,例如,您可以在实际发送之前将文件大小作为前 4 个(或更多,取决于您希望处理的文件大小)字节发送溪流。

如果您希望使用恒定大小的文件,您的实现应该可以工作,在这种情况下,请为 fsize 和 total_bytes_read 添加打印。

于 2012-08-14T14:03:57.053 回答
0

您使用的sendfileAPI 不正确。由于您在第三个参数中传递了一个非 NULL 值,因此sendfile将为您更新偏移量。sendfile但是因为您的发送循环也在更新偏移量,所以在无法在一次调用中发送整个文件的情况下,您将跳过一些字节。

手册页

如果offset不为 NULL,则它指向一个保存文件偏移量的变量,sendfile()将从中开始读取数据in_fd。返回时sendfile(),此变量将设置为读取的最后一个字节之后的字节的偏移量。

您应该从发送循环中删除这一行:

  offset += rc;

编辑: 在您的更新中,您使用从文件fpl中获取fstat信息,但在您的sendfile代码中,您使用fd. 我会确保这些是你所期望的。(这在另一个更新中得到了修复。)无论如何,我已经使用您提供的代码(以及我建议的修复)编写了一个测试程序,并且它似乎可以在小于 2KB 的文件和 3MB 的文件中正常工作。

于 2012-08-14T14:24:41.720 回答