public static byte[] BuildConnectRequest(IPEndPoint destination)
{
// VER(1) + CMD(1) + RSV(1) + ATYP(1) + IPv4(4) + PORT(2) = 10 bytes
var buf = new byte[10];
buf[0] = 0x05; // VER
buf[1] = 0x01; // CMD = CONNECT
buf[2] = 0x00; // RSV
buf[3] = 0x01; // ATYP = IPv4
byte[] ipBytes = destination.Address.GetAddressBytes(); // 4 bytes
Buffer.BlockCopy(ipBytes, 0, buf, 4, 4);
// Port: Network Byte Order (Big Endian)
byte[] portBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)destination.Port));
Buffer.BlockCopy(portBytes, 0, buf, 8, 2);
return buf;
}
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
public class Socks5Client
{
private readonly string _proxyHost;
private readonly int _proxyPort;
private readonly string _username;
private readonly string _password;
public Socks5Client(string proxyHost, int proxyPort, string username = null, string password = null)
{
_proxyHost = proxyHost; _proxyPort = proxyPort; _username = username; _password = password;
}
public async Task ConnectAsync(string destHost, int destPort, CancellationToken ct = default)
{
var tcp = new TcpClient();
await tcp.ConnectAsync(_proxyHost, _proxyPort, ct);
var stream = tcp.GetStream();
// 1. 協商認證方法
await NegotiateAuthAsync(stream, ct);
// 2. 發送 CONNECT 請求
await SendConnectRequestAsync(stream, destHost, destPort, ct);
// 3. 讀取回應
await ReadConnectResponseAsync(stream, ct);
return stream; // 成功後直接用此 Stream 收發資料
}
private async Task NegotiateAuthAsync(NetworkStream stream, CancellationToken ct)
{
// 傳送支援的方法:無認證(0) + 帳密(2)
var methods = new byte[] { 0x05, 0x02, 0x00, 0x02 };
await stream.WriteAsync(methods, 0, methods.Length, ct);
await stream.FlushAsync(ct);
// 讀取伺服器選擇
byte[] resp = new byte[2];
await ReadExactAsync(stream, resp, 0, 2, ct);
if (resp[0] != 0x05) throw new IOException("SOCKS 版本不符");
if (resp[1] == 0xFF) throw new IOException("無支援的認證方法");
// 若選擇帳密認證 (0x02)
if (resp[1] == 0x02)
{
if (string.IsNullOrEmpty(_username)) throw new InvalidOperationException("需要帳密認證但未提供");
var auth = new byte[3 + _username.Length + 1 + _password.Length];
auth[0] = 0x01; // Sub-negotiation version
auth[1] = (byte)_username.Length;
Encoding.ASCII.GetBytes(_username).CopyTo(auth, 2);
auth[2 + _username.Length] = (byte)_password.Length;
Encoding.ASCII.GetBytes(_password).CopyTo(auth, 3 + _username.Length);
await stream.WriteAsync(auth, 0, auth.Length, ct);
await stream.FlushAsync(ct);
byte[] authResp = new byte[2];
await ReadExactAsync(stream, authResp, 0, 2, ct);
if (authResp[1] != 0x00) throw new IOException("帳密認證失敗");
}
}
private async Task SendConnectRequestAsync(NetworkStream stream, string host, int port, CancellationToken ct)
{
// 解析目標位址
IPAddress ip;
byte atyp;
byte[] addrBytes;
if (IPAddress.TryParse(host, out ip) && ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
atyp = 0x01; addrBytes = ip.GetAddressBytes();
}
else if (IPAddress.TryParse(host, out ip) && ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
{
atyp = 0x04; addrBytes = ip.GetAddressBytes();
}
else
{
atyp = 0x03; // Domain name
addrBytes = new byte[1 + Encoding.ASCII.GetByteCount(host)];
addrBytes[0] = (byte)(addrBytes.Length - 1);
Encoding.ASCII.GetBytes(host).CopyTo(addrBytes, 1);
}
// 組裝封包
var buf = new byte[4 + addrBytes.Length + 2];
buf[0] = 0x05; buf[1] = 0x01; buf[2] = 0x00; buf[3] = atyp;
Buffer.BlockCopy(addrBytes, 0, buf, 4, addrBytes.Length);
byte[] portBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)port));
Buffer.BlockCopy(portBytes, 0, buf, 4 + addrBytes.Length, 2);
await stream.WriteAsync(buf, 0, buf.Length, ct);
await stream.FlushAsync(ct);
}
private async Task ReadConnectResponseAsync(NetworkStream stream, CancellationToken ct)
{
// 固定前 4 bytes: VER, REP, RSV, ATYP
byte[] header = new byte[4];
await ReadExactAsync(stream, header, 0, 4, ct);
if (header[0] != 0x05) throw new IOException("回應版本錯誤");
if (header[1] != 0x00) throw new IOException($"代理連線失敗, REP={header[1]:X2}");
// 略過 BND.ADDR + BND.PORT (依 ATYP 長度)
int skip = header[3] switch { 0x01 => 4, 0x04 => 16, 0x03 => 1 + stream.ReadByte(), _ => 0 };
if (skip > 0)
{
byte[] dummy = new byte[skip + 2]; // +2 for port
await ReadExactAsync(stream, dummy, 0, dummy.Length, ct);
}
}
private static async Task ReadExactAsync(Stream stream, byte[] buffer, int offset, int count, CancellationToken ct)
{
int read = 0;
while (read < count)
{
int r = await stream.ReadAsync(buffer, offset + read, count - read, ct);
if (r == 0) throw new EndOfStreamException();
read += r;
}
}
}
// 使用範例
class Program
{
static async Task Main()
{
var client = new Socks5Client("127.0.0.1", 1080, "user", "pass");
using (var stream = await client.ConnectAsync("example.com", 80))
using (var writer = new StreamWriter(stream) { AutoFlush = true })
using (var reader = new StreamReader(stream))
{
await writer.WriteLineAsync("GET / HTTP/1.1");
await writer.WriteLineAsync("Host: example.com");
await writer.WriteLineAsync("Connection: close");
await writer.WriteLineAsync("");
string response = await reader.ReadToEndAsync();
Console.WriteLine(response);
}
}
}
SOCKS5 用戶端核心在於 **精確封包組裝/解析 (Big Endian 埠號、ATYP 判斷)** 與 **流式非同步讀寫 (`ReadExactAsync`)**。掌握協商認證、CONNECT 請求、回應碼處理三階段,即可實作穩健的代理穿透功能。生產環境建議加入 **連線池、重試、逾時、健康檢查** 等韌性機制。