咱们先从最基础的问题入手:轻量级HTTP/HTTPS代理到底是个啥?简单说,它就是个“轻量级中间人”——夹在你的设备(比如电脑、手机)和目标网站之间,一边接收你的访问请求,一边把请求转发给网站,再把网站的响应回传给你。关键在于“轻量”:体积小、占内存少、CPU消耗低,不会拖慢设备运行;同时还能搞定HTTPS的加密流量,甚至能对流量做捕获、修改、重定向这些操作,不管是做网络分析、安全测试,还是绕过部分网络限制,都能用得上。

一、先搞懂:轻量级HTTP/HTTPS代理的核心含义

很多人可能觉得“代理”就是“翻墙工具”,其实不然,代理的核心价值是“流量管控与中转”。而咱们说的这种“轻量级+SSL拦截+流量特性”的代理,有三个核心特点,用大白话讲清楚:

  • 轻量不“臃肿”:不像大型代理服务器那样需要复杂的部署和大量资源,它能在普通设备上跑,启动快、资源占用少——比如代码里会复用内存缓冲区、用缓存减少重复计算,都是为了“轻量高效”。

  • 能破HTTPS的“加密墙”:HTTP是明文传输,代理能直接看明白流量内容;但HTTPS是加密的,普通代理只能转发,没法查看或修改。这种代理靠“中间人攻击(MITM)”的思路,自己生成伪造证书,让你的设备信任它,这样就能解密HTTPS流量,完成拦截。

  • 流量“可操控”:不只是简单转发,还能做很多操作——比如捕获所有请求记录、修改请求/响应的内容、把访问A网站的流量转到B网站、自动下载网站上的图片/视频,甚至在特定操作时自动截图。

二、核心设计思路:如何做到“轻量”又“能打”?

设计的核心目标是“平衡轻量与功能”——不能为了轻量砍关键功能,也不能为了功能变得臃肿。核心思路有三个,咱们逐个说:

  1. 模块化拆分:各管一摊,不重复干活

把整个代理拆成几个独立模块,每个模块只做一件事,这样既好维护,也能减少资源浪费。从代码逻辑里能提炼出这几个核心模块:

  • 配置加载模块:负责读配置文件(比如哪些网站要拦截、用哪个端口启动代理、是否开启SSL拦截),把规则加载到内存里,后续不用重复读文件。

  • 连接处理模块:专门对接客户端(你的设备)和目标服务器,建立连接、管理连接生命周期(比如连接超时关闭、长连接复用)。

  • SSL拦截模块:处理HTTPS加密流量,包括生成/加载证书、伪造目标服务器证书、和客户端/服务器分别建立SSL连接(解密/加密流量)。

  • 流量管控模块:按配置规则处理流量——比如判断这个请求要不要捕获、要不要修改、要不要重定向,是核心功能模块。

  • 优化模块:包括线程池(处理多并发请求)、缓存(DNS解析结果缓存、证书缓存),都是为了提升性能,减少资源消耗。

  1. 资源复用+缓存:能省则省,提升效率

“轻量”的关键不是功能少,而是资源用得巧。这里的设计思路是“减少重复创建、重复计算”:

  • 内存复用:比如用固定大小的缓冲区接收/发送数据,不够用再动态扩容,而不是每次请求都创建新的缓冲区,避免内存碎片。

  • 缓存优化:① DNS缓存:第一次解析某个域名(比如www.baidu.com)后,把IP地址存起来,下次再访问就不用重新解析,省时间;② 证书缓存:第一次拦截某个网站的HTTPS流量时,会生成伪造证书,存起来下次复用,不用重复生成(证书生成是耗时操作)。

  • 线程池复用:用固定数量的线程处理客户端请求,而不是每个请求都创建新线程(创建线程耗资源),线程处理完一个请求再处理下一个,提升线程利用率。

  1. 按规则驱动:灵活管控,不浪费算力

所有流量处理都按提前配置的规则来,比如“只拦截某几个网站的POST请求”“只修改某类响应内容”,这样代理不用对所有流量都做复杂处理,只针对性工作,既灵活又高效。规则里会明确:针对哪个域名、哪个端口、哪种请求方式(GET/POST等)、做什么操作(捕获/修改/重定向)。

三、代码实现原理:从启动到处理请求的完整流程

