0


Linux下C语言完成简单Web代理服务器


总述

开发:WSL+vscode

本项目利用C语言套接字编程和多线程编程实现了Linux下一个简单web代理服务器,只支持HTTP 1.0协议的GET方法。

代理服务器端口号由用户自己手动输入,在终端输入 ./proxy <port>即可启动。

代理服务器的工作流程想必大家都清楚了

服务器创建套接字 ——> 服务器与客户端连接并分配线程 ——> 服务器接收客户端消息 ——> 服务器将客户端消息发向目标地址 ——> 服务器接收目标地址的响应 ——> 服务器将响应反馈给客户端

接下来只需要将各个步骤涉及的功能实现即可。


技术实现

首先验证用户是否输入端口号

  1. if (argc != 2) {
  2. printf("Usage: %s <port>\n", argv[0]);
  3. exit(1);
  4. }

如果输入端口号则验证输入端口号是否合法

  1. int port = atoi(argv[1]);
  2. if (port < 1 || port > 65535) {
  3. fprintf(stderr, "Error: Invalid port number. Port number should be between 1 and 65535.\n");
  4. return 1;
  5. }

接下来创建服务器端套接字

  1. int server_socket = socket(AF_INET, SOCK_STREAM, 0);
  2. struct sockaddr_in server_address;
  3. bzero(&server_address, sizeof(server_address));
  4. server_address.sin_family = AF_INET;
  5. server_address.sin_port = htons(port);
  6. server_address.sin_addr.s_addr = INADDR_ANY;
  7. bind(server_socket, (struct sockaddr *)&server_address, sizeof(server_address));
  8. listen(server_socket, MAX_CONNECTIONS);
  9. printf("Listening on port %d\n", port);

bzero将套接字初始为0,AF_INET表示该套接字使用的是ipv4协议,htons是将端口号转换成网络字节序,INADDR_ANY表示接收来自任意地址的连接。

最后bind函数将服务器套接字绑定起来,也就是说服务器端开始监听端口了。

listen只是用来限制同时连接服务器的最大用户数。

服务器循环接收客户端的连接并为其分配线程处理

  1. while (1) {
  2. int client_socket = accept(server_socket, NULL, NULL);
  3. printf("Accepted connection\n");
  4. if (fork() == 0) {
  5. //创建新进程处理连接的客户端
  6. }
  7. close(client_socket);//关闭连接套接字
  8. }

每有一个新的连接服务器使用fork创建一个新进程处理,完成之后关闭客户端套接字释放资源。

接收客户端消息

  1. char buffer[MAX_BUFFER];
  2. read(client_socket, buffer, sizeof(buffer));
  3. char method[256], url[256], version[256];
  4. sscanf(buffer, "%s %s %s", method, url, version);
  5. if (strcasecmp(method, "GET") != 0) {
  6. char *response = "HTTP/1.0 501 Not Implemented\r\n\r\n";
  7. write(client_socket, response, strlen(response));
  8. close(client_socket);
  9. exit(1);
  10. }

设置缓冲区大小,将客户端消息读入缓冲区,然后使用sscanf将其以空格形式分割成方法、url、http版本。判断请求方法是不是get方法,如果不是则向客户端发送501错误

