本文我们介绍下如何在Windows系统上开发一个代理本机流量的客户端,并且对接我们之前开发的Socks5服务端,实现整个代理的一条龙。对于Socks5代理的服务端的开发可以详见之前的文章。

本机流量劫持

通过系统开启手动代理

通过c#程序打开Windows的手动代理, 并且设置端口号和IP地址,这样只要客户端监听该端口就可以获取到本机的Http的流量数据。

通过对注册表的修改,来开启本机的手动代理,并且设置端口,Ip设置为本机,因为客户端是本机启动的,端口设置不冲突的即可。

黑名单则是设置哪些域名或者IP段不走代理,我们这里先把局域网的排除掉。

// 引入Windows API
[DllImport("wininet.dll")]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength); public const int INTERNET_OPTION_SETTINGS_CHANGED = 39;
public const int INTERNET_OPTION_REFRESH = 37; // 设置系统代理
public static void SetProxy(string proxyServer, bool enable)
{
const string userRoot = "HKEY_CURRENT_USER";
const string subkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
const string keyName = userRoot + "\\" + subkey; // 设置代理服务器地址和端口
Microsoft.Win32.Registry.SetValue(keyName, "ProxyServer", proxyServer); // 启用或禁用代理
Microsoft.Win32.Registry.SetValue(keyName, "ProxyEnable", enable ? 1 : 0); // 通知系统设置已更改
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
} /// <summary>
/// 黑名单
/// </summary>
/// <param name="exceptions"></param>
public static void SetProxyExceptions(string exceptions)
{
const string userRoot = "HKEY_CURRENT_USER";
const string subkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
const string keyName = userRoot + "\\" + subkey; Microsoft.Win32.Registry.SetValue(keyName, "ProxyOverride", exceptions);
}
 SystemProxy.SetProxy($"127.0.0.1:{App.SettingsModel.LocalPort}", true);
SystemProxy.SetProxyExceptions("localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*;<local>");
客户端开启对应TCP服务
 _tcpListener = new TcpListener(IPAddress.Any, _httpProxyPort);
_tcpListener.Start();

主要就是开启一个监听服务,让操作系统将对应的流量转发到我们的Socks5客户端。

将http报文进行解析,获取到请求的targetHost和Port,这样才能后面在和Socks5服务端握手的时候才能告知对方需要代理的远端信息

解析系统的Http请求

/// <summary>
/// 解析http请求报文,提取关键信息
/// </summary>
/// <param name="request">http请求</param>
/// <param name="host">请求主机</param>
/// <param name="port">请求主机端口号</param>
/// <returns>是否解析成功</returns>
private bool TryParseHttpRequest(string request, out string host, out int port)
{
host = null;
port = 0; // 解析 CONNECT 请求(如 CONNECT example.com:443 HTTP/1.1)
if (request.StartsWith("CONNECT"))
{
var parts = request.Split(' ')[1].Split(':');
host = parts[0];
port = int.Parse(parts[1]);
return true;
} // 2. 处理 GET/POST 请求(HTTP)
using (var reader = new StringReader(request))
{
string line;
while ((line = reader.ReadLine()) != null)
{
// 从 Host 头提取目标
if (line.StartsWith("Host:", StringComparison.OrdinalIgnoreCase))
{
var hostParts = line.Substring(5).Trim().Split(':');
host = hostParts[0];
if (hostParts.Length > 1)
port = int.Parse(hostParts[1]);
return true;
} // 空行表示头结束
if (string.IsNullOrWhiteSpace(line))
break;
}
} // 3. 旧式HTTP/1.0请求可能没有Host头,从URL解析
if (request.StartsWith("GET ") || request.StartsWith("POST "))
{
var url = request.Split(' ')[1];
if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
{
host = uri.Host;
port = uri.Port;
return true;
}
} return false;
}
远程Socks5服务端握手

这里我们采用的是带有认证的握手,需要服务端也开启认证配置,这里对于握手协议和认证还不清楚的可以看我集合的上面一篇文章

将http的远程信息作为握手信息与服务端建立连接,让服务端建立与目标的连接代理。

    /// <summary>