...
enum {
	_INTERFACE,
	PORT,
	TLS_MITM,
	POOL_NTHREADS,
	TIMEOUT_CONNECT,
	TIMEOUT_TUNNEL,
	INIT_BUFFER_SIZE
};

typedef enum
{
	PROXY_TO_PAGE,
	PROXY_TO_CLIENT,
	CLIENT_TO_PROXY,
	PAGE_TO_PROXY
} PROXY_FLOW;

typedef enum 
{
	INIT,
	HEAD_REQUEST,
	BODY_REQUEST,
	HEAD_RESPONSE,
	BODY_RESPONSE,
	FINISHED,
	INIT_KEEPALIVE
} HTTP_STAGE;

typedef enum {
	SWITCHING_PROTOCOLS = 101
} HTTP_STATUS;

typedef enum  {
	UNKNOW,
	ALL,
	CONNECT,
	GET,
	PUT,
	POST,
	HEAD,
	DELETE_,
	OPTIONS,
	TRACE
} PROXY_METHOD;

typedef enum {
	DIRECT,
	CAPTURE,
	MODIFY_BODY_REQUEST,
	MODIFY_BODY_RESPONSE,
	REDIRECT,
	REJECT,
	SCREENSHOT,
	FAKE_TLS_EXT_HOSTNAME,
	REMOVE_HEADER_REQUEST,
	REMOVE_HEADER_RESPONSE,
	ADD_HEADER_REQUEST,
	ADD_HEADER_RESPONSE,
	DOWNLOAD_CONTENT
} PROXY_ACTION;

typedef struct {
	char url[MAX_URL_SIZE];
	char hostname[MAX_HOSTNAME_SIZE];
	char tls_ext_hostname[MAX_HOSTNAME_SIZE];
	int port;
	char method[8];
	char version_protocol;
} reqinfo, *preqinfo;

typedef struct {
	bool chunked;
	struct {
		bool keep_alive;
		bool upgrade;
	} connection;
	u_int content_length;
	char* content_length_offset;
	char content_type[MAX_STR_VALUE_HEADER_SIZE];
	char upgrade[MAX_STR_VALUE_HEADER_SIZE];
} headers, *pheaders;

typedef struct {
	char* pointer;
	u_int written;
	u_int size_block;
} buffer, *pbuffer;

typedef struct _domain_cache
{
	char domain[MAX_HOSTNAME_SIZE];
	u_long ip;
	struct _domain_cache* next;
} domain_cache, *pdomain_cache;

typedef struct 
{
	char hostname[MAX_HOSTNAME_SIZE];
	u_int port;
} redirect, *predirect;

typedef struct 
{
	char hostname[MAX_HOSTNAME_SIZE];
} fake_tls_ext_hostname, *pfake_tls_ext_hostname;

typedef struct 
{
	char content_type[24];
} download_content, *pdownload_content;

typedef struct 
{
	char* prefix;
	char* inject;
	u_int prefix_length;
	u_int inject_length;
} injection, *pinjection;

typedef struct 
{
	char header[2048];
	u_int header_length;
} head, *phead;

typedef struct _rule {
	PROXY_ACTION action;
	char domain[MAX_HOSTNAME_SIZE];
	char url[MAX_URL_SIZE];
	bool ssl;
	u_int port;
	u_int method;
	void* extra_data;
	struct _rule* next;
} rule, *prule;

typedef struct _action {
	PROXY_ACTION action;
	void* extra_data;
	struct _action* next;
} action, *paction;

typedef struct _client_connection {
	struct sockaddr_in client_addr;
	int client_socket;
} client_connection, *pclient_connection;