将客户端请求发送到目标地址

  1. int port=80; //default port
  2. char host[256], path[256], *port_ptr;
  3. sscanf(url, "http://%[^/]%s", host, path);
  4. // Check if the URL contains a port number
  5. port_ptr = strchr(host, ':');
  6. if (port_ptr) {
  7. *port_ptr = '\0'; // Null-terminate the host string
  8. port = atoi(port_ptr + 1); // Convert the port number to an integer
  9. }
  10. printf("Host: %s:%d\nPath: %s\n", host, port, path);
  11. struct hostent *server = gethostbyname(host);
  12. int target_socket = socket(AF_INET, SOCK_STREAM, 0);
  13. struct sockaddr_in target_address;
  14. bzero(&target_address, sizeof(target_address));
  15. target_address.sin_family = AF_INET;
  16. target_address.sin_port = htons(port);
  17. bcopy((char *)server->h_addr_list[0], (char *)&target_address.sin_addr.s_addr, server->h_length);
  18. connect(target_socket, (struct sockaddr *)&target_address, sizeof(target_address));
  19. char request[MAX_BUFFER];
  20. sprintf(request, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", path, host);
  21. write(target_socket, request, strlen(request));

默认网站端口是80,将客户端发来的url拆成主机地址和端口号,如果url不含有端口号则使用默认端口号80,否则使用url中给出的端口号。

创建与目标地址连接的套接字,gethostbyname是解析域名的库函数,bcopy将gethostbyname解析到的ip地址复制到target_address套接字中,然后connect连接,发送request请求,注意http报文格式。

接收目标地址响应并反馈给客户端

  1. while (1) {
  2. bzero(buffer, sizeof(buffer));
  3. int bytes = read(target_socket, buffer, sizeof(buffer) - 1);
  4. if (bytes <= 0) {
  5. break;
  6. }
  7. write(client_socket, buffer, bytes);
  8. }
  9. close(client_socket);
  10. close(target_socket);
  11. exit(0);

接收比较简单,直接把目标地址的响应全部发给客户端就行了。

由于是HTTP 1.0,最后不要忘记释放所有资源哦


运行效果

假设gcc编译生成proxy可执行文件,终端输入./proxy 8888运行程序

在火狐里面设置一下代理

输入http://baidu.com访问一下,成功访问到百度的html页面

也可以进行telnet测试

也是成功返回了百度的html


源码

源码放在下面了,大家慢慢享用 ^_^

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <sys/socket.h>
  6. #include <netinet/in.h>
  7. #include <netdb.h>
  8. #include <arpa/inet.h>
  9. #define MAX_BUFFER 4096
  10. #define MAX_CONNECTIONS 100
  11. int main(int argc, char *argv[]) {
  12. if (argc != 2) {
  13. printf("Usage: %s <port>\n", argv[0]);
  14. exit(1);
  15. }
  16. int port = atoi(argv[1]);
  17. if (port < 1 || port > 65535) {
  18. fprintf(stderr, "Error: Invalid port number. Port number should be between 1 and 65535.\n");
  19. return 1;
  20. }
  21. int server_socket = socket(AF_INET, SOCK_STREAM, 0);
  22. struct sockaddr_in server_address;
  23. bzero(&server_address, sizeof(server_address));
  24. server_address.sin_family = AF_INET;
  25. server_address.sin_port = htons(port);
  26. server_address.sin_addr.s_addr = INADDR_ANY;
  27. bind(server_socket, (struct sockaddr *)&server_address, sizeof(server_address));
  28. listen(server_socket, MAX_CONNECTIONS);
  29. printf("Listening on port %d\n", port);
  30. while (1) {
  31. int client_socket = accept(server_socket, NULL, NULL);
  32. printf("Accepted connection\n");
  33. if (fork() == 0) {
  34. char buffer[MAX_BUFFER];
  35. read(client_socket, buffer, sizeof(buffer));
  36. char method[256], url[256], version[256];
  37. sscanf(buffer, "%s %s %s", method, url, version);
  38. if (strcasecmp(method, "GET") != 0) {
  39. char *response = "HTTP/1.0 501 Not Implemented\r\n\r\n";
  40. write(client_socket, response, strlen(response));
  41. close(client_socket);
  42. exit(1);
  43. }
  44. int port=80; //default port
  45. char host[256], path[256], *port_ptr;
  46. sscanf(url, "http://%[^/]%s", host, path);
  47. // Check if the URL contains a port number
  48. port_ptr = strchr(host, ':');
  49. if (port_ptr) {
  50. *port_ptr = '\0'; // Null-terminate the host string
  51. port = atoi(port_ptr + 1); // Convert the port number to an integer
  52. }
  53. printf("Host: %s:%d\nPath: %s\n", host, port, path);
  54. struct hostent *server = gethostbyname(host);
  55. int target_socket = socket(AF_INET, SOCK_STREAM, 0);
  56. struct sockaddr_in target_address;
  57. bzero(&target_address, sizeof(target_address));
  58. target_address.sin_family = AF_INET;
  59. target_address.sin_port = htons(port);
  60. bcopy((char *)server->h_addr_list[0], (char *)&target_address.sin_addr.s_addr, server->h_length);
  61. connect(target_socket, (struct sockaddr *)&target_address, sizeof(target_address));
  62. char request[MAX_BUFFER];
  63. sprintf(request, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", path, host);
  64. write(target_socket, request, strlen(request));
  65. while (1) {
  66. bzero(buffer, sizeof(buffer));
  67. int bytes = read(target_socket, buffer, sizeof(buffer) - 1);
  68. if (bytes <= 0) {
  69. break;
  70. }
  71. write(client_socket, buffer, bytes);
  72. }
  73. close(client_socket);
  74. close(target_socket);
  75. exit(0);
  76. }
  77. close(client_socket);
  78. }
  79. return 0;
  80. }
标签: linux c语言 前端

本文转载自: https://blog.csdn.net/weixin_73924291/article/details/140430741
版权归原作者 猿神起洞⋋ _ ⋌ 所有, 如有侵权,请联系我们删除。

“Linux下C语言完成简单Web代理服务器”的评论:

还没有评论