0

fscanf()FILE. 我有正确确定客户端是否请求现有文件的代码(使用stat()并且它还确保它不是目录)。我将省略这部分,因为它工作正常。

我的目标是使用 HTTP 标头(一个字符串)和正确读取的数据将一个字符串发送回客户端,我认为它必须在某个时候变成一个字符串,以便与标头连接以发送回。我知道这+不是有效的 C,但为简单起见,我想发送这个:headerString+dataString.

下面的代码似乎适用于文本文件,但不适用于图像。我希望单独阅读每个字符可以解决问题,但事实并非如此。当我将浏览器 (Firefox) 指向我的服务器以查找图像时,它告诉我“图像(图像的名称)无法显示,因为它包含错误。”。

这是应该将文件读入的代码httpData

int i = 0;
FILE* file;
file = fopen(fullPath, "r");
if (file == NULL) errorMessageExit("Failed to open file");
while(!feof(file)) {
    fscanf(file, "%c", &httpData[i]);
    i++;
}
fclose(file);
printf("httpData = %s\n", httpData);

编辑:这是我发送的:

char* httpResponse = malloc((strlen(httpHeader)+strlen(httpData)+1)*sizeof(char));
strcpy(httpResponse, httpHeader);
strcat(httpResponse, httpData);
printf("HTTP response = %s\n", httpResponse);

数据部分产生???对于图像,但对于 html 文件正确的 html。

4

1 回答 1

1

图像包含二进制数据。256 个不同的 8 位模式中的任何一个都可能出现在图像中,特别是包括空字节、0x00 或'\0'. b在某些系统(尤其是 Windows)上,您需要使用标准 I/O 调用中的字母来区分文本文件和二进制文件fopen()(在 Unix 和 Windows 上都可以正常工作)。鉴于二进制数据可以包含空字节,您不能使用strcpy()et al 来复制数据块,因为str*()函数在第一个空字节处停止复制。因此,您必须使用mem*()具有起始位置和长度的函数或等效函数。

应用于您的代码,打印二进制文件httpData%s无法正常工作;将%s在第一个空字节处停止。由于您已经用于stat()验证文件的存在,因此您也有文件的大小。假设您不必处理动态更改的文件,这意味着您可以分配httpData正确的大小。您还可以将大小传递给阅读代码。这也意味着读取代码可以使用fread(),写入代码可以使用fwrite(),节省了逐个字符的 I/O。

因此,我们可能有一个函数:

int readHTTPData(const char *filename, size_t size, char *httpData)
{
    FILE *fp = fopen(filename, "rb");
    size_t n;
    if (fp == 0)
        return E_FILEOPEN;
    n = fread(httpData, size, 1, fp);
    fclose(fp);
    if (n != 1)
        return E_SHORTREAD;
    fputs("httpData = ", stdout);
    fwrite(httpData, size, 1, stdout);
    putchar('\n');
    return 0;
}

该函数在成功时返回 0,在失败时返回一些预定义的(负数?)错误号。由于内存分配是在调用例程之前完成的,因此非常简单:

  • 打开文件;如果失败则报告错误。
  • 在单个操作中读取文件。
  • 关闭文件。
  • 如果读取没有获得所有预期的数据,则报告错误。
  • 报告读取的数据(仅调试 - 并将二进制数据打印到标准输出 raw 不是世界上最好的主意,但它与问题中的代码的作用相似)。
  • 报告成功。

在原始代码中,有一个循环:

int i = 0;
...
while(!feof(file)) {
    fscanf(file, "%c", &httpData[i]);
    i++;
}

这个循环有很多问题:

  1. 您不应该使用feof()来测试是否有更多数据要读取。它报告是否已给出EOF 指示,而不是是否给出。
  2. 因此,当最后一个字符被读取时,feof()报告“假”,但fscanf()尝试读取下一个(不存在的)字符,将其添加到缓冲区(可能作为字母,例如 ÿ、y-变音符号、0xFF、 U+00FF,带分音符号的拉丁文小写字母 Y)。
  3. 该代码不检查读取了多少个字符,因此它没有防止缓冲区溢出的保护。
  4. 与. fscanf()_getc()

这是代码的更接近正确的版本,假设这size是分配给httpData.

int i = 0;
int c;

while ((c = getc(file)) != EOF && i < size)
    httpData[i++] = c;

您可以检查是否在您期望的时候获得 EOF。请注意,fread()代码在函数内部进行大小检查fread()。此外,按照我编写参数的方式,这是一个全有或全无的命题——要么size读取所有字节,要么将所有内容视为缺失。如果您想要字节计数并且愿意容忍或处理短读取,您可以反转大小参数的顺序。fwrite()如果您想确保所有内容都已写入,您也可以检查 return from ,但人们往往不太注意检查输出是否成功。(不过,检查您是否得到了预期的输入几乎总是至关重要的——不要吝啬输入检查。)

在某些时候,对于纯文本数据,您需要考虑 CRLF 与 NL 行尾。文本文件会自动处理;二进制文件没有。如果要传输的数据是image/png或类似的,您可能不需要担心这一点。如果你在 Unix 上处理text/plain,你可能不得不担心 CRLF 行结尾(但我不是这方面的专家——我最近没有做过低级 HTTP 的东西(不是在这个千年),所以规则可能已经改变)。

于 2013-09-11T16:09:54.783 回答