c# - 如何使用 Socket 接收 HTTP 消息

标签 c# .net http sockets

我正在为我的 Web 客户端使用 Socket 类。我无法使用 HttpWebRequest,因为它不支持 socks 代理。所以我必须自己解析 header 并处理分块编码。对我来说最困难的事情是确定内容的长度,所以我必须逐字节阅读。首先,我必须使用 ReadByte() 找到最后一个 header (“\r\n\r\n”组合),然后检查正文是否具有传输编码。如果是这样,我必须读取 block 的大小等:

public void ParseHeaders(Stream stream)
{
    while (true)
    {
        var lineBuffer = new List<byte>();
        while (true)
        {
            int b = stream.ReadByte();
            if (b == -1) return;
            if (b == 10) break;
            if (b != 13) lineBuffer.Add((byte)b);
        }
        string line = Encoding.ASCII.GetString(lineBuffer.ToArray());
        if (line.Length == 0) break;
        int pos = line.IndexOf(": ");
        if (pos == -1) throw  new VkException("Incorrect header format");
        string key = line.Substring(0, pos);
        string value = line.Substring(pos + 2);
        Headers[key] = value;
    }
}

但是这种方法的性能很差。你能建议更好的解决方案吗?也许是一些通过套接字处理 http 请求的开源示例或库(虽然不是很大很复杂,但我是菜鸟)。 最好的办法是发布示例链接,该示例读取消息正文并正确处理以下情况:内容具有分块编码、gzip 或 deflate 编码、Content-Length header 被省略(连接关闭时消息结束)。类似于 HttpWebRequest 类的源代码。

更新: 我的新函数如下所示:

int bytesRead = 0;
byte[] buffer = new byte[0x8000];
do
{
    try
    {
        bytesRead = this.socket.Receive(buffer);
        if (bytesRead <= 0) break;
        else
        {
            this.m_responseData.Write(buffer, 0, bytesRead);
            if (this.m_inHeaders == null) this.GetHeaders();
        }
    }
    catch (Exception exception)
    {
        throw new Exception("Read response failed", exception);
    }
}
while ((this.m_inHeaders == null) || !this.isResponseBodyComplete());

其中 GetHeaders()isResponseBodyComplete() 使用带有已接收数据的 m_responseData (MemoryStream)。

最佳答案

我建议您不要自己实现它 - HTTP 1.1 协议(protocol)非常复杂,需要几个人月才能完成。

问题是,是否有 .NET 的 HTTP 请求协议(protocol)解析器?这个问题已经在 SO 上被问到,在答案中你会看到一些建议,包括处理 HTTP 流的源代码。

Converting Raw HTTP Request into HTTPWebRequest Object

编辑:转子代码相当复杂,难以像网页一样阅读/导航。但是,添加 SOCKS 支持的实现工作仍然比自己实现整个 HTTP 协议(protocol)要低得多。最多几天之内,您就会得到一些您可以依赖的东西,这些东西基于久经考验的实现。

请求和响应从 Connection 类中的 NetworkStreamm_Transport 读取/写入。这在这些方法中使用:

internal int Read(byte[] buffer, int offset, int size) 
//and
private static void ReadCallback(IAsyncResult asyncResult)

都在http://www.123aspx.com/Rotor/RotorSrc.aspx?rot=42903

套接字创建于

private void StartConnectionCallback(object state, bool wasSignalled)

因此您可以修改此方法以创建到您的 socks 服务器的套接字,并进行必要的握手以获得外部连接。其余代码可以保持不变。

我在网上浏览网页大约 30 分钟后就获得了这些信息。如果将这些文件加载​​到 IDE 中,速度应该会快得多。通读这段代码似乎是一种负担 - 毕竟,阅读代码比编写代码要难得多,但您只是对已经建立的工作系统进行微小的更改。

为确保更改在所有情况下都有效,明智的做法是在连接断开时也进行测试,以确保客户端使用相同的方法重新连接,从而重新建立 SOCKS 连接并发送 SOCKS 请求.

关于c# - 如何使用 Socket 接收 HTTP 消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2945724/

相关文章:

java - 在 Android 上从 URL 数组下载图像并将其显示到多个不同的 ImageView

javascript - 使用 $http.post() 从 Angular 发布到 php 的变量不起作用

c# - 搜索并匹配前 6 个字符的 pdf 文件,并在 Windows 默认程序中打开它

.net - 使用GeneratePathProperty将另一个包中的程序集包含到我的NuGet包中

c# - 无法在代码中获取 DropDownList 的文本 - 可以获取值但不能获取文本

.net - 线程安全对象 - 静态还是非静态?

javascript - Angular 2异步http调用导致未定义

c# - 无需安装程序即可发布 Windows 窗体项目

c# - Serilog 不创建日志文件

c# - 调用传递值的基本构造函数