/// 带有身份验证的登录
/// </summary>
/// <param name="socks5Stream"></param>
/// <param name="targetHost"></param>
/// <param name="targetPort"></param>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private async Task PerformSocks5Handshake(NetworkStream socks5Stream,
string targetHost,
int targetPort,
string username,
string password)
{
// === 1. 协商认证方法 ===
// 发送支持的认证方法:无认证(0x00) 和 用户名/密码(0x02)
var authMethods = new byte[] { 0x05, 0x02, 0x00, 0x02 };
await socks5Stream.WriteAsync(authMethods, 0, authMethods.Length); // 读取服务器选择的认证方法
var authResponse = new byte[2];
await socks5Stream.ReadAsync(authResponse, 0, 2); if (authResponse[1] == 0xFF)
throw new Exception("SOCKS5服务器不支持任何提供的认证方法"); // === 2. 用户名/密码认证 ===
if (authResponse[1] == 0x02)
{
// 构建认证请求包
var authRequest = new byte[3 + username.Length + password.Length];
authRequest[0] = 0x01; // 认证子协商版本
authRequest[1] = (byte)username.Length;
Encoding.ASCII.GetBytes(username).CopyTo(authRequest, 2);
authRequest[2 + username.Length] = (byte)password.Length;
Encoding.ASCII.GetBytes(password).CopyTo(authRequest, 3 + username.Length); await socks5Stream.WriteAsync(authRequest, 0, authRequest.Length); // 读取认证响应
var authResult = new byte[2];
await socks5Stream.ReadAsync(authResult, 0, 2);
if (authResult[1] != 0x00)
throw new Exception("SOCKS5用户名/密码认证失败");
} // === 3. 发送连接请求 ===
var request = new byte[7 + targetHost.Length];
request[0] = 0x05; // VER
request[1] = 0x01; // CMD=CONNECT
request[2] = 0x00; // RSV
request[3] = 0x03; // ATYP=域名
request[4] = (byte)targetHost.Length;
Encoding.ASCII.GetBytes(targetHost).CopyTo(request, 5);
BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)targetPort)).CopyTo(request, 5 + targetHost.Length); await socks5Stream.WriteAsync(request, 0, request.Length); // === 4. 读取连接响应 ===
var response = new byte[10];
await socks5Stream.ReadAsync(response, 0, 10);
if (response[1] != 0x00)
throw new Exception($"SOCKS5连接失败 (状态码: {response[1]})");
}
交换流量

