一、引言
Web 浏览器和 Web 服务器使用称为 HTTP(超文本传输协议)的基于文本的协议进行交互,它们二者可以建立Internet 连接并使用 HTTP 请求某些内容,Web 服务器响应请求的内容并关闭连接,浏览器读取内容并将其显示在屏幕上。在实现简易Web服务器这个选题上,需要实现一个简易的Web服务端用于接收并处理请求和实现一个客户端用于发起并展示请求。在这个选题中,涉及到了多线程的使用、线程同步和互斥的处理、生产者-消费者模式和锁的使用等操作系统的知识点和基于Socket的TCP请求连接方式的知识,所以选择该选题可以提高我对操作系统相关知识的掌握程度和对基于linux相关功能实现的水平。
代码详情:
Client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#define BUFFER_SIZE 4096 // 定义缓冲区大小
#define MAX_THREADS 10 // 定义最大线程数
// 定义线程数据结构,用于存储每个线程的特定数据
typedef struct thread_data {
int thread_id; // 线程ID
const char* server_ip; // 服务器IP地址
int server_port; // 服务器端口号
pthread_barrier_t* barrier; // 用于CONCUR模式的线程屏障
pthread_mutex_t* order_mutex; // 用于FIFO模式的互斥锁
pthread_cond_t* cond_var; // 用于FIFO模式的条件变量
int request_count; // 请求计数,用于控制请求发送的顺序和数量
} thread_data;
// 线程函数,模拟HTTP客户端,发送HTTP请求并接收响应
void* http_client(void* arg) {
thread_data* data = (thread_data*)arg;
if (data->barrier) {
int rc = pthread_barrier_wait(data->barrier);
if (rc != 0 && rc != PTHREAD_BARRIER_SERIAL_THREAD) {
perror("Failed to wait on barrier");
return NULL;
}
}
// 定义HTTP请求格式
const char* request = "GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n";
char response[BUFFER_SIZE + 1]; // 定义响应缓冲区
ssize_t received; // 用于存储接收到的数据大小
struct sockaddr_in server_addr; // 服务器地址结构
// 无限循环发送请求
while (1) {
// 创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket failed");
sleep(1); // 短暂休眠后重试
continue;
}
// 初始化服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(data->server_port);
inet_pton(AF_INET, data->server_ip, &server_addr.sin_addr);
// 连接到服务器
if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("connect failed");
close(sockfd);
continue;
}
// 发送HTTP请求
if (send(sockfd, request, strlen(request), 0) < 0) {
perror("send failed");
close(sockfd);
continue;
}
// 接收响应
memset(response, 0, sizeof(response));
received = recv(sockfd, response, BUFFER_SIZE, 0);
if (received > 0) {
response[received] = '\0';
printf("Thread %d received response: %s\n", data->thread_id, response);
} else {
if (received == 0) {
printf("Connection closed by the server.\n");
} else {
perror("recv failed");
}
close(sockfd);
continue;
}
// 关闭套接字并休眠一段时间后再次发送请求
close(sockfd);
usleep(100000); // 休眠100毫秒
}
return NULL;
}
// 主函数,程序入口点
int main(int argc, char* argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s [threads] [workload]\n", argv[0]);
return 1;
}
int threads = atoi(argv[1]); // 从命令行参数获取线程数
int workload = atoi(argv[2]); // 从命令行参数获取工作模式(0为CONCUR,1为FIFO)
if (threads <= 0 || threads > MAX_THREADS) {
fprintf(stderr, "Invalid arguments\n");
return 1;
}
pthread_t threads_id[MAX_THREADS]; // 线程ID数组
thread_data data[MAX_THREADS]; // 线程数据数组
pthread_barrier_t barrier; // 线程屏障
pthread_mutex_t order_mutex = PTHREAD_MUTEX_INITIALIZER; // 初始化互斥锁
// 根据工作负载模式初始化同步机制
if (workload == 0) {
if (pthread_barrier_init(&barrier, NULL, threads) != 0) {
perror("barrier init failed");
return 1;
}
} else if (workload == 1) {
pthread_mutexattr_t mutex_attr;
pthread_mutexattr_init(&mutex_attr);
if (pthread_mutex_init(&order_mutex, &mutex_attr) != 0) {