Loading... ### 基于C语言的简单HTTP代理服务器示例 ### 一、HTTP代理服务器简介 HTTP代理服务器作为中间服务器,接收客户端的HTTP请求,将请求转发至目标服务器,并将目标服务器的响应返回给客户端。代理服务器可以用于缓存、内容过滤、访问控制等功能。 本示例将使用C语言实现一个简单的HTTP代理服务器,该代理服务器会接受客户端的请求,将其转发给目标Web服务器,并将响应数据返回给客户端。 ### 二、设计思路 1. **监听客户端连接**:代理服务器会监听特定的端口,等待客户端的连接。 2. **解析HTTP请求**:从客户端接收到的请求中提取目标服务器的URL、端口等信息。 3. **建立到目标服务器的连接**:代理服务器将客户端的请求转发到目标服务器。 4. **转发响应数据**:将目标服务器的响应数据返回给客户端。 ### 三、代码实现 #### 3.1 头文件与全局定义 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <netdb.h> #include <sys/socket.h> #include <arpa/inet.h> #define PORT 8888 // 代理服务器监听的端口 #define BUFFER_SIZE 4096 // 缓冲区大小 ``` **解释**: - **`netinet/in.h`**和** `arpa/inet.h`**:提供了与网络相关的函数和数据结构。 - **`BUFFER_SIZE`**:定义用于存储请求和响应的缓冲区大小。 #### 3.2 创建监听套接字 ```c int create_server_socket(int port) { int server_socket; struct sockaddr_in server_addr; // 创建socket server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket < 0) { perror("Socket creation failed"); exit(1); } // 初始化地址结构 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网络接口 server_addr.sin_port = htons(port); // 转换为网络字节序 // 绑定socket到指定的端口 if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("Bind failed"); close(server_socket); exit(1); } // 监听socket,允许最多5个待连接 if (listen(server_socket, 5) < 0) { perror("Listen failed"); close(server_socket); exit(1); } return server_socket; } ``` **解释**: - **`socket()`**:创建监听套接字,用于与客户端通信。 - **`bind()`**:将套接字绑定到指定端口上。 - **`listen()`**:监听客户端的连接,允许最多5个连接排队。 #### 3.3 解析HTTP请求 ```c void parse_request(char *request, char *hostname, char *path, int *port) { char *url_start, *url_end, *port_start; int url_length; // 提取URL部分 url_start = strstr(request, "http://"); if (!url_start) { fprintf(stderr, "Invalid HTTP request\n"); exit(1); } url_start += 7; // 跳过"http://" // 查找URL中的路径部分 url_end = strchr(url_start, '/'); if (!url_end) { strcpy(path, "/"); } else { strcpy(path, url_end); // 复制路径部分 } // 提取主机名和端口 url_length = url_end ? url_end - url_start : strlen(url_start); strncpy(hostname, url_start, url_length); hostname[url_length] = '\0'; // 查找是否有端口 port_start = strchr(hostname, ':'); if (port_start) { *port_start = '\0'; // 截断主机名 *port = atoi(port_start + 1); // 提取端口 } else { *port = 80; // 默认端口为80 } } ``` **解释**: - **`parse_request()`**:解析从客户端接收到的HTTP请求,提取目标主机、路径和端口。 - **`strstr()`**和** `strchr()`**:分别用于查找HTTP头中的子串和字符。 - **`atoi()`**:将端口字符串转换为整数。 #### 3.4 与目标服务器通信 ```c int connect_to_server(const char *hostname, int port) { int server_socket; struct sockaddr_in server_addr; struct hostent *server; // 创建socket server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket < 0) { perror("Socket creation failed"); return -1; } // 获取主机信息 server = gethostbyname(hostname); if (!server) { fprintf(stderr, "No such host: %s\n", hostname); close(server_socket); return -1; } // 初始化目标服务器的地址 bzero((char *) &server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&server_addr.sin_addr.s_addr, server->h_length); server_addr.sin_port = htons(port); // 连接到目标服务器 if (connect(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("Connection to server failed"); close(server_socket); return -1; } return server_socket; } ``` **解释**: - **`connect_to_server()`**:创建一个与目标服务器的连接,返回一个用于通信的套接字。 - **`gethostbyname()`**:将主机名解析为IP地址。 - **`connect()`**:连接到远程服务器。 #### 3.5 代理服务器主逻辑 ```c void handle_client(int client_socket) { char buffer[BUFFER_SIZE]; char hostname[256], path[256]; int target_port, server_socket; int bytes_read; // 接收客户端请求 bytes_read = recv(client_socket, buffer, sizeof(buffer) - 1, 0); if (bytes_read < 0) { perror("Failed to read from client"); close(client_socket); return; } buffer[bytes_read] = '\0'; // 确保缓冲区以null结尾 // 解析客户端的请求 parse_request(buffer, hostname, path, &target_port); // 连接到目标服务器 server_socket = connect_to_server(hostname, target_port); if (server_socket < 0) { close(client_socket); return; } // 转发客户端请求到目标服务器 send(server_socket, buffer, bytes_read, 0); // 接收来自目标服务器的响应并转发给客户端 while ((bytes_read = recv(server_socket, buffer, sizeof(buffer), 0)) > 0) { send(client_socket, buffer, bytes_read, 0); } // 关闭连接 close(server_socket); close(client_socket); } ``` **解释**: - **`handle_client()`**:处理与客户端的通信,接收请求、解析并转发到目标服务器,将服务器响应传回客户端。 - **`recv()`**和** `send()`**:分别用于接收和发送数据。 #### 3.6 主函数 ```c int main() { int server_socket, client_socket; struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); // 创建服务器监听socket server_socket = create_server_socket(PORT); printf("HTTP代理服务器已启动,监听端口: %d\n", PORT); while (1) { // 接受客户端连接 client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_len); if (client_socket < 0) { perror("Accept failed"); continue; } // 处理客户端请求 handle_client(client_socket); } // 关闭服务器socket close(server_socket); return 0; } ``` **解释**: - **`accept()`**:等待并接受客户端的连接请求。 - **`main()`**:程序主逻辑,循环等待客户端连接,并调用 `handle_client()`处理每个连接。 --- ### 四、代码结构总结与功能说明 - **主函数**:负责创建监听套接字,等待客户端连接。 - **`handle_client()`**:负责接收客户端的HTTP请求,连接到目标服务器,并将响应返回客户端。 - **`connect_to_server()`**:与目标服务器建立TCP连接。 - **`parse_request()`**:解析客户端的HTTP请求,提取目标服务器的主机名、路径和端口信息。 ### 五、程序运行示例 启动代理服务器后,客户端通过该代理发送HTTP请求: 1. 编译并运行代理服务器: ```bash gcc -o proxy_server proxy_server.c ./proxy ``` _server ``` 代理服务器在端口`8888`上启动并监听。 2. 使用浏览器或`curl`命令通过代理服务器访问网页: ```bash curl -x http://localhost:8888 http://example.com ``` 代理服务器会接收请求,将其转发到 `example.com`,并将响应返回客户端。 --- ### 六、脑图总结 ```mermaid graph TD; A[HTTP代理服务器] --> B[创建监听套接字]; A --> C[解析HTTP请求]; A --> D[连接到目标服务器]; A --> E[转发响应给客户端]; B --> F[accept客户端连接]; C --> G[提取主机名与端口]; D --> H[建立TCP连接]; E --> I[recv与send数据]; ``` ### 七、总结 通过本示例,我们实现了一个简单的基于C语言的HTTP代理服务器。该服务器能够接受客户端的HTTP请求,将请求转发给目标服务器,并将服务器的响应传递回客户端。 最后修改:2024 年 10 月 04 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