AxiHttp/OnlySocketHttp.cs
2024-09-24 18:16:11 +08:00

922 lines
24 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
namespace OnlySocketHttp
{
public class RequestAsync
{
public OnlySocketHttpResponseInfo resp;
public bool isDone;
public bool bIsSuccess => resp != null ? resp.bTimeOut : false;
}
public class OnlySocketHttpResponseInfo
{
public string host = "";//host主机头
public string url = "";//pathAndQuery
public int port = 80;
public string request = "";
public string encoding = "";
public string header = "";
public string body = "";
public string reuqestBody = "";
public string reuqestHeader = "";
public Dictionary<string, string> headers = new Dictionary<string, string>();
public string response = "";
public string gzip = "";
public int length = 0;
public int code = 0;
public int location = 0;
public int runTime = 0;//获取网页消耗时间,毫秒
public int sleepTime = 0;//休息时间
public string cookies = "";
public bool bTimeOut = false;
}
public class OnlySocketHttp
{
public const char T = '\n';
public const string CT = "\r\n";
public const string CTRL = "\r\n\r\n";
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 long index = 0;
public static RequestAsync HttpRequestAsync(string url)
{
RequestAsync requestAsync = new RequestAsync();
string strURI = url;
string strHost = "";
string strIP = "";
string strPort = "";
string strRelativePath = "";
bool bSSL = false;
bool foward_302 = true;
if (!ParseURI(strURI, ref bSSL, ref strHost, ref strIP, ref strPort, ref strRelativePath))
{
requestAsync.isDone = true;
requestAsync.resp = null;
//URL 不合法 也可以处理成404
return requestAsync;
}
int port;
int.TryParse(strPort, out port);
UnityEngine.Debug.Log($"strURI->{strURI}");
UnityEngine.Debug.Log($"strHost->{strHost}");
UnityEngine.Debug.Log($"strPort->{port}");
UnityEngine.Debug.Log($"strRelativePath->{strRelativePath}");
Thread thread = new Thread(
() =>
{
// 构造HTTP请求报文
string requestContent = $"GET {strRelativePath} HTTP/1.1\r\n" +
$"Accept-Language: en-us\r\n" +
$"Accept-Encoding: gzip,deflate\r\n" +
$"Host: {strHost}\r\n" +
"Connection: close\r\n" +
"\r\n";
try
{
OnlySocketHttpResponseInfo resp = OnlySocketHttp.sendRequestRetry(bSSL, 1, strHost, port, "",
requestContent,
5,
"UTF-8",
foward_302);
requestAsync.resp = resp;
requestAsync.isDone = true;
}
catch
{
requestAsync.resp = null;
requestAsync.isDone = true;
}
});
thread.Start();
return requestAsync;
}
public static bool ParseURI(string strURI, ref bool bIsSSL, ref string strHost, ref string strIP, ref string strPort, ref string strRelativePath)
{
string strAddressRet;
string strPortRet;
string strRelativePathRet;
string strIPRet;
string strProtocol = strURI.Substring(0, 7);
if (strProtocol != "http://")
return false;
bIsSSL = strURI.ToLower().StartsWith("https://");
string strLeft = strURI.Substring(7, strURI.Length - 7);
int nIndexPort = strLeft.IndexOf(':');
if (nIndexPort == -1)
{
strPortRet = "80";
int nIndexRelative = strLeft.IndexOf('/');
if (nIndexRelative != -1)
{
strAddressRet = strLeft.Substring(0, nIndexRelative);
strRelativePathRet = strLeft.Substring(nIndexRelative, strLeft.Length - nIndexRelative);
}
else
return false;
}
else
{
strAddressRet = strLeft.Substring(0, nIndexPort);
int nIndexRelative = strLeft.IndexOf('/');
if (nIndexRelative != -1)
{
strPortRet = strLeft.Substring(nIndexPort + 1, nIndexRelative - (nIndexPort + 1));
strRelativePathRet = strLeft.Substring(nIndexRelative, strLeft.Length - nIndexRelative);
}
else
return false;
}
strHost = strAddressRet;
try
{
IPHostEntry hostinfo = Dns.GetHostEntry(strAddressRet);
IPAddress[] aryIP = hostinfo.AddressList;
strIPRet = aryIP[0].ToString();
}
catch
{
return false;
}
strIP = strIPRet;
strPort = strPortRet;
strRelativePath = UrlEncode(strRelativePathRet);
return true;
}
public static string UrlEncode(string str)
{
string sb = "";
List<char> filter = new List<char>() { '!', '#', '$', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '=', '?', '@', '_', '~' };
byte[] byStr = System.Text.Encoding.UTF8.GetBytes(str); //System.Text.Encoding.Default.GetBytes(str)
for (int i = 0; i < byStr.Length; i++)
{
if (filter.Contains((char)byStr[i]))
sb += (char)byStr[i];
else if ((char)byStr[i] >= 'a' && (char)byStr[i] <= 'z')
sb += (char)byStr[i];
else if ((char)byStr[i] >= 'A' && (char)byStr[i] <= 'Z')
sb += (char)byStr[i];
else if ((char)byStr[i] >= '0' && (char)byStr[i] <= '9')
sb += (char)byStr[i];
else
sb += (@"%" + Convert.ToString(byStr[i], 16));
}
return sb;
}
/**
*
发生异常尝试重连
*
*/
public static OnlySocketHttpResponseInfo 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);
OnlySocketHttpResponseInfo server = new OnlySocketHttpResponseInfo();
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)
{
UnityEngine.Debug.Log("发包发生异常,正在重试----" + e.Message);
server.bTimeOut = true;
continue;
}
finally
{
count++;
}
}
return server;
}
private static void checkContentLength(ref OnlySocketHttpResponseInfo 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, "\r\n" + newContentLength);
}
}
else
{
request = Regex.Replace(request, Content_Length_Str + "\\d+", Content_Length_Str_M + "0");
request += CTRL;
}
}
private static void doHeader(ref OnlySocketHttpResponseInfo 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 OnlySocketHttpResponseInfo sendHTTPRequest(int count, String host, int port, String payload, String request, int timeout, String encoding, Boolean foward_302)
{
String index = Thread.CurrentThread.Name + OnlySocketHttp.index;
Stopwatch sw = new Stopwatch();
sw.Start();
OnlySocketHttpResponseInfo server = new OnlySocketHttpResponseInfo();
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\r\n\r\n是结束标志单个chunked块\r\n结束
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 OnlySocketHttpResponseInfo sendHTTPSRequest(int count, String host, int port, String payload, String request, int timeout, String encoding, Boolean foward_302)
{
String index = Thread.CurrentThread.Name + OnlySocketHttp.index;
Stopwatch sw = new Stopwatch();
sw.Start();
OnlySocketHttpResponseInfo server = new OnlySocketHttpResponseInfo();
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\r\n\r\n是结束标志单个chunked块\r\n结束
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)
{
UnityEngine.Debug.Log("解压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, @"charset\b\s*=\s*""?(?<charset>[^""]*)", RegexOptions.IgnoreCase);
if (m.Success)
{
return m.Groups["charset"].Value.ToUpper();
}
else
{
if (String.IsNullOrEmpty(body))
{
return "";
}
m = Regex.Match(body, @"charset\b\s*=\s*""?(?<charset>[^""]*)", RegexOptions.IgnoreCase);
if (m.Success)
{
return m.Groups["charset"].Value.ToUpper();
}
}
return "";
}
}
class TimeOutSocket
{
private bool IsConnectionSuccessful = false;
private Exception socketexception = null;
private ManualResetEvent TimeoutObject = new ManualResetEvent(false);
public int useTime = 0;
public TcpClient Connect(String host, int port, int timeoutMSec)
{
Stopwatch sw = new Stopwatch();
sw.Start();
TimeoutObject.Reset();
socketexception = null;
TcpClient tcpclient = new TcpClient();
tcpclient.BeginConnect(host, port, new AsyncCallback(CallBackMethod), tcpclient);
if (TimeoutObject.WaitOne(timeoutMSec, false))
{
if (IsConnectionSuccessful)
{
sw.Stop();
useTime = (int)sw.ElapsedMilliseconds;
return tcpclient;
}
else
{
throw socketexception;
}
}
else
{
tcpclient.Close();
throw new TimeoutException("TimeOut Exception");
}
}
private void CallBackMethod(IAsyncResult asyncresult)
{
try
{
IsConnectionSuccessful = false;
TcpClient tcpclient = asyncresult.AsyncState as TcpClient;
if (tcpclient.Client != null)
{
tcpclient.EndConnect(asyncresult);
IsConnectionSuccessful = true;
}
}
catch (Exception ex)
{
IsConnectionSuccessful = false;
socketexception = ex;
}
finally
{
TimeoutObject.Set();
}
}
}
class Tools
{
public static long currentMillis()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
}
/// <summary>
/// 将16进制转换成10进制
/// </summary>
/// <param name="str">16进制字符串</param>
/// <returns></returns>
public static int convertToIntBy16(String str)
{
try
{
return Convert.ToInt32(str, 16);
}
catch (Exception e)
{
}
return 0;
}
public static String getCurrentPath(String url)
{
int index = url.LastIndexOf("/");
if (index != -1)
{
return url.Substring(0, index) + "/";
}
else
{
return "";
}
}
/// <summary>
/// 将字符串转换成数字错误返回0
/// </summary>
/// <param name="strs">字符串</param>
/// <returns></returns>
public static int convertToInt(String str)
{
try
{
return int.Parse(str);
}
catch (Exception e)
{
UnityEngine.Debug.Log("info:-" + e.Message);
}
return 0;
}
}
}