网络分配存在一些问题。最终目标是让一个C程序通过HTTP从给定的URL中获取文件并将其写入给定的文件名。我已经让它适用于大多数文本文件,但我遇到了一些问题,我怀疑它们都来自同一个根本原因。
这是我用来将数据从网络文件描述符传输到输出文件描述符的代码的快速版本:
unsigned long content_length; // extracted from HTTP header
unsigned long successfully_read = 0;
while(successfully_read != content_length)
{
char buffer[2048];
int extracted = read(connection,buffer,2048);
fprintf(output_file,buffer);
successfully_read += extracted;
}
正如我所说的,这适用于大多数文本文件(虽然%符号会混淆fprintf,所以有办法处理它会很好)。问题是当我尝试获取非文本文件时它会永远挂起(.png是我正在使用的基本测试文件,但程序需要能够处理任何事情)。
我已经做了一些调试,我知道我不会超过content_length,在读取过程中遇到错误,或者遇到一些网络瓶颈。我在网上浏览了一下,但我能找到的所有二进制文件的C / I代码似乎都是基于你知道文件中的数据是如何构造的想法。我不知道它的结构如何,我并不在乎;我只想将一个文件描述符的内容复制到另一个文件描述符中。
有人能指出我可以使用的内置文件i / o函数吗?
编辑:或者,HTTP标题中是否有一个标准字段可以告诉我如何处理我正在使用的文件?
答案 0 :(得分:4)
您使用错误的工具进行工作。 fprintf
采用格式字符串和额外参数,如下所示:
fprintf(output_file, "hello %s, today is the %d", cstring, dayoftheweek);
如果您从未知来源传递第二个参数(例如您正在执行的网络),则可能会在字符串中意外地使用%s
或%d
或其他格式说明符。然后fprintf
将尝试读取比传递的更多的参数,并导致未定义的行为。
使用fwrite
:
fwrite(buffer, 1, extracted, output_file);
答案 1 :(得分:1)
您的代码有几点:
对于fprintf - 您使用数据作为第二个参数,实际上它应该是格式,数据应该是第三个参数。这就是为什么你会遇到%字符的问题,以及为什么它在呈现二进制数据时会遇到困难,因为它需要一个格式字符串。
您需要使用其他功能(例如fwrite)来输出文件。
作为旁注,这是一个安全问题 - 如果您从服务器获取特制文件,则可能会暴露您内存的随机区域。
答案 2 :(得分:0)
除了Seth的回答:除非您使用第三方库来处理所有HTTP内容,否则您需要处理Transfer-Encoding
标头和可能的压缩,或者至少检测它们并抛出一个如果您不知道如何处理这种情况,则会出错。
一般情况下,解析HTTP响应标头可能(也可能不是),只有当它们包含您理解的内容时,才能继续解释标头后面的数据。
答案 3 :(得分:0)
我打赌你的程序正在挂起,因为它期望X字节但是接收Y而不是X< Y(很可能是没有压缩 - 但PNG不能用gzip很好地压缩)。您将获得数据的块[*],其中一个块最有可能跨越content_length
,因此您的条件while(successfully_read != content_length)
始终为真。
如果你想看看你的程序如何继续尝试读取它永远不会得到的数据,你可以尝试在strace
下运行你的程序或者你的操作系统的等价物(因为你可能已经建立了一个HTTP) /1.1请求保持连接打开,并且您没有发出第二个请求)或已经结束(如果服务器关闭连接,您(重复)调用read(2)将返回0,这将离开您(仍然true)循环条件不变。
如果要将程序的输出发送到stdout,您可能会发现它没有产生输出 - 如果您要检索的资源不包含换行符或其他刷新强制控制字符,则会发生这种情况。当输出到文件时,其他stdio缓冲机制可能适用。 (例如,文件将保持为空,直到stdio缓冲区累积至少4096个字节。)
[*]然后还有Transfer-Encoding: chunked
,正如@ roland-illig所暗示的那样,这将破坏content_length
(可能是从同名HTTP头派生)和实际字节数之间的确切等价转移到插座上。
答案 4 :(得分:0)
您正在将文件作为文本文件打开。这样做意味着程序将在每次write()调用结束时添加\ r \ n字符。尝试将文件打开为二进制文件,这些错误的大小将消失。