c# 自带的httpwebrequest效率太低,对于自组http封包不好操作。
在写超级sql注入工具时,研究了很长一段时间如何使用socket来发送http、https请求。
经过一年的修改和测试,可完美、高效发送并解析http/https请求。修改过无数次bug。
在这里把核心代码分享出来,供大家学习或做开发参考。
用这个代码写了一个简单的http发包工具。供大家参考。
工具下载:
httptool.rar
核心类:http.cs
using system;
using system.collections.generic;
using system.text;
using tools;
using system.net;
using system.net.sockets;
using system.io测试数据pression;
using system.io;
using system.net.security;
using system.text.regularexpressions;
using system.threading;
using system.diagnostics;
using system.security.authentication;
using system.security.cryptography.x509certificates;
using httptool;
namespace tools
{
public class http
{
public const char t = '
';
public const string ct = "
";
public const string ctrl = "
";
public const string content_length_str = "content-length: " ;
public const string content_length_str_m = "content-length: " ;
public const string content_length = "content-length" ;
public const string content_encoding = "content-encoding" ;
public const string transfer_encoding = "transfer-encoding" ;
public const string connection = "connection" ;
public static main main = null ;
public static long index = 0;
public void initmain(main m)
{
main = m;
}
/**
*
发生异常尝试重连
*
*/
public static serverinfo sendrequestretry(boolean isssl, int trycount, string host, int port, string payload, string request, int timeout, string encoding, boolean foward_302)
{
int count = 0;
interlocked.increment( ref index);
serverinfo server = new serverinfo();
timeout = timeout * 1000;
while ( true )
{
if (count >= trycount) break ;
try
{
if (!isssl)
{
server = sendhttprequest(count, host, port, payload, request, timeout, encoding, foward_302);
return server;
}
else
{
server = sendhttpsrequest(count, host, port, payload, request, timeout, encoding, foward_302);
return server;
}
}
catch (exception e)
{
tools.syslog( "发包发生异常,正在重试----" + e.message);
server.timeout = true ;
continue ;
}
finally
{
count++;
}
}
return server;
}
private static void checkcontentlength( ref serverinfo server, ref string request)
{
//重新计算并设置content-length
int sindex = request.indexof(ctrl);
server.reuqestheader = request;
if (sindex != -1)
{
server.reuqestheader = request.substring(0, sindex);
server.reuqestbody = request.substring(sindex + 4, request.length - sindex - 4);
int contentlength = encoding.utf8.getbytes(server.reuqestbody).length;
string newcontentlength = content_length_str_m + contentlength;
if (request.indexof(content_length_str_m) != -1)
{
request = regex.replace(request, content_length_str_m + "d+" , newcontentlength);
}
else
{
request = request.insert(sindex, "
" + newcontentlength);
}
}
else
{
request = regex.replace(request, content_length_str + "d+" , content_length_str_m + "0" );
request += ctrl;
}
}
private static void doheader( ref serverinfo server, ref string [] headers)
{
for ( int i = 0; i < headers.length; i++)
{
if (i == 0)
{
server.code = tools.converttoint(headers[i].split( ' ' )[1]);
}
else
{
string [] kv = regex.split(headers[i], ": " );
string key = kv[0].tolower();
if (!server.headers.containskey(key))
{
//自动识别编码
if ( "content-type" .equals(key))
{
string hecnode = gethtmlencoding(kv[1], "" );
if (! string .isnullorempty(hecnode))
{
server.encoding = hecnode;
}
}
if (kv.length > 1)
{
server.headers.add(key, kv[1]);
}
else
{
server.headers.add(key, "" );
}
}
}
}
}
private static serverinfo sendhttprequest( int count, string host, int port, string payload, string request, int timeout, string encoding, boolean foward_302)
{
string index = thread.currentthread.name + http.index;
stopwatch sw = new stopwatch();
sw.start();
serverinfo server = new serverinfo();
tcpclient clientsocket = null ;
int sum = 0;
try
{
if (port > 0 && port <= 65556)
{
//编码处理
server.request = request;
timeoutsocket tos = new timeoutsocket();
clientsocket = tos.connect(host, port, timeout);
if (sw.elapsedmilliseconds >= timeout)
{
return server;
}
clientsocket.sendtimeout = timeout - tos.usetime;
if (clientsocket.connected)
{
checkcontentlength( ref server, ref request);
server.request = request;
byte [] requestbyte = encoding.utf8.getbytes(request);
clientsocket.client.send(requestbyte);
byte [] responsebody = new byte [1024 * 1000];
int len = 0;
//获取header头
string tmp = "" ;
stringbuilder sb = new stringbuilder();
clientsocket.receivetimeout = timeout - ( int )sw.elapsedmilliseconds;
do
{
byte [] responseheader = new byte [1];
len = clientsocket.client.receive(responseheader, 1, socketflags.none);
if (len == 1)
{
char c = ( char )responseheader[0];
sb.append(c);
if (c.equals(t))
{
tmp = string .concat(sb[sb.length - 4], sb[sb.length - 3], sb[sb.length - 2], c);
}
}
} while (!tmp.equals(ctrl) && sw.elapsedmilliseconds < timeout);
server.header = sb.tostring().replace(ctrl, "" );
string [] headers = regex.split(server.header, ct);
if (headers != null && headers.length > 0)
{
//处理header
doheader( ref server, ref headers);
//自动修正编码
if (! string .isnullorempty(server.encoding))
{
encoding = server.encoding;
}
encoding encod = encoding.getencoding(encoding);
//302 301跳转
if ((server.code == 302 || server.code == 301) && foward_302)
{
stringbuilder rsb = new stringbuilder(server.request);
int urlstart = server.request.indexof( " " ) + 1;
int urlend = server.request.indexof( " http" );
if (urlstart != -1 && urlend != -1)
{
string url = server.request.substring(urlstart, urlend - urlstart);
rsb.remove(urlstart, url.length);
string location = server.headers[ "location" ];
if (!server.headers[ "location" ].startswith( "/" ) && !server.headers[ "location" ].startswith( "http" ))
{
location = tools.getcurrentpath(url) + location;
}
rsb.insert(urlstart, location);
return sendhttprequest(count, host, port, payload, rsb.tostring(), timeout, encoding, false );
}
}
//根据请求头解析
if (server.headers.containskey(content_length))
{
int length = int .parse(server.headers[content_length]);
while (sum < length && sw.elapsedmilliseconds < timeout)
{
int readsize = length - sum;
len = clientsocket.client.receive(responsebody, sum, readsize, socketflags.none);
if (len > 0)
{
sum += len;
}
}
}
//解析chunked传输
else if (server.headers.containskey(transfer_encoding))
{
//读取长度
int chunkedsize = 0;
byte [] chunkedbyte = new byte [1];
//读取总长度
sum = 0;
do
{
string ctmp = "" ;
do
{
len = clientsocket.client.receive(chunkedbyte, 1, socketflags.none);
ctmp += encoding.utf8.getstring(chunkedbyte);
} while ((ctmp.indexof(ct) == -1) && (sw.elapsedmilliseconds < timeout));
chunkedsize = tools.converttointby16(ctmp.replace(ct, "" ));
//chunked的结束0
是结束标志,单个chunked块
结束
if (ctmp.equals(ct))
{
continue ;
}
if (chunkedsize == 0)
{
//结束了
break ;
}
int onechunklen = 0;
while (onechunklen < chunkedsize && sw.elapsedmilliseconds < timeout)
{
len = clientsocket.client.receive(responsebody, sum, chunkedsize - onechunklen, socketflags.none);
if (len > 0)
{
onechunklen += len;
sum += len;
}
}
//判断
} while (sw.elapsedmilliseconds < timeout);
}
//connection close方式或未知body长度
else
{
while (sw.elapsedmilliseconds < timeout)
{
if (clientsocket.client.poll(timeout, selectmode.selectread))
{
if (clientsocket.available > 0)
{
len = clientsocket.client.receive(responsebody, sum, (1024 * 200) - sum, socketflags.none);
if (len > 0)
{
sum += len;
}
}
else
{
break ;
}
}
}
}
//判断是否gzip
if (server.headers.containskey(content_encoding))
{
server.body = ungzip(responsebody, sum, encod);
}
else
{
server.body = encod.getstring(responsebody, 0, sum);
}
}
}
}
}
catch (exception e)
{
exception ee = new exception( "http发包错误!错误消息:" + e.message + e.targetsite.name + "----发包编号:" + index);
throw ee;
}
finally
{
sw.stop();
server.length = sum;
server.runtime = ( int )sw.elapsedmilliseconds;
if (clientsocket != null )
{
clientsocket.close();
}
}
return server;
}
private static bool validateservercertificate( object sender, x509certificate certificate, x509chain chain, sslpolicyerrors sslpolicyerrors)
{
return true ;
}
private static serverinfo sendhttpsrequest( int count, string host, int port, string payload, string request, int timeout, string encoding, boolean foward_302)
{
string index = thread.currentthread.name + http.index;
stopwatch sw = new stopwatch();
sw.start();
serverinfo server = new serverinfo();
int sum = 0;
tcpclient clientsocket = null ; ;
try
{
if (port > 0 && port <= 65556)
{
timeoutsocket tos = new timeoutsocket();
clientsocket = tos.connect(host, port, timeout);
if (sw.elapsedmilliseconds >= timeout)
{
return server;
}
clientsocket.sendtimeout = timeout - tos.usetime;
sslstream ssl = null ;
if (clientsocket.connected)
{
ssl = new sslstream(clientsocket.getstream(), false , new remotecertificatevalidationcallback(validateservercertificate));
sslprotocols protocol = sslprotocols.ssl3 | sslprotocols.ssl2 | sslprotocols.tls;
ssl.authenticateasclient(host, null , protocol, false );
if (ssl.isauthenticated)
{
checkcontentlength( ref server, ref request);
server.request = request;
byte [] requestbyte = encoding.utf8.getbytes(request);
ssl.write(requestbyte);
ssl.flush();
}
}
server.request = request;
byte [] responsebody = new byte [1024 * 1000];
int len = 0;
//获取header头
string tmp = "" ;
stringbuilder sb = new stringbuilder();
stringbuilder bulider = new stringbuilder();
clientsocket.receivetimeout = timeout - ( int )sw.elapsedmilliseconds;
do
{
byte [] responseheader = new byte [1];
int read = ssl.readbyte();
char c = ( char )read;
sb.append(c);
if (c.equals(t))
{
tmp = string .concat(sb[sb.length - 4], sb[sb.length - 3], sb[sb.length - 2], c);
}
} while (!tmp.equals(ctrl) && sw.elapsedmilliseconds < timeout);
server.header = sb.tostring().replace(ctrl, "" );
string [] headers = regex.split(server.header, ct);
//处理header
doheader( ref server, ref headers);
//自动修正编码
if (! string .isnullorempty(server.encoding))
{
encoding = server.encoding;
}
encoding encod = encoding.getencoding(encoding);
//302 301跳转
if ((server.code == 302 || server.code == 301) && foward_302)
{
int urlstart = server.request.indexof( " " );
int urlend = server.request.indexof( " http" );
if (urlstart != -1 && urlend != -1)
{
string url = server.request.substring(urlstart + 1, urlend - urlstart - 1);
if (!server.headers[ "location" ].startswith( "/" ) && !server.headers[ "location" ].startswith( "https" ))
{
server.request = server.request.replace(url, tools.getcurrentpath(url) + server.headers[ "location" ]);
}
else
{
server.request = server.request.replace(url, server.headers[ "location" ]);
}
return sendhttpsrequest(count, host, port, payload, server.request, timeout, encoding, false );
}
}
//根据请求头解析
if (server.headers.containskey(content_length))
{
int length = int .parse(server.headers[content_length]);
while (sum < length && sw.elapsedmilliseconds < timeout)
{
len = ssl.read(responsebody, sum, length - sum);
if (len > 0)
{
sum += len;
}
}
}
//解析chunked传输
else if (server.headers.containskey(transfer_encoding))
{
//读取长度
int chunkedsize = 0;
byte [] chunkedbyte = new byte [1];
//读取总长度
sum = 0;
do
{
string ctmp = "" ;
do
{
len = ssl.read(chunkedbyte, 0, 1);
ctmp += encoding.utf8.getstring(chunkedbyte);
} while (ctmp.indexof(ct) == -1 && sw.elapsedmilliseconds < timeout);
chunkedsize = tools.converttointby16(ctmp.replace(ct, "" ));
//chunked的结束0
是结束标志,单个chunked块
结束
if (ctmp.equals(ct))
{
continue ;
}
if (chunkedsize == 0)
{
//结束了
break ;
}
int onechunklen = 0;
while (onechunklen < chunkedsize && sw.elapsedmilliseconds < timeout)
{
len = ssl.read(responsebody, sum, chunkedsize - onechunklen);
if (len > 0)
{
onechunklen += len;
sum += len;
}
}
//判断
} while (sw.elapsedmilliseconds < timeout);
}
//connection close方式或未知body长度
else
{
while (sw.elapsedmilliseconds < timeout)
{
if (clientsocket.client.poll(timeout, selectmode.selectread))
{
if (clientsocket.available > 0)
{
len = ssl.read(responsebody, sum, (1024 * 200) - sum);
if (len > 0)
{
sum += len;
}
}
else
{
break ;
}
}
}
}
//判断是否gzip
if (server.headers.containskey(content_encoding))
{
server.body = ungzip(responsebody, sum, encod);
}
else
{
server.body = encod.getstring(responsebody, 0, sum);
}
}
}
catch (exception e)
{
exception ee = new exception( "https发包错误!错误消息:" + e.message + "----发包编号:" + index);
throw ee;
}
finally
{
sw.stop();
server.length = sum;
server.runtime = ( int )sw.elapsedmilliseconds;
if (clientsocket != null )
{
clientsocket.close();
}
}
return server;
}
public static string ungzip( byte [] data, int len, encoding encoding)
{
string str = "" ;
memorystream ms = new memorystream(data, 0, len);
gzipstream gs = new gzipstream(ms, compressionmode.decompress);
memorystream outbuf = new memorystream();
byte [] block = new byte [1024];
try
{
while ( true )
{
int bytesread = gs.read(block, 0, block.length);
if (bytesread <= 0)
{
break ;
}
else
{
outbuf.write(block, 0, bytesread);
}
}
str = encoding.getstring(outbuf.toarray());
}
catch (exception e)
{
tools.syslog( "解压gzip发生异常----" + e.message);
}
finally
{
outbuf.close();
gs.close();
ms.close();
}
return str;
}
public static string gethtmlencoding( string header, string body)
{
if ( string .isnullorempty(header) && string .isnullorempty(body))
{
return "" ;
}
body = body.toupper();
match m = regex.match(header, @"charsets*=s*""?(?<charset>[^""]*)" , regexoptions.ignorecase);
if (m.success)
{
return m.groups[ "charset" ].value.toupper();
}
else
{
if ( string .isnullorempty(body))
{
return "" ;
}
m = regex.match(body, @"charsets*=s*""?(?<charset>[^""]*)" , regexoptions.ignorecase);
if (m.success)
{
return m.groups[ "charset" ].value.toupper();
}
}
return "" ;
}
}
}
以上就是关于c# socket发送http/https请求的核心代码了,需要的朋友可以参考一下。
dy("nrwz");
查看更多关于c#使用Socket发送HTTP/HTTPS请求的实现代码的详细内容...