以下是一个使用线程池实现的简单Web服务器的代码示例。本示例使用C语言和POSIX线程库。
首先,我们需要创建一个结构体来存储线程池:
```c
typedef struct {
int num_threads;
int max_requests;
int listenfd;
char* root;
threadpool_t* threadpool;
} server_t;
```
其中,`num_threads`表示线程池中的线程数,`max_requests`表示线程池允许的最大请求数,`listenfd`表示服务器监听的套接字,`root`表示Web服务器的根目录,`threadpool`表示线程池。
接下来,我们需要创建一个函数来初始化线程池:
```c
void threadpool_init(server_t* server) {
server->threadpool = threadpool_create(server->num_threads, server->max_requests, 0);
if (server->threadpool == NULL) {
fprintf(stderr, "Error creating thread pool\n");
exit(EXIT_FAILURE);
}
}
```
该函数使用`threadpool_create`函数创建线程池,并将其存储在`server->threadpool`中。
接下来,我们需要创建一个函数来处理客户端请求:
```c
void handle_request(void* arg) {
int sockfd = *(int*)arg;
free(arg);
char buf[1024];
int n = recv(sockfd, buf, sizeof(buf), 0);
if (n < 0) {
perror("Error receiving request");
close(sockfd);
return;
}
buf[n] = '\0';
char method[32], path[256];
sscanf(buf, "%s %s", method, path);
if (strcasecmp(method, "GET") != 0) {
perror("Error: unsupported method");
close(sockfd);
return;
}
char full_path[512];
sprintf(full_path, "%s%s", server->root, path);
FILE* fp = fopen(full_path, "r");
if (fp == NULL) {
perror("Error opening file");
close(sockfd);
return;
}
char response[1024];
sprintf(response, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
send(sockfd, response, strlen(response), 0);
char filebuf[1024];
while ((n = fread(filebuf, 1, sizeof(filebuf), fp)) > 0) {
send(sockfd, filebuf, n, 0);
}
fclose(fp);
close(sockfd);
}
```
该函数使用`recv`函数接收客户端的请求,并解析出请求的方法和路径。如果请求的方法不是GET方法,则返回错误。然后,该函数将请求的路径与服务器的根目录拼接成完整的路径,并尝试打开该文件。如果文件打开失败,则返回错误。否则,该函数将HTTP响应头发送给客户端,并将文件的内容发送给客户端。最后,该函数关闭文件和套接字。
接下来,我们需要创建一个函数来接受客户端连接并将其分配到线程池中:
```c
void accept_client(server_t* server) {
struct sockaddr_in clientaddr;
socklen_t clientlen = sizeof(clientaddr);
int* connfd = malloc(sizeof(int));
*connfd = accept(server->listenfd, (struct sockaddr*)&clientaddr, &clientlen);
if (*connfd < 0) {
perror("Error accepting client");
free(connfd);
return;
}
threadpool_add(server->threadpool, handle_request, connfd, 0);
}
```
该函数使用`accept`函数接受客户端连接,并将连接的套接字存储在一个新的整数指针中。然后,该函数使用`threadpool_add`函数将连接分配到线程池中,并指定`handle_request`函数来处理连接。最后,该函数释放分配的整数指针。
最后,我们需要创建一个函数来启动Web服务器:
```c
void start_server(server_t* server) {
struct sockaddr_in serveraddr;
server->listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (server->listenfd < 0) {
perror("Error creating socket");
exit(EXIT_FAILURE);
}
bzero((char*)&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(PORT);
if (bind(server->listenfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) {
perror("Error binding socket");
exit(EXIT_FAILURE);
}
if (listen(server->listenfd, LISTENQ) < 0) {
perror("Error listening");
exit(EXIT_FAILURE);
}
for (;;) {
accept_client(server);
}
}
```
该函数使用`socket`函数创建一个监听套接字,并使用`bind`函数将其绑定到服务器地址。然后,该函数使用`listen`函数开始监听客户端连接。最后,该函数使用一个无限循环来接受客户端连接并将其分配到线程池中。
完整的代码示例可以在下面找到:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "threadpool.h"
#define PORT 8080
#define LISTENQ 1024
typedef struct {
int num_threads;
int max_requests;
int listenfd;
char* root;
threadpool_t* threadpool;
} server_t;
void threadpool_init(server_t* server) {
server->threadpool = threadpool_create(server->num_threads, server->max_requests, 0);
if (server->threadpool == NULL) {
fprintf(stderr, "Error creating thread pool\n");
exit(EXIT_FAILURE);
}
}
void handle_request(void* arg) {
int sockfd = *(int*)arg;
free(arg);
char buf[1024];
int n = recv(sockfd, buf, sizeof(buf), 0);
if (n < 0) {
perror("Error receiving request");
close(sockfd);
return;
}
buf[n] = '\0';
char method[32], path[256];
sscanf(buf, "%s %s", method, path);
if (strcasecmp(method, "GET") != 0) {
perror("Error: unsupported method");
close(sockfd);
return;
}
char full_path[512];
sprintf(full_path, "%s%s", server->root, path);
FILE* fp = fopen(full_path, "r");
if (fp == NULL) {
perror("Error opening file");
close(sockfd);
return;
}
char response[1024];
sprintf(response, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
send(sockfd, response, strlen(response), 0);
char filebuf[1024];
while ((n = fread(filebuf, 1, sizeof(filebuf), fp)) > 0) {
send(sockfd, filebuf, n, 0);
}
fclose(fp);
close(sockfd);
}
void accept_client(server_t* server) {
struct sockaddr_in clientaddr;
socklen_t clientlen = sizeof(clientaddr);
int* connfd = malloc(sizeof(int));
*connfd = accept(server->listenfd, (struct sockaddr*)&clientaddr, &clientlen);
if (*connfd < 0) {
perror("Error accepting client");
free(connfd);
return;
}
threadpool_add(server->threadpool, handle_request, connfd, 0);
}
void start_server(server_t* server) {
struct sockaddr_in serveraddr;
server->listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (server->listenfd < 0) {
perror("Error creating socket");
exit(EXIT_FAILURE);
}
bzero((char*)&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(PORT);
if (bind(server->listenfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) {
perror("Error binding socket");
exit(EXIT_FAILURE);
}
if (listen(server->listenfd, LISTENQ) < 0) {
perror("Error listening");
exit(EXIT_FAILURE);
}
for (;;) {
accept_client(server);
}
}
int main(int argc, char** argv) {
server_t server;
server.num_threads = 4;
server.max_requests = 1000;
server.root = "/var/www/html";
threadpool_init(&server);
start_server(&server);
return 0;
}
```