You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#通过Socket经代理发送HTTP POST请求收到乱码如何解决

问题原因

你收到§♥♥ ☻☻2这类乱码的核心原因是:你通过CONNECT方法连接的是目标站点的443 HTTPS端口,代理返回200代表TCP隧道打通后,后续传输的所有内容都是TLS加密的二进制数据,你直接发送明文HTTP请求、直接用ASCII解码收到的原始字节,拿到的是TLS握手帧的二进制内容,自然是乱码。
除此之外你的代码还有两个会导致异常的逻辑问题:

  • TCP是面向流的协议,单次Receive调用最多读取指定长度的字节,无法保证一次读到完整报文,你固定读256字节的写法,很容易出现读半包、漏读数据的问题
  • 没有区分隧道建立阶段和HTTPS传输阶段的流量处理逻辑,把TLS密文当成明文HTTP解析
修复步骤
  • 代理返回CONNECT 200响应后,不要直接在原始Socket上发送HTTP请求,先基于现有Socket创建SSL流,完成和目标站点的TLS握手
  • 所有HTTP请求、响应的收发都走完成握手的SSL流,不要直接操作原始Socket传输应用层内容
  • 调整接收逻辑,循环读取数据直到识别到HTTP响应头结束标记\r\n\r\n,再根据响应头里的Content-Length或Transfer-Encoding字段读取完整响应体,不要单次读取固定长度就终止
修正后参考代码
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Net.Sockets;

public static void Post(string host, string proxyip, int proxyport, string httpRequest)
{
    try
    {
        string connectRequest = $"CONNECT {host}:443 HTTP/1.1\r\nHost: {host}:443\r\n\r\n";
        Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPEndPoint proxy = new IPEndPoint(IPAddress.Parse(proxyip), proxyport);
        s.ReceiveTimeout = 5000;
        s.SendTimeout = 5000;
        s.Connect(proxy);
        
        // 发送CONNECT隧道建立请求
        Byte[] connectReqBytes = Encoding.ASCII.GetBytes(connectRequest);
        s.Send(connectReqBytes);
        
        // 循环读取CONNECT响应直到拿到完整响应头
        StringBuilder connectRespBuilder = new StringBuilder();
        Byte[] recvBuffer = new Byte[1024];
        while (true)
        {
            int recvLen = s.Receive(recvBuffer);
            connectRespBuilder.Append(Encoding.ASCII.GetString(recvBuffer, 0, recvLen));
            string connectResp = connectRespBuilder.ToString();
            if (connectResp.Contains("\r\n\r\n"))
            {
                if (!connectResp.StartsWith("HTTP/1.1 200"))
                {
                    Console.WriteLine($"代理隧道建立失败: {connectResp}");
                    return;
                }
                break;
            }
        }
        Console.WriteLine("代理隧道建立成功,开始TLS握手");
        
        // 基于现有Socket创建SSL流完成TLS握手
        using SslStream sslStream = new SslStream(new NetworkStream(s, ownsSocket: true), false, (sender, cert, chain, errors) => true);
        sslStream.AuthenticateAsClient(host);
        
        // 通过SSL流发送构造好的HTTP POST请求
        Byte[] reqBytes = Encoding.ASCII.GetBytes(httpRequest);
        sslStream.Write(reqBytes);
        sslStream.Flush();
        
        // 读取HTTP响应
        StringBuilder respBuilder = new StringBuilder();
        recvBuffer = new Byte[1024];
        int readLen;
        while ((readLen = sslStream.Read(recvBuffer, 0, recvBuffer.Length)) > 0)
        {
            respBuilder.Append(Encoding.UTF8.GetString(recvBuffer, 0, readLen));
            // 简化逻辑:读到响应头结束标记先停止,生产环境需根据Content-Length/分块编码规则判断响应是否完整
            if (respBuilder.ToString().Contains("\r\n\r\n"))
            {
                break;
            }
        }
        Console.WriteLine("收到响应:");
        Console.WriteLine(respBuilder.ToString());
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
    }
}
注意事项
  • 示例代码里的证书验证回调直接返回true是测试环境写法,生产环境请去掉该逻辑,正常校验服务端证书避免中间人攻击
  • 示例里的响应读取逻辑做了简化,如果你需要读取完整响应(尤其是带大响应体的接口),需要解析响应头的Content-Length字段,循环读取对应长度的内容;如果是Transfer-Encoding: chunked的分块响应,需要按照分块编码规则解析,不能读到\r\n\r\n就停止
  • 构造POST请求的时候记得带上Content-Length请求头,值和请求体的字节长度一致,否则服务端会一直等待请求体导致超时

内容的提问来源于stack exchange,提问作者Hikmet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 16:06:25