C套接字无效的范围标头

时间:2017-12-24 01:15:39

标签: c sockets http

我正在尝试制作一个程序,为其他人提供下载文件。 它侦听端口并接受任何请求并通过套接字发送文件。 我还没有担心NAT。

我遇到的问题是用aria2c(或wget)下载该文件会给我“无效的范围标题错误”。但是,在本地下载(从127.0.0.1下载)可以正常工作。

知道这可能会导致问题,我通过添加-x1标志禁用了aria2c上的多线程下载。

我做错了什么?

错误是:

12/23 20:04:07 [ERROR] CUID#8 - Download aborted. URI=http://104.38.127.225:8000
Exception: [AbstractCommand.cc:351] errorCode=8 URI=http://104.38.127.225:8000
  -> [HttpResponse.cc:86] errorCode=8 Invalid range header. Request: 98304-187031/187032, Response: 0-187031/187032

对代码的一些解释:

  1. findFilename只是从文件路径生成文件名
  2. serveFile打开一个套接字并监听它,以便向连接它的任何人发送指定的文件
  3. 我的代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    #include <netdb.h>
    #include <netinet/in.h>
    
    #include <string.h>
    
    #include <unistd.h> /* for close() */
    
    /* find file name from path */
    /* file name have to be shorter than 512 characters*/
    /* remember to deallocate memory */
    char *findFilename(char *filepath)
    {
      size_t pt;
      size_t filenamePt = 0;
      char ch;
      char *filename = (char*) malloc(512);
    
      for (pt = 0; pt < strlen(filepath); pt++)
      {
        ch = filepath[pt];
        filename[filenamePt] = ch;
        filenamePt++;
    
        if(ch == '/')
        {
          bzero(filename, 512);
          filenamePt = 0;
        }
      }
    
      filename[filenamePt + 1] = '\0';
    
      return filename;
    }
    
    int serveFile(char* filepath, int port)
    {
      FILE *file;
      char *buffer;
      long int fileLength;
      size_t bufferSize;
    
      /* get file name */
      char* filename = findFilename(filepath);
      if (!filename)
      {
        fprintf(stderr, "file path error\n");
        return -1;
      }
    
    
    
      /* open file */
      file = fopen(filepath, "rb");
      if (!file)
      {
        fprintf(stderr, "file open error\n");
        return -1;
      }
    
      /* get file length */
      fseek(file, 0, SEEK_END);
      fileLength=ftell(file);
      fseek(file, 0, SEEK_SET);
      bufferSize = fileLength + 1;
    
      /* allocate memory */
      buffer=(char *)malloc(bufferSize);
      if (!buffer)
      {
          fprintf(stderr, "allocate memory error\n");
          fclose(file);
          return -1;
      }
    
      /* read file */
      fread(buffer, fileLength, 1, file);
      fclose(file);
    
      /* compose header */
      char header[1024];
    
      sprintf(header,
              "HTTP/1.1 200 OK\n"
              "Content-Length: %li\n"
              "Accept-Ranges: bytes\n"
              "Content-Disposition: attachment; filename=\"%s\"\n"
              "\n", fileLength, filename);
    
      /* do not need filename anymore */
      free(filename);
    
      /* create socket */
      int socketfd = socket(PF_INET, SOCK_STREAM, 0);
    
      /* configure socket */
      struct sockaddr_in address;
    
      bzero(&address, sizeof(address));
      address.sin_family = AF_INET;
      address.sin_port = htons(port); /* host to network short */
      address.sin_addr.s_addr = INADDR_ANY;
    
      /* bind & listen */
      if(bind(socketfd, (struct sockaddr*) &address, sizeof(address)) != 0)
      {
        fprintf(stderr, "bind error\n");
        return -1;
      }
    
      if (listen(socketfd, 16)!=0)
      {
        fprintf(stderr, "listen error\n");
        return -1;
      }
    
      /* accept client */
      while (1)
      {
        socklen_t size = sizeof(address);
        int clientSocket = accept(socketfd, (struct sockaddr*) &address, &size);
    
        puts("client connected");
    
        if (fork() == 0)
        {
          write(clientSocket, header, strlen(header));
          write(clientSocket, buffer, bufferSize);
          exit(0);
        }
        else
        {
          close(clientSocket);
        }
      }
    
    }
    
    
    int main(int argc, char *argv[])
    {
      if (argc < 3 || argc > 3)
      {
        fprintf(stderr, "needs 2 arguments: file path & port\n");
        return -1;
      }
      char *filepath = argv[1];
      int port = atoi(argv[2]);
    
      printf("serving %s on port %d\n"
           "file name(not path) cannot exceed 512 characters\n"
             "keyboard interrupt to kill\n", filepath, port);
      serveFile(filepath, port);
    }
    

1 个答案:

答案 0 :(得分:1)

null

你附上的错误说明了一切,真的。

您的代码假定每次向服务器发出的请求都是一个请求整个文件的请求。

由于很多好的理由,情况并非总是如此(参见编辑)。

12/23 20:04:07 [ERROR] CUID#8 - Download aborted. URI=http://104.38.127.225:8000 Exception: [AbstractCommand.cc:351] errorCode=8 URI=http://104.38.127.225:8000 -> [HttpResponse.cc:86] errorCode=8 Invalid range header. Request: 98304-187031/187032, Response: 0-187031/187032 个请求(using the Range header)允许客户端请求文件的一部分,这样即使在网络出现故障后也可以将下载分解为块并恢复下载。

但是,无论请求是什么,您的代码也会发送整个文件。

此外,您的代码正在发送Range标头,表示将遵守Range请求(即使不是)。

在这种情况下,客户端抱怨你的代码,表明它已经请求了一个块,你的代码发送了整个代码。

修改

正如@RemyLebeau所指出的,the Range function is optional, though widely used

这就是您有时只收到错误的原因......

Accept-Ranges似乎在一定程度上需要范围功能,或者它无法作为下载管理器工作(毕竟,管理下载是aria2c功能的目的。 / p>

其他下载工具不需要(或利用)此功能。