我是 C 新手,尝试构建一个非常简单的网络服务器作为入门项目,但由于某种原因,在处理一个请求(并正确返回 HTML 文件)后,我在 handleClient 函数中收到 SIGABRT
当我介绍 Posix 线程时它就出现了,我假设它与多线程和不正确的内存处理有关。有人可以指出我正确的方向或帮助我找出代码的哪一部分可能是错误的吗?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
void serveFile(int socket, const char* filename) {
char buffer[1024];
int fd;
int readBytes;
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("File open failed");
return;
}
while ((readBytes = read(fd, buffer, sizeof(buffer))) > 0) {
write(socket, buffer, readBytes);
}
close(fd);
}
void *handleClient(void *arg) {
int socket = *(int*)arg;
char buffer[1024];
int readBytes;
readBytes = read(socket, buffer, sizeof(buffer));
buffer[readBytes] = '\0';
printf("Received: %s\n", buffer);
char *header = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n";
write(socket, header, strlen(header));
serveFile(socket, "./helloworld.html");
close(socket);
free(arg);
return NULL;
}
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
while(1) {
printf("Waiting for connections...\n");
int *client_fd = malloc(sizeof(int));
if ((*client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
perror("accept");
continue;
}
pthread_t thread_id;
if(pthread_create(&thread_id, NULL, handleClient, (void *)client_fd)) {
perror("Could not create thread");
continue;
}
pthread_detach(thread_id);
}
return 0;
}
编辑:这是服务器从客户端接收到的内容:
Received: GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9,sv-SE;q=0.8,sv;q=0.7
Cookie: access_token="token"
我正在使用 m3 芯片的 macOS 上运行和编译。我正在使用最新版本的 Chrome 进行测试,这就是错误消息在终端中的显示方式
zsh: abort ./http_server
您的代码有几个对我来说很明显的问题。
首先,这是潜在的缓冲区溢出:
char buffer[1024];
int readBytes;
readBytes = read(socket, buffer, sizeof(buffer));
buffer[readBytes] = '\0';
如果读取完整的 1024 个字节,就会溢出
buffer
。
如果您想将数据读取为以 null 结尾的字符串,则必须自行添加它:
char buffer[1024];
int readBytes;
readBytes = read(socket, buffer, sizeof(buffer) - 1);
buffer[readBytes] = '\0';
这个转换为
socklen_t
可能是完全错误的:
if ((*client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
...
如果
addrlen
不是 socklen_t
,且 accept()
大于 socklen_t
,则 int addrlen
可以覆盖内存。 永远不要抛弃警告。
可能还有其他问题 - 这些是我很快注意到的问题。