这是一段基于简单结构在套接字上写入HTTP响应的代码
void write_response(request *req, response *resp, int socket) {
char *raw_resp;
int bytes = 0;
asprintf(&raw_resp, "HTTP/1.1 %d %s\r\n", resp->code, resp->reason_phrase);
bytes += strlen(raw_resp);
for (int i = 0; i < resp->header_count; i++) {
asprintf(&raw_resp, "%s%s", raw_resp, resp->headers[i]);
bytes += strlen(resp->headers[i]);
}
if (resp->content != NULL) {
asprintf(&raw_resp, "%s\r\n", raw_resp);
raw_resp = realloc(raw_resp, bytes + 2 + resp->content->size);
memcpy(&raw_resp[strlen(raw_resp)], resp->content->data,
resp->content->size);
bytes += (resp->content->size + 2);
}
write(socket, raw_resp, bytes);
free(raw_resp);
}
基本上,它首先添加HTTP请求行,然后添加标题,最后添加主体。
然而,valgrind在前2个asprintf上报告了Invalid free() / delete / delete[] / realloc()
和18 bytes in 1 blocks are definitely lost in loss record 2 of 4
(内存泄漏),但奇怪的是没有在第3个上。
我使用asprintf吗?
您的代码不会free
asprintf
分配的所有字符串。鉴于您如何使用asprintf
执行动态字符串连接,修复此问题有点麻烦。请注意,您也不处理内存分配失败。您可以使用asprintf
返回值来检测并更新bytes
而无需额外的strlen()
调用。
/* return the number of bytes written or -1 in case of error */
int write_response(request *req, response *resp, int socket) {
char *raw_resp, *new_p;
int bytes;
bytes = asprintf(&raw_resp, "HTTP/1.1 %d %s\r\n", resp->code, resp->reason_phrase);
if (bytes < 0)
return -1;
for (int i = 0; i < resp->header_count; i++) {
bytes = asprintf(&new_p, "%s%s", raw_resp, resp->headers[i]);
free(raw_resp);
raw_resp = newp;
if (bytes < 0)
return -1;
}
if (resp->content != NULL) {
bytes = asprintf(&new_p, "%s\r\n", raw_resp);
free(raw_resp);
raw_resp = newp;
if (bytes < 0)
return -1;
new_p = realloc(raw_resp, bytes + resp->content->size);
if (new_p == NULL) {
free(raw_resp);
return -1;
}
raw_resp = new_p;
memcpy(raw_resp + bytes, resp->content->data, resp->content->size);
bytes += resp->content->size;
}
bytes = write(socket, raw_resp, bytes);
free(raw_resp);
return bytes;
}
备注:
asprintf
执行字符串连接与分配似乎效率低下,只需使用strlen
,realloc
和memcpy
。asprintf()
是非标准的,并非在所有平台上都可用。write
的单个调用,否则单独编写内容可能更有效,以避免额外调用realloc()
以获取潜在的大量内存。snprintf
和strlen
在初始阶段计算头文件的长度也可能更有效,并将头部空间直接分配给完整大小,或者如果使用本地阵列低于合理阈值(4K)则甚至不分配。这是一个修改版本:
int write_response(request *req, response *resp, int socket) {
char buffer[4096];
char *raw_resp, *allocated = NULL;
int bytes, pos;
bytes = snprintf(NULL, 0, "HTTP/1.1 %d %s\r\n", resp->code, resp->reason_phrase);
for (int i = 0; i < resp->header_count; i++)
bytes += strlen(resp->headers[i]);
if (resp->content != NULL)
bytes += 2 + resp->content->size;
/* need an extra byte for `snprintf` null terminator
if no headers and no contents */
if (bytes < sizeof(buffer)) {
raw_resp = buffer;
} else {
raw_resp = allocated = malloc(bytes + 1):
if (raw_resp == NULL)
return -1;
}
pos = snprintf(raw_resp, bytes, "HTTP/1.1 %d %s\r\n", resp->code, resp->reason_phrase);
for (int i = 0; i < resp->header_count; i++) {
int len = strlen(resp->headers[i]);
memcpy(raw_resp + pos, resp->headers[i], len);
pos += len;
}
if (resp->content != NULL) {
raw_resp[pos++] = '\r';
raw_resp[pos++] = '\n';
memcpy(raw_resp + pos, resp->content->data, resp->content->size);
pos += resp->content->size;
}
bytes = write(socket, raw_resp, bytes);
free(allocated);
return bytes;
}
这是手册页所说的内容:
函数asprintf()....分配一个字符串......
然后
应该将此指针传递给free(3)以在不再需要时释放已分配的存储。
每次新呼叫都会导致新的分配。您似乎没有释放以前分配的字符串。
你错过了自由,除了第一次之外你不需要asprintf
可:
void write_response(request *req, response *resp, int socket) {
char * buff;
int len;
len = asprintf(&buff, "HTTP/1.1 %d %s\r\n", resp->code, resp->reason_phrase);
if (len == -1)
.. error management
for (int i = 0; i < resp->header_count; i++) {
size_t hlen = strlen(resp->headers[i]);
buff = realloc(buff, len + hlen + 1);
if (buff == NULL)
... error management
strcpy(buff + len, resp->headers[i]);
len += hlen;
}
if (resp->content != NULL) {
buff = realloc(buff, len + 2 + resp->content->size);
if (buff == NULL)
... error management
buff[len] = '\r';
buff[len + 1] = '\n';
memcpy(len + 2, resp->content->data, resp->content->size);
len += resp->content->size + 2;
}
if (write(socket, buff, len) != len)
... error management
free(buff);
}