【问题标题】:Reading & loading HTML file into HTTP response in C在 C 中将 HTML 文件读取并加载到 HTTP 响应中
【发布时间】:2021-01-07 16:56:11
【问题描述】:

我正在 Linux 上编写一个简单的 HTTP1.1 服务器,它只托管一个网站 (root = index.html),我试图在单个数据包中发送 HTTP 响应。

我正在尝试找到一种最佳方式来定位和读取 html 文件,然后将其连接到响应缓冲区中。

注意:URL = "/index.html"。

首先,我尝试了以下方法:

    char *root;
    char PATH[9999];

    root = getenv("PWD");
    strcpy(PATH, root); 
    strcpy(&PATH[strlen(root)], URL); 
    int filefd = open(PATH, O_RDONLY);

但这让我遇到了段错误

所以我尝试了这个

    char *PATH;

    getcwd(PATH, sizeof(PATH));
    strcat(PATH, URL); 
    int filefd = open(PATH, O_RDONLY);

但它也不起作用。

注意,我将文件复制到 HTTP 响应中的方式如下:

    char body[fileLen];
    if ( read(filefd, body, fileLen) != -1 ) 
      strcat(response, body);

其中 fileLen 是使用 fseek() 和 ftell() 获得的 index.html 文件的长度

【问题讨论】:

  • 一般来说,我建议使用调试器或内存分析器来跟踪这样的错误。在 Linux 上,您可以使用 gdb 和 Valgrind 等。我怀疑你的strcat() 超出了response,因为body 不是以空值结尾的。但是为什么要猜测什么时候可以调试呢?

标签: c linux sockets http server


【解决方案1】:

我正在尝试在单个数据包中发送 HTTP 响应

这永远行不通。 TCP,就其本质而言,是流式传输的,并且永远不会保证使用单个数据包。没有充分的理由尝试将所有内容都填充到单个缓冲区中。充其量,这是等待发生的缓冲区溢出。但是现在,您甚至无法将文件名放入正确分配的字符缓冲区,更不用说打开文件并将其读入 HTTP 响应缓冲区了。

char *root;
char PATH[9999];

root = getenv("PWD");
strcpy(PATH, root); 
strcpy(&PATH[strlen(root)], URL); 
int filefd = open(PATH, O_RDONLY);

这会导致段错误,因为您没有检查 getenv() 的结果是否为 NULL。 getenv("PWD") 不是获取调用进程当前工作目录的正确方法。将 NULL 指针传递给strcpy()strlen()未定义的行为

char *root = getenv("PWD");
if (root) {
    char PATH[9999];
    strcpy(PATH, root); 
    strcpy(&PATH[strlen(root)], URL); 
    int filefd = open(PATH, O_RDONLY);
    ...
}
char *PATH;

getcwd(PATH, sizeof(PATH));
strcat(PATH, URL); 
int filefd = open(PATH, O_RDONLY);

这失败了,因为您没有为PATH 分配任何内存来指向所以getpwd()strcat() 有一些东西要写入。改用固定数组:

char PATH[9999];

if (getcwd(PATH, sizeof(PATH))) {
    strcat(PATH, URL); 
    int filefd = open(PATH, O_RDONLY);
    ...
}
char body[fileLen];
if ( read(filefd, body, fileLen) != -1 ) 
    strcat(response, body);

这也是错误的。 strcat() 需要一个以 null 结尾的字符串来复制,但 read() 的结果不是以 null 结尾的。 read() 返回它实际读取的字节数:

char body[fileLen];
int numRead;
if ( (numRead = read(filefd, body, fileLen)) > 0 ) 
    memcpy(&response[strlen(response)], body, numRead);

或者:

char body[fileLen];
int numRead = read(filefd, &response[strlen(response)], fileLen);
...

当然,无论哪种情况,在调用read() 之前,您都必须确保分配给response 的空间足够大以实际容纳整个HTTP 响应标头+ 文件数据。

这就是利用 TCP 的流式传输特性而不是将整个 HTTP 响应存储到单个缓冲区中的地方。先发送HTTP响应头,然后循环调用read()发送每一个读取成功的缓冲区,例如:

int sendRaw(int sckt, const void *buf, size_t buflen) {
    const char *ptr = (const char*) buf;
    int numSent;
    while ( buflen > 0 ) {
        if ( (numSent = send(sckt, ptr, buflen, 0)) < 0 ) {
            return -1;
        }
        ptr += numSent;
        buflen -= numSent;
    }
    return 0;
}

int sendStr(int sckt, const char *str) {
    return sendStr(sckt, str, strlen(str));
}

...

const char *URL = "/index.html";
char PATH[9999];

if (getcwd(PATH, sizeof(PATH))) {
    strcat(PATH, URL); 
} else {
    PATH[0] = '\0';
}

int filefd = open(PATH, O_RDONLY);
if ( filefd < 0 ) {
    // error handling as needed ...
    sendStr(sckt, "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n");
    close(sckt);
    return;
}

fileLen = ...; // get file length as needed ...

if ( dprintf(sckt, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nContent-Length: %d\r\n\r\n", fileLen) < 0 ) {
    // error handling as needed ...
    close(filefd);
    close(sckt);
    return;
}

char body[1024];
int numRead;

while ( fileLen > 0 ) {
    if ( (numRead = read(filefd, body, min(fileLen, 1024))) <= 0 ) {
        // error handling as needed ...
        close(filefd);
        close(sckt);
        return;
    }

    if ( sendRaw(sckt, body, numRead) < 0 ) {
        // error handling as needed ...
        close(filefd);
        close(sckt);
        return;
    }

    fileLen -= numRead;
}

close(filefd);
...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    • 2018-10-24
    • 1970-01-01
    • 1970-01-01
    • 2019-03-06
    • 2015-04-17
    相关资源
    最近更新 更多