#ifdef OPENSSL
	char hex_map[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
	u_char alpn_protocol[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '1' };
	u_int lentgh = sizeof(alpn_protocol);

	typedef struct _certificate_cache
	{
		char cache_hash[200];
		X509* certificate;
		struct _certificate_cache* next;
	} certificate_cache, *pcertificate_cache;

	typedef struct {
		X509* certificate;
		SSL_CTX* context;
		SSL* connection;
	} ssl_layer, *pssl_layer;
#endif

typedef struct {
	int error_code;
	int sock_browser;
	int sock_page;
	int response_code;
	client_connection connection;
	PROXY_METHOD method;
	PROXY_ACTION action;
	HTTP_STAGE stage;
	buffer head_request;
	buffer body_request;
	buffer head_response;
	buffer body_response;
	paction actions;
	reqinfo info;
	headers headers_request;
	headers headers_response;
	u_long thread_id;
	bool ssl;
	bool connection_established;
	u_int nrequests;
	#ifdef OPENSSL
		pssl_layer ssl_page;
		pssl_layer ssl_browser;
		char* ssl_error;
	#endif
} client, *pclient;

struct {
	char _interface[20];
	u_int port;
	u_int nthreads;
	u_long timeout_connect;
	u_long timeout_tunnel;
	u_int init_buffer_size;
	u_int tls_mitm;
	char* cfg_file;
	#ifdef OPENSSL
		X509* certificate;
		EVP_PKEY* client_key;
		EVP_PKEY* certificate_key;
 		X509_NAME* ca_issuer;
 		#ifdef CA_MEM_CACHE
			pcertificate_cache certificate_cache;
		#endif
	#endif
	#ifdef DEBUG
		pthread_mutex_t stdout_lock;
	#endif
	#ifdef DNS_MEM_CACHE
		pthread_mutex_t domain_cache_lock;
	#endif
	pthread_mutex_t certificate_cache_lock;
	pdomain_cache domain_cache;
	int socket;
	prule proxy_rules;
	buffer pac_request;
} proxy_config, *pproxy_config;

struct {
	char* phttpsd2s;
	char* parse_http_url;
	char* parse_http_url_port;
	char* parse_https_url;
	char* parse_https_url_port;
	char* parse_response_code;
	char* transfer_encoding;
	char* proxy_connection;
	char* keep_alive;
	char* content_lentgh;
	char* content_lentghinject;
	char* content_type;
	char* upgrade;
	char* websocket;
	char* hostname;
	char* parse_hostname;
	char* connection;
	char* screenshot;
	char* format_int_value_header;
	char* format_str_value_header;
	char* chunked;
	char* endchunk;
	char* accept_encoding;
	char* head_connection_established;
	char* http_get;
	char* http_post;
	char* http_put;
	char* http_connect;
	char* http_head;
	char* http_delete;
	char* head_pac_response;
	char* pac_response_1;
	char* pac_response_2;
	char* pac_response_3;
} const_char, *pconst_char;

char* PROXY_ACTIONS[] = { [DIRECT] = "DIRECT", [CAPTURE] = "CAPTURE", [MODIFY_BODY_REQUEST] = "MODIFY_BODY_REQUEST", [MODIFY_BODY_RESPONSE] = "MODIFY_BODY_RESPONSE", [REDIRECT] = "REDIRECT", 
						  [REJECT] = "REJECT", [SCREENSHOT] = "SCREENSHOT", [FAKE_TLS_EXT_HOSTNAME] = "FAKE_TLS_EXT_HOSTNAME", 
						  [REMOVE_HEADER_REQUEST] = "REMOVE_HEADER_REQUEST", [REMOVE_HEADER_RESPONSE] = "REMOVE_HEADER_RESPONSE",
						  [ADD_HEADER_REQUEST] = "ADD_HEADER_REQUEST", [ADD_HEADER_RESPONSE] = "ADD_HEADER_RESPONSE", [DOWNLOAD_CONTENT] = "DOWNLOAD_CONTENT" };
char* PROXY_METHODS[] = { [UNKNOW] = "UNKNOW", [ALL] = "ALL", [CONNECT] = "CONNECT", [GET] = "GET", [PUT] = "PUT", [POST] = "POST", [HEAD] = "HEAD", [DELETE_] = "DELETE", [OPTIONS] = "OPTIONS", [TRACE] = "TRACE" };
char* KEY_SETTING[] = {   [_INTERFACE] = "INTERFACE", [PORT] = "PORT", [POOL_NTHREADS] = "POOL_NTHREADS", [TIMEOUT_CONNECT] = "TIMEOUT_CONNECT",
						  [TIMEOUT_TUNNEL] = "TIMEOUT_TUNNEL", [INIT_BUFFER_SIZE] = "INIT_BUFFER_SIZE", [TLS_MITM] = "TLS_MITM"};

...

void load_strings()
{
	strcpy(proxy_config._interface, __INTERFACE_BIND);
	proxy_config.port = __PORT_BIND;
	proxy_config.timeout_connect = __TIMEOUT_CONNECT * 1000;
	proxy_config.timeout_tunnel = __TIMEOUT_TUNNEL * 1000;
	proxy_config.init_buffer_size = __INIT_SIZE_BUFFER;
	proxy_config.cfg_file = __CONFIG_FILE;
#ifdef THREAD_POOL
	proxy_config.nthreads = __POOL_NTHREAD;
#endif
	const_char.http_get = "GET";
	const_char.http_post = "POST";
	const_char.http_connect = "CONNECT";
	const_char.http_put = "PUT";
	const_char.http_head = "HEAD";
	const_char.http_delete = "DELETE";
	const_char.parse_http_url = "http://%" IN2STR(MAX_HOSTNAME_SIZE) "[^/]%" IN2STR(MAX_URL_SIZE) "s";
	const_char.parse_http_url_port = "http://%" IN2STR(MAX_HOSTNAME_SIZE) "[^:]:%d%" IN2STR(MAX_URL_SIZE) "[^\n]";
	const_char.parse_https_url = "https://%" IN2STR(MAX_HOSTNAME_SIZE) "[^/]%" IN2STR(MAX_URL_SIZE) "s";
	const_char.parse_https_url_port = "https://%" IN2STR(MAX_HOSTNAME_SIZE) "[^:]:%d%" IN2STR(MAX_URL_SIZE) "[^\n]";
	const_char.parse_response_code = "%*s %d %*s\r\n";
	const_char.transfer_encoding = "Transfer-Encoding:";
	const_char.content_lentgh = "Content-Length:";
	const_char.content_lentghinject = "Content-Length: %d";
	const_char.content_type = "Content-Type:";
	const_char.proxy_connection = "Proxy-Connection:";
	const_char.connection = "Connection:";
	const_char.accept_encoding = "Accept-Encoding:";
	const_char.upgrade = "Upgrade:";
	const_char.hostname = "Host:";
	const_char.parse_hostname = "Host: %s\r\n\r\n";
	const_char.format_int_value_header = "%*s %d";
	const_char.format_str_value_header = "%*s %" IN2STR(MAX_STR_VALUE_HEADER_SIZE) "s";
	const_char.chunked = "chunked";
	const_char.websocket = "websocket";
	const_char.keep_alive = "keep-alive";
	const_char.endchunk = "0\r\n\r\n";
	const_char.screenshot = "sc";
	const_char.head_pac_response = "HTTP/1.0 200\r\nContent-Type: application/x-ns-proxy-autoconfig\r\nConnection: Close\r\nContent-Length: %d\r\n\r\n";
	const_char.head_connection_established = "HTTP/1.1 200 Connection established\r\n\r\n";
	const_char.pac_response_1 = "function FindProxyForURL(url, host){if(";
	const_char.pac_response_2 = "dnsDomainIs(host, \"%s\")||";
	const_char.pac_response_3 = "){return \"PROXY %s:%d\"}return \"DIRECT\"}";
	const_char.phttpsd2s = "https://";
}

...

bool start_http_proxy()
{
...

	dummy_struct_size = sizeof(proxy_addr);
	proxy_addr.sin_family = 2;
	proxy_addr.sin_port = htons(proxy_config.port);
	proxy_addr.sin_addr.s_addr = inet_addr(proxy_config._interface);
	#ifdef _WIN32
		CHECK_OP(!WSAStartup(MAKEWORD(2, 2), &wsd))
	#endif
	IF_GZERO(proxy_config.socket = socket(2, 1, 0))
	CHECK_OP(!setsockopt(proxy_config.socket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt_reuse, sizeof(int)))
	CHECK_OP(!bind(proxy_config.socket, (struct sockaddr*)&proxy_addr, sizeof(proxy_addr)))
	CHECK_OP(!listen(proxy_config.socket, __MAX_CLIENT))
	#ifdef THREAD_POOL
		IF_GZERO(create_thread_pool(&pool, proxy_config.nthreads))
	#endif
	#ifdef OPENSSL
		SSLeay_add_ssl_algorithms();
		if(!load_ssl_files()){
			generate_ssl_files();
		}
	#endif
	while (true){
		connection.client_socket = accept(proxy_config.socket, (struct sockaddr*)&connection.client_addr, &dummy_struct_size);
		#ifdef THREAD_POOL
			add_pool_job(pool, client_socket);
		#else
			#ifdef _WIN32
				CloseHandle(CreateThread(0, 0, (LPTHREAD_START_ROUTINE)handle_client, (void*)&connection, 0, 0));
			#else
				pthread_create(&id, 0, (void*)handle_client, (void*)&connection);
				pthread_detach(id);
			#endif
		#endif
	}
	__exception:
		LOGGER("->it was an error starting proxy, error (%lu).", GET_SYSTEM_ERROR())
	RETN_OK
}

int main(int argc, char** argv)
{
	#ifdef DNS_MEM_CACHE
		pthread_mutex_init(&proxy_config.domain_cache_lock, 0);
	#endif
	pthread_mutex_init(&proxy_config.certificate_cache_lock, 0);
	#ifdef DEBUG
		pthread_mutex_init(&proxy_config.stdout_lock, 0);
	#endif
	LOGGER("->proxy by %s V:%s", __AUTHOR, __VERSION)
	load_strings();
	if(argc == 2){
		proxy_config.cfg_file = argv[1];
	}
	IF_GZERO(load_proxy_rules())
	start_http_proxy();
...
}


If you need the complete source code, please add the WeChat number (c17865354792)

咱们把代码里的核心流程提炼成“大白话步骤”,不用纠结具体函数名,重点看“它到底干了啥”:

  1. 启动流程:初始化准备工作

  2. 初始化基础配置:比如默认的监听端口、缓冲区大小、超时时间,加载一些固定的HTTP相关字符串(比如“GET”“POST”“Content-Length”这些请求头字段)。

  3. 加载配置规则:读配置文件,把里面的拦截规则、重定向规则、SSL拦截开关等加载到内存,生成对应的规则列表。

  4. 初始化SSL相关(如果开启SSL拦截):加载或生成根证书(这个证书需要你手动安装到客户端,否则客户端会提示“证书不安全”),准备好证书生成、签名的工具。

  5. 创建监听Socket:绑定指定的IP和端口,开始监听客户端的连接请求。

  6. 初始化线程池(如果开启):创建固定数量的线程,等待处理客户端请求。

  7. 核心流程:处理一个客户端请求

这是最关键的部分,咱们用“客户端→代理→目标服务器”的交互逻辑,拆解成步骤:

  1. 接收客户端连接:监听Socket收到客户端的连接请求后,创建一个新的Socket和客户端建立连接,记录客户端的IP和端口。

  2. 读取客户端请求:通过新Socket读取客户端发送的HTTP/HTTPS请求(比如“GET https://www.xxx.com HTTP/1.1”),解析请求里的关键信息——请求方式、目标域名、端口、请求头、请求体。

  3. 匹配规则:根据解析出的信息(域名、端口、请求方式),去匹配之前加载的规则,判断这个请求需要做什么操作(比如捕获、修改、重定向,还是直接转发)。

  4. 处理HTTPS请求(如果是HTTPS):

  • 如果开启SSL拦截:代理先伪装成目标服务器,用之前生成的伪造证书和客户端建立SSL连接,解密客户端的请求(拿到明文);然后代理再以客户端的身份,和真正的目标服务器建立SSL连接,把解密后的请求发给服务器。

  • 如果不开启SSL拦截:代理直接把客户端的加密请求转发给目标服务器,不做解密。

  1. 处理流量(按规则执行操作):
  • 捕获:把请求头、请求体、后续的响应头、响应体保存到文件里。

  • 修改:比如在请求体里插入一段内容,或者删除某个请求头(比如“Accept-Encoding”),再或者修改响应体的内容。

  • 重定向:把目标域名改成其他域名,比如把访问“www.xxx.com”的请求,转发到“www.yyy.com”。

  • 下载:如果响应是图片、视频,自动把这些内容保存到本地文件夹。

  1. 转发请求/响应:把处理后的请求发给目标服务器,接收服务器的响应;如果需要修改响应,就先修改再转发给客户端。

  2. 关闭连接或保持长连接:如果客户端请求里要求“Keep-Alive”(长连接),就保持连接,等待客户端的下一个请求;否则关闭连接,释放资源。

  3. 关键技术点:SSL拦截的核心逻辑

HTTPS拦截是这个代理的核心难点,咱们再拆解开讲清楚,用“角色扮演”的思路理解:

  • 第一步:代理要拿到“扮演目标服务器”的资格——生成根证书,客户端安装并信任这个根证书后,才会信任代理后续生成的所有伪造证书。

  • 第二步:当客户端访问HTTPS网站时,代理先拦截客户端的“握手请求”(客户端要和服务器建立SSL连接的请求)。

  • 第三步:代理用根证书,动态生成一个和目标服务器域名一致的伪造证书,发给客户端,假装自己就是目标服务器。

  • 第四步:客户端信任伪造证书后,和代理建立SSL连接,把加密的请求发给代理——代理用自己的私钥解密,拿到明文请求。

  • 第五步:代理再以客户端的身份,和真正的目标服务器建立SSL连接,把明文请求加密后发给服务器,拿到服务器的加密响应。

  • 第六步:代理解密服务器的响应,按规则处理(比如修改),再加密后发给客户端。

这里的关键是“客户端必须信任代理的根证书”,否则客户端会识别出伪造证书,提示“网站不安全”,拒绝连接。

四、步骤4:启动代理服务(小白照做即可)

_lite: debug_
gcc proxy.c -o proxy.out -pthread -Wall -DDEBUG
_lite: debug, ssl_
gcc proxy.c -o proxy.exe -pthread -lssl -lcrypto -Wall -DOPENSSL -DDEBUG
_full: debug, pool, cache_
gcc proxy.c -o proxy.out -pthread -lssl -lcrypto -Wall -DDEBUG -DDNS_MEM_CACHE -DCA_MEM_CACHE -DOPENSSL -DTHREAD_POOL

_to windows add:
-lgdi32 -lws2_32 -lGdiplus

1. 启动代理

终端输入命令,回车启动:

./proxy

2. 启动成功验收(关键!看这3点)

  1. 终端会输出以下日志(没有红色报错):
    ->proxy by RedToor V:2025.12.21.1358
    -->loading configuration: h2polar.cfg...
    -->interface 127.0.0.1 port 51234 nthreads 20 timeout-connect 4000 timeout-tunnel 30000 init-buffer-size 8192 max-clients 100 tls-mitm 1
    -->set your http/s client with http://127.0.0.1:51234/h2Polar.pac (pac script) url
    -->rules loaded:
    (#1)[CAPTURE] [ALL] [YES] [443] [www.baidu.com] [/*]
    (#2)[DOWNLOAD_CONTENT] [ALL] [NO] [80] [*] [/*] [image/*]
    (#3)[REDIRECT] [ALL] [NO] [80] [test.com] [/*] [www.baidu.com] [80]
    
  2. 终端不要关闭(关闭则代理停止),重新打开一个新终端(Ctrl+Alt+T),进入「~/proxy_test」目录,输入 ls,能看到新增2个证书文件:
    • proxy.cer(根证书,后续SSL拦截要用)
    • proxy.key(私钥,无需手动操作)
  3. 满足以上两点,说明代理启动成功!

常见启动失败排查(小白必看)

  • 报错「Address already in use」:端口51234被占用,修改「proxy.cfg」中的PORT(比如改成8888),重新编译启动即可。
  • 报错「proxy.cfg not found」:配置文件不在当前目录,确保「proxy.cfg」和「proxy」在同一个文件夹(~/proxy_test)。

配置客户端(让浏览器走代理,小白优先配Chrome)

1. 本地Chrome浏览器配置(最简单,小白首选)

步骤1:打开系统代理设置
  1. 打开Chrome浏览器,点击右上角「三个点」→「设置」→ 拉到最下面点击「高级」→ 找到「系统」→ 点击「打开您的计算机的代理设置」。
  2. 弹出Linux系统的「网络代理」窗口。
步骤2:手动配置代理
  1. 选择「手动」(关闭「自动」)。
  2. 填写代理信息:
    • HTTP代理:地址填「127.0.0.1」,端口填「51234」(和配置文件一致)。
    • HTTPS代理:地址填「127.0.0.1」,端口填「51234」。
    • 忽略本地地址:保持默认即可。
  3. 点击「应用到整个系统」,关闭窗口。

2. 验证客户端配置

打开Chrome,在地址栏输入 http://127.0.0.1:51234,若显示「无法访问此网站」(正常,代理不提供网页服务),说明配置路径正确;若提示「代理服务器无响应」,则配置有误,重新检查端口和地址。

五、相关领域知识点:搞懂代理背后的“底层逻辑”

这个轻量级代理涉及多个领域的基础知识点,搞懂这些,就能更清楚代理的设计思路,咱们逐个总结:

  1. 网络协议基础:HTTP/HTTPS与TCP/Socket
  • HTTP/HTTPS协议:HTTP是明文传输的应用层协议,所有请求/响应都是明文;HTTPS是HTTP+SSL/TLS,在应用层和传输层之间加了一层加密,保证数据安全。代理的核心就是解析和修改这两种协议的数据包。

  • TCP/Socket:TCP是可靠的传输层协议,代理和客户端、代理和服务器之间的连接都是基于TCP的;Socket是“网络编程接口”,代理就是通过Socket来建立连接、发送/接收数据的——比如代码里的socket()、bind()、listen()、accept()这些操作,都是Socket编程的基础。

  1. 加密与安全:SSL/TLS协议与中间人攻击
  • SSL/TLS协议:是HTTPS的加密基础,负责身份验证(确认对方是真正的目标服务器)和数据加密(防止数据被篡改、窃取)。核心是“证书机制”——服务器用证书证明自己的身份,客户端验证证书合法后,再和服务器协商加密密钥。

  • 中间人攻击(MITM):正常的HTTPS是“客户端→服务器”直接加密通信,中间人攻击就是代理插入两者之间,同时和双方建立连接,解密并修改数据。这种攻击本身是恶意的,但在代理场景下,是“授权的中间人攻击”(用户手动安装根证书,授权代理拦截)。

  • 证书相关:根证书是信任链的顶端,代理的根证书被客户端信任后,由根证书签名的伪造证书也会被信任;证书里包含域名、公钥、签名等信息,代理伪造证书时,会复制目标服务器证书的域名等信息,只替换公钥和签名(用代理自己的根证书签名)。

  1. 并发编程:线程池与互斥锁
  • 线程池:如果每个客户端请求都创建一个新线程,大量请求会导致线程过多,消耗大量内存和CPU(线程切换成本高)。线程池提前创建固定数量的线程,循环处理请求,减少线程创建/销毁的开销,提升并发处理能力——这是“轻量级”代理处理多并发的关键。

  • 互斥锁:多个线程同时操作共享资源(比如缓存、日志文件)时,会出现“数据混乱”(比如两个线程同时写日志,内容会重叠)。互斥锁的作用是“保证同一时间只有一个线程能操作共享资源”——比如代码里的pthread_mutex_lock()、pthread_mutex_unlock(),就是用来加锁、解锁的。

  1. 缓存策略:DNS缓存与证书缓存
  • DNS缓存:把域名解析后的IP地址存起来,下次再访问同一个域名时,不用再向DNS服务器发送解析请求,减少网络延迟和DNS服务器的压力。

  • 证书缓存:生成伪造证书是耗时操作(需要加密计算),把第一次生成的证书存起来,下次访问同一个网站时直接复用,提升SSL拦截的效率。

六、总结

最后咱们总结一下,这个轻量级代理的设计之所以高效、轻量,核心是做到了这几点:

  • 资源高效利用:通过内存复用、线程池、缓存,减少重复创建、重复计算,降低内存和CPU消耗——这是“轻量”的核心。

  • 模块化架构:把配置、连接、SSL拦截、流量处理拆分成独立模块,每个模块只做一件事,既好维护,又能按需扩展功能(比如新增一种流量处理操作)。

  • 规则驱动的灵活管控:不用修改代码,只改配置文件就能实现不同的流量管控需求,适配不同的使用场景(比如渗透测试、网络分析)。

  • SSL拦截的高效实现:通过证书缓存、动态生成伪造证书,在保证拦截功能的同时,尽量减少性能损耗。

这种代理虽然“轻量”,但应用场景很广:比如安全测试人员用它捕获和分析应用的网络请求,找漏洞;开发人员用它修改请求/响应,调试接口;网络管理员用它管控内部网络的流量,拦截危险请求;甚至普通用户用它绕过某些网络限制(比如重定向到可访问的域名)。核心价值就是“小而精”——体积小、资源省,但能精准搞定HTTPS拦截和流量管控的核心需求。

Welcome to follow WeChat official account【程序猿编码

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