内容目录
背景
因为c# httpclient会出现无法访问网络情况,当IE无法访问网络情况,这个因为由于windows 底层实现导致的,只能修复IE才能上网,但这这样子用户体验不好,那么我就找c# 完全自己实现httpclient,找了好多发现都是底层依赖httpwebrequest,这个还是依赖IE,最后找到这个touchsocket这个网络库,然后我用它的官方例子却跑一些http请求会失败,我自己抓包分析才发现它写的例子是错误,http host不能那么设置,因为HTTP server会识别,但它带有端口可能就出现错误。
例子
namespace ConsoleApp1
{
class Program
{
static public async Task<HttpResponse> HTTPGet(string raw_url)
{
HttpResponse respose = null;
await Task.Run(() =>
{
Uri url = new Uri(raw_url);
int index = url.AbsoluteUri.LastIndexOf(url.AbsolutePath);
string new_ip_host = url.AbsoluteUri.Substring(0, index);
string user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.42";
TouchSocket.Http.HttpClient client = new TouchSocket.Http.HttpClient();
client.Setup(new TouchSocketConfig()
.SetRemoteIPHost(new IPHost(new_ip_host)))
.Connect();//先做连接
HttpRequest request = new HttpRequest();
request
.InitHeaders()
.SetHeader(HttpHeaders.UserAgent, user_agent)
.SetHost(url.Host)
.SetUrl(url.AbsolutePath)
.AsGet();
try
{
//必须这么写,如用SetHeader会增加一个close,既包含keep-alive又close了,这样子逻辑有点混乱
request.Headers["connection"] = "close"; //因为目前这个写法无法复用keep-alive,那么直接用close就可以了
respose = client.Request(request, timeout: 1000 * 10); //这个等待结果完成
}
catch (Exception)
{
}
});
return respose;
}
public static async Task<HttpResponse> HTTPostJSON(string raw_url, string json_text)
{
HttpResponse respose = null;
await Task.Run(() =>
{
Uri url = new Uri(raw_url);
string new_ip_host = url.AbsoluteUri.Replace(url.AbsolutePath, "");
string user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.42";
TouchSocket.Http.HttpClient client = new TouchSocket.Http.HttpClient();
client.Setup(new TouchSocketConfig()
.SetRemoteIPHost(new IPHost(new_ip_host)))
.Connect();//先做连接
HttpRequest request = new HttpRequest();
request
.InitHeaders()
.SetHeader(HttpHeaders.Connection, "Close") //因为目前这个写法无法复用keep-alive,那么直接用close就可以了
.SetHeader(HttpHeaders.ContentType, "application/json")
.SetHeader(HttpHeaders.UserAgent, user_agent)
.SetHost(url.Host)
.SetUrl(url.AbsolutePath)
.AsPost();
try
{
//必须这么写,如用SetHeader会增加一个close,既包含keep-alive又close了,这样子逻辑有点混乱
request.Headers["connection"] = "close"; //因为目前这个写法无法复用keep-alive,那么直接用close就可以了
//设置body的内容
request.SetContent(json_text);
respose = client.Request(request, timeout: 1000 * 10); //这个等待结果完成
}
catch (Exception)
{
}
});
return respose;
}
static async void TestPost()
{
Dictionary<string, object> object_dic = new Dictionary<string, object>();
object_dic["id"] = 1;
object_dic["msg"] = "hello world";
var str = JsonFastConverter.JsonTo(object_dic);
var r = await HTTPostJSON("http://wwww.baidu.com/you", str);//这里构建一个没有用,只是为了抓包用HTTP的包,看格式是否正确
Console.WriteLine(r.GetBody());
}
static async void TestGet()
{
var r = await HTTPGet("http://u.tools/docs/guide/about-uTools.html#utools-%E6%98%AF%E4%BB%80%E4%B9%88");
if(r != null)
{
Console.WriteLine(r.GetBody());
}
}
static void Main(string[] args)
{
TestGet();
Console.ReadLine();
}
}
}
总结
我只是做了简单封装,用url来解析域名,方便使用,同时我没用keep-alive 因为这种写法没复用连接,那么消耗几分钟断开连接也没必要,我猜测微软的httpclient有复用,因为官方都推荐用单例。官方例子一定要设置证书,目前最新代码是不用设置,默认检测https就会设置证书。
这个网络库代码看了一下有点像java风格,各种接口抽象,各种封装,代码阅读不是那么直观,我现在反而不喜欢过多封装,不过这是每个人风格。