【发布时间】:2017-12-24 01:15:39
【问题描述】:
我正在尝试制作一个程序来提供文件供其他人下载。 它侦听端口并接受任何请求并通过套接字发送文件。 我还不担心 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
对代码的一点解释:
-
findFilename只是从文件路径生成文件名 -
serveFile打开一个套接字并监听它以向任何连接到它的人发送指定文件
我的代码:
#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);
}
【问题讨论】:
-
您正在实施 HTTP,尽管您在问题的任何地方都没有这么说。
-
write确实需要错误检查,并且确实需要处理不完整的操作。请记住,write写入最多字节数,但它也可以写入更少(尤其是在较慢的网络连接上)... -
@YuanFu, read the
manpage.write可靠地发送它返回的字节数(即,如果write返回 100,则您知道发送了 100 个字节)。 TCP/IP 驱动程序有一个有限的缓冲区。您的程序应将数据流式传输到 TCP/IP 套接字,管理任何无法发送的缓冲区数据。请注意,“可靠”这个词是相对的,可靠的send并不承诺recv。 -
您根本没有实现范围。那么为什么要发送
Accept-ranges标头?您需要对 RFC 2616 和后续版本有很好的了解才能实现 HTTP。你有很多阅读要做。 -
@Myst 如果有中断,它只能在阻塞模式下写入更少。但它肯定需要一个错误检查,就像这段代码中的所有其他系统调用一样。