Linux C 多线程http服务器GET、POST方法全实现
时间: 2024-01-09 11:03:47 浏览: 154
以下是一个基于 Linux C 多线程的 HTTP 服务器示例,支持 GET、POST 方法,并能够处理表单提交:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <ctype.h>
#define PORT 8080
#define MAX_CONNECTIONS 10
#define BUFFER_SIZE 1024
#define MAX_REQUEST_SIZE 8192
#define MAX_QUERY_PARAMS 16
#define MAX_HEADERS 32
#define MAX_POST_PARAMS 16
typedef struct {
int conn_fd;
char method[8];
char uri[MAX_REQUEST_SIZE];
char query_params[MAX_QUERY_PARAMS][MAX_REQUEST_SIZE];
int num_query_params;
char headers[MAX_HEADERS][MAX_REQUEST_SIZE];
int num_headers;
char post_params[MAX_POST_PARAMS][MAX_REQUEST_SIZE];
int num_post_params;
} http_request;
void parse_request_line(char *line, http_request *request) {
char *method, *uri, *query;
method = strtok(line, " ");
uri = strtok(NULL, " ");
query = strchr(uri, '?');
// parse method
if (method) {
strncpy(request->method, method, sizeof(request->method));
} else {
strncpy(request->method, "GET", sizeof(request->method));
}
// parse URI and query parameters
if (uri) {
if (query) {
strncpy(request->uri, uri, query-uri);
query++;
while (*query) {
char *next = strchr(query, '&');
if (next) {
strncpy(request->query_params[request->num_query_params], query, next-query);
request->query_params[request->num_query_params][next-query-query] = '\0';
request->num_query_params++;
query = next + 1;
} else {
strncpy(request->query_params[request->num_query_params], query, MAX_REQUEST_SIZE);
request->num_query_params++;
break;
}
}
} else {
strncpy(request->uri, uri, sizeof(request->uri));
}
}
}
void parse_header_line(char *line, http_request *request) {
char *name, *value;
name = strtok(line, ": ");
value = strtok(NULL, "\r\n");
if (name && value) {
strncpy(request->headers[request->num_headers], line, sizeof(request->headers[0]));
request->num_headers++;
}
}
void parse_post_params(char *data, http_request *request) {
while (*data) {
char *next = strchr(data, '&');
if (next) {
strncpy(request->post_params[request->num_post_params], data, next-data);
request->post_params[request->num_post_params][next-data-data] = '\0';
request->num_post_params++;
data = next + 1;
} else {
strncpy(request->post_params[request->num_post_params], data, MAX_REQUEST_SIZE);
request->num_post_params++;
break;
}
}
}
void *handle_connection(void *arg) {
http_request *request = (http_request*)arg;
char buffer[BUFFER_SIZE];
ssize_t n;
// read request message
memset(buffer, 0, sizeof(buffer));
n = read(request->conn_fd, buffer, sizeof(buffer)-1);
if (n <= 0) {
close(request->conn_fd);
free(request);
pthread_exit(NULL);
}
// parse request message
char *line = strtok(buffer, "\r\n");
if (line) {
parse_request_line(line, request);
}
while ((line = strtok(NULL, "\r\n"))) {
parse_header_line(line, request);
}
// handle request method
if (strcasecmp(request->method, "GET") == 0) {
// handle GET request
char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nHello, world!";
write(request->conn_fd, response, strlen(response));
} else if (strcasecmp(request->method, "POST") == 0) {
// handle POST request
char *content_length_header = NULL;
for (int i = 0; i < request->num_headers; i++) {
if (strncasecmp(request->headers[i], "Content-Length:", 15) == 0) {
content_length_header = request->headers[i];
break;
}
}
if (content_length_header) {
int content_length = atoi(content_length_header+15);
char *data = buffer + strlen(buffer) + 2;
n = read(request->conn_fd, data, content_length);
if (n > 0) {
data[n] = '\0';
parse_post_params(data, request);
}
}
char response[BUFFER_SIZE];
snprintf(response, sizeof(response), "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
"<html><body><h1>POST parameters:</h1><ul>");
for (int i = 0; i < request->num_post_params; i++) {
char *param = request->post_params[i];
for (int j = 0; j < strlen(param); j++) {
if (param[j] == '+') {
param[j] = ' ';
}
}
char *name = strtok(param, "=");
char *value = strtok(NULL, "=");
snprintf(response+strlen(response), sizeof(response)-strlen(response),
"<li>%s: %s</li>", name, value);
}
strncat(response, "</ul></body></html>", sizeof(response)-strlen(response)-1);
write(request->conn_fd, response, strlen(response));
}
close(request->conn_fd);
free(request);
pthread_exit(NULL);
}
int main() {
int sock_fd, conn_fd;
struct sockaddr_in serv_addr, client_addr;
socklen_t client_len;
pthread_t threads[MAX_CONNECTIONS];
// create socket
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0) {
perror("Failed to create socket");
exit(EXIT_FAILURE);
}
// set server address
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(PORT);
// bind socket to server address
if (bind(sock_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Failed to bind socket");
exit(EXIT_FAILURE);
}
// listen for connections
if (listen(sock_fd, MAX_CONNECTIONS) < 0) {
perror("Failed to listen for connections");
exit(EXIT_FAILURE);
}
printf("HTTP server is listening on port %d\n", PORT);
// handle connections in separate threads
while (1) {
client_len = sizeof(client_addr);
conn_fd = accept(sock_fd, (struct sockaddr*)&client_addr, &client_len);
if (conn_fd < 0) {
perror("Failed to accept connection");
continue;
}
http_request *request = (http_request*)malloc(sizeof(http_request));
memset(request, 0, sizeof(http_request));
request->conn_fd = conn_fd;
pthread_t thread;
if (pthread_create(&thread, NULL, handle_connection, request) < 0) {
perror("Failed to create thread");
close(conn_fd);
free(request);
continue;
}
pthread_detach(thread);
}
return 0;
}
```
上述代码创建了一个监听在 8080 端口的 HTTP 服务器,可以处理多个连接。在每个连接到来时,都会创建一个新的线程来处理请求。在处理请求的线程中,首先读取请求消息,然后解析请求行、请求头和请求体中的参数,根据请求方法的不同返回不同的响应消息。
需要注意的是,在处理 POST 请求时,需要首先读取请求头中的 Content-Length 字段,然后读取相应长度的请求体数据,最后解析 POST 参数。在本例中,使用了 URL 编码格式,因此需要将 POST 参数中的 + 号替换为空格。
阅读全文