所谓的交换流量就是把远程代理的流量和本机的请求流量通过客户端作为中间人来转发

    private async Task ForwardDataAsync(NetworkStream src, NetworkStream dest)
{
var buffer = new byte[4096];
int bytesRead;
while ((bytesRead = await src.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await dest.WriteAsync(buffer, 0, bytesRead);
}
}
await Task.WhenAny(ForwardDataAsync(httpStream, socks5Stream),ForwardDataAsync(socks5Stream, httpStream)
代理流量显示

客户端也需要显示上传和下载流量的一些显示,我们这里简单点,因为我们之前开发的服务端是有基于用户的流量统计的,所以只需要把数据获取到就行,一般情况下为了性能,也可以做双端统计减少压力。

我们这里通过SSE将用户的流量信息基于用户名推送到客户端。

public async Task ConnectAsync(string remoteAddress, string userName)
{
_httpClient = new HttpClient(new HttpClientHandler
{
Proxy = new WebProxy($"http://{remoteAddress}:5000"), // 明确指定代理
});
_cts = new CancellationTokenSource(); try
{
using var response = await _httpClient
.GetAsync($"http://{remoteAddress}:5000/account/flow/{userName}", HttpCompletionOption.ResponseHeadersRead,_cts.Token);
if (response.IsSuccessStatusCode)
{
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
while (!_cts.Token.IsCancellationRequested)
{
var line = await reader.ReadLineAsync();
if (!string.IsNullOrEmpty(line))
{
MessageReceived?.Invoke(line);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"SSE连接错误: {ex.Message}");
}
}

验证

远端开启服务端

开启客户端

添加一些配置

包括:服务端IP,服务端Port,本地代理Port,用户名密码



开启客户端



可以看到代理成功,走的是本机的代理和服务端的代理请求

结尾

因为本身代理采用的修改系统代理设置是一种基础设置,所谓可能存在下面影响:

仅影响支持系统代理的应用(部分UWP应用、游戏等会绕过

无法代理非HTTP/HTTPS流量(如DNS、UDP

c#开发完整的Socks5代理客户端与服务端(已完结)的更多相关文章

  1. Netty入门——客户端与服务端通信

    Netty简介Netty是一个基于JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞.基于事件驱动.高性能.高可靠性和高可定制性.换句话说,Netty是一个NIO框架,使用它可以简单快速 ...

  2. 二、网络编程-socket之TCP协议开发客户端和服务端通信

    知识点:之前讲的udp协议传输数据是不安全的,不可靠不稳定的,tcp协议传输数据安全可靠,因为它们的通讯机制是不一样的.udp是用户数据报传输,也就是直接丢一个数据包给另外一个程序,就好比寄信给别人, ...

  3. app开发中如何利用sessionId来实现服务端与客户端保持回话

    app开发中如何利用sessionId来实现服务端与客户端保持回话 这个问题太过于常见,也过于简单,以至于大部分开发者根本没有关注过这个问题,我根据和我沟通的开发者中,总结出来常用的方法有以下几种: ...

  4. python网络编程TCP服务多客户端的服务端开发

    #服务多客户端TCP服务端开发 2 #方法说明 3 """ 4 bind(host,port)表示绑定端口号,host是ip地址,ip地址一般不进 行绑定,表示本机的任何 ...

  5. TLSv1.3 Support:主流 Web 客户端和服务端对 TLSv1.3 的支持情况

    TLSv1.3 Support:主流 Web 客户端和服务端对 TLSv1.3 的支持情况 请访问原文链接:https://sysin.org/blog/tlsv1-3-support/,查看最新版. ...

  6. SignalR 实现web浏览器客户端与服务端的推送功能

    SignalR 是一个集成的客户端与服务器库,基于浏览器的客户端和基于 ASP.NET 的服务器组件可以借助它来进行双向多步对话. 换句话说,该对话可不受限制地进行单个无状态请求/响应数据交换:它将继 ...

  7. Asp.Net MVC 模型验证详解-实现客户端、服务端双重验证

    概要 在asp.net webform开发中经常会对用户提交输入的信息进行校验,一般为了安全起见大家都会在客户端进行Javascript(利于交互).服务端双重校验(安全).书写校验代码是一个繁琐的过 ...

  8. Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信

    Android BLE与终端通信(三)--客户端与服务端通信过程以及实现数据通信 前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主 ...

  9. 客户端获取服务端自定义类数据 z

    客户端获取服务端自定义类数据 问题一:超时问题,在最后获取数据的时候突然提示服务超时,服务已断开 解决:配置文件添加: <bindings> <wsHttpBinding> & ...

  10. Netty4 学习笔记之二:客户端与服务端心跳 demo

    前言 在上一篇Netty demo 中,了解了Netty中的客户端和服务端之间的通信.这篇则介绍Netty中的心跳. 之前在Mina 中心跳的使用是通过继承 KeepAliveMessageFacto ...

随机推荐

  1. 为什么Raft算法是分布式系统的首选?

    背景 当今的数据中心和应用程序在高度动态的环境中运行,为了应对高度动态的环境,它们通过额外的服务器进行横向扩展,并且根据需求进行扩展和收缩.同时,服务器和网络故障也很常见. 因此,系统必须在正常操作期 ...

  2. Vue3 性能优化十大技巧:打造高性能应用的终极指南

    在现代前端开发中,性能优化是提升用户体验和系统效率的关键.Vue3 作为目前最流行的前端框架之一,提供了许多内置的性能优化工具和方法.本文将深入探讨 Vue3 中的十大性能优化技巧,帮助你从零开始构建 ...

  3. Echarts与Vue3中获取DOM节点可能出现的异常错误

    useTemplateRef 的简单介绍 官方:返回一个浅层 ref,其值将与模板中的具有匹配 ref attribute 的元素或组件同步. 参数匹配机制‌:useTemplateRe的参数需与模板 ...

  4. 解决bootstrapvalidator配合select2插件不能正常校验的问题

    我在使用bootstrapvalidator对select2插件进行校验时,出现了不能校验的问题,于是求助度娘,发现大多的解决方法是这样的: 1.添加一个隐藏域,将bootstrapvalidator ...

  5. ABAQUS阻尼设置

    结构阻尼 瑞利阻尼

  6. leetcode两数之和变种(找出所有满足总和的两个数)

    偶尔看到leetcode 的两数之和,但是之前遇到过两数之和的变种,之前一开始想不出来,后面看了别人的题解才想到解法,这里记录一下. 题目描述: 原leetcode题目描述 给定一个整数数组 nums ...

  7. Joker 智能开发平台再放大招,新作将彻底重塑开发模式

    -- 突破传统枷锁,引领开发模式全面革新 自前端可视化智能平台重磅发布后,其在行业内的影响力便如涟漪般迅速扩散.凭借着创新的设计理念和过硬的性能表现,这个平台为无数开发者和企业提供了高效且便捷的开发解 ...

  8. VMware网络虚拟化介绍(之一)

    2014年5月,在我加入VMware三个月之后,我涂鸦了一篇<扒一扒SDN的那些事儿>,当时放言如果阅读量过百就写续篇.后来果然阅读量没过百,也就80多的样子,其中好几份还是我自恋地进去查 ...

  9. 小白必看的java完整下载攻略!(在Typora中有图片参考)

    Java下载 在浏览器上搜索JDK(2024年最新版是22,本人下载的是21) 点击官网下载,会跳到Oracle官网,需要注册账号才可下载 根据自己的电脑型号选择下载(本人下载的是64的) 正常情况下 ...

  10. Dubbo学习系列之十九(Apollo分布式部署)

    说一个人是武林高手:十八般武艺,样样精通!如今,后端技术层出不穷,让人眼花缭乱,如果看官不能达到样样精通,至少 拿起方天画戟能耍几下才行,比如削个苹果.言归正传,配置中心属于基础设施,当然必须玩得溜, ...