代码示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#define BUFFER_SIZE 1024
// 解析HTTP响应头
void parse_headers(char *buffer, char *headers) {
char *end = strstr(buffer, "\r\n\r\n");
if (end) {
int headers_length = end - buffer;
strncpy(headers, buffer, headers_length);
headers[headers_length] = '\0';
}
}
// 处理HTTP响应
void handle_http_response(int sockfd) {
char buffer[BUFFER_SIZE];
ssize_t bytes_read;
char headers[BUFFER_SIZE];
int body_fd = open("response_body", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (body_fd == -1) {
perror("open");
return;
}
// 读取并解析HTTP响应头
bytes_read = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
if (bytes_read <= 0) {
perror("recv");
close(body_fd);
return;
}
buffer[bytes_read] = '\0';
parse_headers(buffer, headers);
printf("HTTP Headers:\n%s\n", headers);
// 跳过响应头和空行
char *end_of_headers = strstr(buffer, "\r\n\r\n");
if (end_of_headers) {
ssize_t offset = end_of_headers - buffer + 4;
bytes_read -= offset;
if (bytes_read > 0) {
write(body_fd, end_of_headers + 4, bytes_read);
}
}
// 逐块读取并写入响应体,避免内存溢出
while ((bytes_read = recv(sockfd, buffer, sizeof(buffer), 0)) > 0) {
write(body_fd, buffer, bytes_read);
}
if (bytes_read < 0) {
perror("recv");
}
close(body_fd);
}
int main() {
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&servaddr.sin_zero, 0, sizeof(servaddr.sin_zero));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(80);
servaddr.sin_addr.s_addr = inet_addr("192.168.1.100"); // 替换为实际服务器IP
if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {
perror("connect failed");
close(sockfd);
exit(EXIT_FAILURE);
}
// 发送HTTP请求
const char *request = "GET / HTTP/1.1\r\nHost: 192.168.1.100\r\n\r\n";
send(sockfd, request, strlen(request), 0);
handle_http_response(sockfd);
close(sockfd);
return 0;
}
优化策略及原理
- 逐块读取响应体:在处理大文件响应时,采用逐块读取的方式,每次读取固定大小(如
BUFFER_SIZE
)的数据,并立即写入文件。这样避免了一次性将整个大文件读入内存,防止内存溢出。其原理是通过控制每次读入内存的数据量,使内存使用保持在可控范围内。
- 及时处理和丢弃已解析的头部:解析完HTTP响应头后,立即将其从缓冲区中处理(如打印或保存到其他地方),并跳过头部和空行直接处理响应体。这减少了不必要的内存占用,因为不再需要在内存中长时间保留已经解析过的头部数据。