我正在为我的网络客户端使用Socket
课程。我不能使用HttpWebRequest
,因为它不支持socks代理。所以我必须自己解析标题并处理分块编码。对我来说最困难的是确定内容的长度,所以我必须逐字节地读取它。首先,我必须使用ReadByte()
来查找最后一个标题(“\ r \ n \ r \ n”组合),然后检查正文是否具有传输编码。如果是的话,我必须阅读大块的大小等:
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请求(虽然不是很大很复杂,我是菜鸟)。 最好的方法是将链接发布到读取消息体的示例并正确处理以下情况:内容具有chunked-encoding,gzip或deflate编码,省略Content-Length头(消息在连接关闭时结束)。类似于HttpWebRequest类的源代码。
UPD: 我的新功能如下所示:
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
)。
答案 0 :(得分:9)
我建议你不要自己实现 - HTTP 1.1协议足够复杂,使其成为几个人月的项目。
问题是,是否有针对.NET的HTTP请求协议解析器?这个问题已在SO上提出,在答案中你会看到几个建议,包括处理HTTP流的源代码。
Converting Raw HTTP Request into HTTPWebRequest Object
编辑:转子代码相当复杂,难以像网页一样阅读/导航。但是,添加SOCKS支持的实现工作要比自己实现整个HTTP协议要低得多。您可以在几天内完成一些您可以依赖的工作,这是基于经过实践检验的实施。
请求和响应从NetworkStream
类中的m_Transport
,Connection
读取/写入。这用于这些方法:
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服务器的Socket,并执行必要的握手以获取外部连接。其余代码可以保持不变。
我在大约30分钟的时候在网页上查看了这个信息。如果将这些文件加载到IDE中,这应该会快得多。阅读这段代码似乎是一种负担 - 毕竟,阅读代码要比编写代码要困难得多,但是你只是对已经建立的工作系统做了很小的改动。
为了确保更改在所有情况下都有效,最好还测试连接何时断开,以确保客户端使用相同的方法重新连接,从而重新建立SOCKS连接并发送SOCKS请求
答案 1 :(得分:2)
如果问题是ReadByte
太慢的瓶颈,我建议你用StreamBuffer
包装你的输入流。如果您声称具有的性能问题是昂贵的,因为小读取,那么这将为您解决问题。
另外,你不需要这个:
string line = Encoding.ASCII.GetString(lineBuffer.ToArray());
HTTP设计要求标头仅由ASCII字符组成。你真的不想 - 或者不需要 - 将它变成实际的.NET字符串(它们是Unicode)。
如果你想找到HTTP标头的EOF,你可以做到这一点以获得良好的性能。
int k = 0;
while (k != 0x0d0a0d0a)
{
var ch = stream.ReadByte();
k = (k << 8) | ch;
}
当字符串\r\n\r\n
受到欢迎时k
将等于0x0d0a0d0a
答案 2 :(得分:1)
在大多数(应该是全部)http请求中,应该有一个名为content-length的标头,它将告诉您请求正文中有多少字节。然后,只需分配适当的字节数并一次读取这些字节即可。
答案 3 :(得分:0)
虽然我倾向于同意mdma尽可能努力避免实现自己的HTTP堆栈,但您可以考虑的一个技巧是从流中等大小的块中读取。如果你执行读操作并给它一个比可用缓冲区大的缓冲区,它应该返回它读取的字节数。这应该可以减少系统调用次数并显着提高性能。但是,您仍然需要像现在一样扫描缓冲区。
答案 4 :(得分:0)
查看另一个客户端的代码是有帮助的(如果不是混淆): http://src.chromium.org/viewvc/chrome/trunk/src/net/http/
我目前正在做这样的事情。我发现提高客户端效率的最好方法是使用提供的异步套接字函数。他们是相当低级别的,摆脱忙碌的等待和自己处理线程。所有这些都在其方法名称中包含Begin
和End
。但首先,我会尝试使用阻塞,这样你就可以获得HTTP的语义。然后你可以提高效率。请记住:过早优化是邪恶的 - 所以让它工作,然后优化所有的东西!
另外:在使用ToArray()
时,您的某些效率可能会受到限制。众所周知,它在计算上有点贵。更好的解决方案可能是将中间结果存储在byte[]
缓冲区中,并使用正确的编码将它们附加到StringBuilder
。
对于gzipped或deflated数据,请读入所有数据(请记住,您第一次询问时可能无法获得所有数据。请跟踪您已读入的数据量,并继续追加相同的缓冲区)。然后,您可以使用GZipStream(..., CompressionMode.Decompress)
解码数据。
我会说这样做并不像有些人所暗示的那么困难,你只需要冒险一点!
答案 5 :(得分:0)
这里关于扩展Socket和/或TCPClient的所有答案似乎都错过了一些非常明显的东西--HttpWebRequest也是类,因此可以扩展。
您不需要编写自己的HTTP /套接字类。您只需要使用自定义连接方法扩展HttpWebRequest。连接后,所有数据都是标准HTTP,并且可以由基类正常处理。
public class SocksHttpWebRequest : HttpWebRequest
public static Create( string url, string proxy_url ) {
... setup socks connection ...
// call base HttpWebRequest class Create() with proxy url
base.Create(proxy_url);
}
SOCKS握手并不是特别复杂,因此如果您对编程套接字有基本的了解,那么实现连接不需要很长时间。之后,HttpWebRequest可以完成HTTP繁重的工作。
答案 6 :(得分:0)
为什么不读取2个换行符然后从字符串中抓取?性能可能会更糟,但仍然应该是合理的:
Dim Headers As String = GetHeadersFromRawRequest(ResponseBinary)
If Headers.IndexOf("Content-Encoding: gzip") > 0 Then
Dim GzSream As New GZipStream(New MemoryStream(ResponseBinary, Headers.Length + (vbNewLine & vbNewLine).Length, ReadByteSize - Headers.Length), CompressionMode.Decompress)
ClearTextHtml = New StreamReader(GzSream).ReadToEnd()
End If
Private Function GetHeadersFromRawRequest(ByVal request() As Byte) As String
Dim Req As String = Text.Encoding.ASCII.GetString(request)
Dim ContentPos As Integer = Req.IndexOf(vbNewLine & vbNewLine)
If ContentPos = -1 Then Return String.Empty
Return Req.Substring(0, ContentPos)
End Function
答案 7 :(得分:-1)
您可能希望查看TcpClient
中的System.Net
类,它是Socket的包装器,可简化基本操作。
从那里你将不得不阅读HTTP协议。也准备做一些拉链操作。 Http 1.1支持GZip的内容和部分块。你将不得不学习相当多的东西来手工解析它们。
基本的Http 1.0很简单,协议在网上有很好的记录,我们友好的社区谷歌可以帮助你解决这个问题。
答案 8 :(得分:-1)
我会创建一个SOCKS代理,它可以隧道传输HTTP,然后让它接受来自HttpWebRequest的请求并转发它们。我认为这比重新创建HttpWebRequest所做的一切要容易得多。你可以从Privoxy开始,或者只是自己动手。该协议很简单,并在此处记录:
http://en.wikipedia.org/wiki/SOCKS
在他们链接到的RFC上。
您提到您必须拥有许多不同的代理 - 您可以为每个代理设置一个本地端口。