c#开发完整的Socks5代理客户端与服务端(已完结)
本文我们介绍下如何在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代理客户端与服务端(已完结)的更多相关文章
- Netty入门——客户端与服务端通信
Netty简介Netty是一个基于JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞.基于事件驱动.高性能.高可靠性和高可定制性.换句话说,Netty是一个NIO框架,使用它可以简单快速 ...
- 二、网络编程-socket之TCP协议开发客户端和服务端通信
知识点:之前讲的udp协议传输数据是不安全的,不可靠不稳定的,tcp协议传输数据安全可靠,因为它们的通讯机制是不一样的.udp是用户数据报传输,也就是直接丢一个数据包给另外一个程序,就好比寄信给别人, ...
- app开发中如何利用sessionId来实现服务端与客户端保持回话
app开发中如何利用sessionId来实现服务端与客户端保持回话 这个问题太过于常见,也过于简单,以至于大部分开发者根本没有关注过这个问题,我根据和我沟通的开发者中,总结出来常用的方法有以下几种: ...
- python网络编程TCP服务多客户端的服务端开发
#服务多客户端TCP服务端开发 2 #方法说明 3 """ 4 bind(host,port)表示绑定端口号,host是ip地址,ip地址一般不进 行绑定,表示本机的任何 ...
- TLSv1.3 Support:主流 Web 客户端和服务端对 TLSv1.3 的支持情况
TLSv1.3 Support:主流 Web 客户端和服务端对 TLSv1.3 的支持情况 请访问原文链接:https://sysin.org/blog/tlsv1-3-support/,查看最新版. ...
- SignalR 实现web浏览器客户端与服务端的推送功能
SignalR 是一个集成的客户端与服务器库,基于浏览器的客户端和基于 ASP.NET 的服务器组件可以借助它来进行双向多步对话. 换句话说,该对话可不受限制地进行单个无状态请求/响应数据交换:它将继 ...
- Asp.Net MVC 模型验证详解-实现客户端、服务端双重验证
概要 在asp.net webform开发中经常会对用户提交输入的信息进行校验,一般为了安全起见大家都会在客户端进行Javascript(利于交互).服务端双重校验(安全).书写校验代码是一个繁琐的过 ...
- Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信
Android BLE与终端通信(三)--客户端与服务端通信过程以及实现数据通信 前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主 ...
- 客户端获取服务端自定义类数据 z
客户端获取服务端自定义类数据 问题一:超时问题,在最后获取数据的时候突然提示服务超时,服务已断开 解决:配置文件添加: <bindings> <wsHttpBinding> & ...
- Netty4 学习笔记之二:客户端与服务端心跳 demo
前言 在上一篇Netty demo 中,了解了Netty中的客户端和服务端之间的通信.这篇则介绍Netty中的心跳. 之前在Mina 中心跳的使用是通过继承 KeepAliveMessageFacto ...
随机推荐
- [NOI2014] 购票 题解
首先发现 \(p_x\times dis(x,y)+q_x\) 异常像是能斜率优化的样子,那先把求 \(f_x\) 的式子写出来(下设 \(d_x\) 表示 \(x\) 到根的距离): \[f_x=\ ...
- Deepseek学习随笔(6)--- API 开发与自动化
获取 API Key 要开始使用 DeepSeek 的 API,你首先需要获取 API Key: 登录 DeepSeek 控制台 . 进入 API 管理 页面,生成 API Key. API 调用示例 ...
- 用 Dockerfile 创建镜像的基本步骤有哪些?
使用 Dockerfile 创建镜像的基本步骤如下: 定义基础镜像 在 Dockerfile 的开头,使用 FROM 指令指定一个基础镜像.例如: FROM ubuntu:latest 这表示基于最新 ...
- Elasticsearch搜索引擎学习笔记(三)
索引的一些操作 集群健康 GET /_cluster/health 创建索引 PUT /index_test { "settings": { "index": ...
- abaqus建模时突发意外,软件闪退怎么才能找回操作?
abaqus/CAE 建模的时候可能经常由于各种各样的原因闪退(中断.卡住.未响应等等.) 这是很让人崩溃的时候,一个良好的习惯就是经常Ctrl+S,并且操作的时候不要太急,否则abaqus容易反应不 ...
- 通过 C# 打印Word文档
Word文档是日常办公和学习中不可或缺的一部分.比如在商务往来中,经常需要打印 Word 文档用于撰写和传递正式的商务信函.合作协议.项目提案等.打印出来的文档便于双方签字盖章,具有法律效力和正式性. ...
- QT5.14.1+Win7 64+Oracle11gR2 Qt连接数据库
原文链接 1.QT5.14下OCI驱动编译完整步骤 1.安装qt的时候手动选择安装源码资源(默认不安装Source的) 2.进入QT安装目录下E:\Qt5.14\5.14.0\Src\qtbase\s ...
- node_modules/@umijs/runtime" does not exist in container.
使用 umi 脚手架搭建项目,启动时报错 node_modules/@umijs/runtime" does not exist in container. 出现问题 .umi 是临时文件夹 ...
- 简易TXT文本小说阅读器
上次学习爬取小说保存到txt文本文件,方便离线阅读,现在做一个简易TXT文本小说阅读器,支持手动翻页和自动翻页阅读. 废话不多说,直接上代码,实践下. read_txt.py: import time ...
- 使用Python完成设备巡检
在企业网络中,设备巡检是保持网络稳定性和安全性的核心任务.无论是路由器.交换机,还是防火墙和服务器等设备,都需要定期进行巡检,以确保网络设施的正常运行.然而,传统的设备巡检通常是通过手动登录设备.查看 ...