C#中的废物 WebRequest

2018-09-10 17:13:29 +08:00
 xiangyuecn

吐槽+骂街贴:吃屎的.net ,一个基本的 http 请求也搞的这么难用,因为一个功能放弃一门(暂且叫语言吧)。

一直用的 WebRequest,直到今天...

1. 完全没法根据需要设定超时

有 Timeout、ReadWriteTimeout ?没用的!看 MSDN 的解释:

https://msdn.microsoft.com/zh-cn/library/system.net.httpwebrequest.timeout(v=vs.110).aspx 域名系统 (DNS) 查询可能需要最多 15 秒钟才能返回或超时。 如果您的请求包含需要解析的主机名,并设置 Timeout 为一个值小于 15 秒,可能需要 15 秒或更长之前 WebException 引发来指示您的请求超时

至少包含一个隐藏的不可控因素,也就是说你没法简单的完全控制请求超时,Timeout 完全是摆设,TMD 的.net 侮辱了 Timeout 这个单词。

ReadWriteTimeout 算是摆设吧,是数据流 read、write 调用超时,意思相近于发送一个直接后等待下一个字节的时间不能超过这个,并非 100%读取完数据和发送完数据的超时控制。然后他默认的 5 分钟完全是废物设定,没错,废物。( java 的 HttpURLConnection 有个设定好像也是如此吧记得)。

没有 ConnectTimeout 选项。没有 ConnectTimeout 选项。没有 ConnectTimeout 选项。(根源所在)

2. 曲线救国 BeginGetRequestStream

使用异步的发送数据流可以变相实现 ConnectTimeout,拿到请求数据流代表已经连上了服务器。

但 GET、HEAD 明确不支持获取 RequestStream。意思就是说 GET 请求你往请求体里面塞东西,.net 就把你写的渣渣代码给干掉,从源头上拒绝你的操作。看到这个逼玩意怎么一个火字了得。

劳资发个请求你还要管我的 method 是什么!

GET 没法拿到请求数据流,GET 没法拿到请求数据流,GET 没法拿到请求数据流。(已经过了底线)

WebRequest 没法实现

比如:严格控制一个 GET 请求耗时在 3 秒以内,超过时间就超时,用来检测 url 是否可以访问。遇到类似 www.google.com 就,,,呵呵了。POST 用 BeginGetRequestStream 没这个问题

要自己实现整体的请求超时功能,他不支持,开个线程也好,开个计时器也好。

GET 不让发请求体,可能很科学,但遇上真不爽

我只是想拿一下发送请求的数据流而已,并非真要往 stream 里面塞数据。

放弃.net

用第三方库解决此类问题?关键是框架给的功能已经能满足 98%的功能了,卡在了这 2%必须实现的功能上而已。用第三方的是不可能的,这辈子都不可能为了这么小小的功能 load 一个 100M 的第三方库的。

so..用 java 吧,或者 php 也行,至少写一个简单的网络请求没这糟心。遇到忍无可忍的地方就换个语言,不是针对.net ,我是说所有的语言!

(文中 C#语言==.net 框架 并非你异议中的 C#语言!=.net 框架,不服去车上拿刀,拿紧)

10817 次点击
所在节点    程序员
84 条回复
forerunner
2018-09-10 23:08:51 +08:00
然后再次确认一下,你用的是 .net framework 还是 .net core ?版本是什么?不知道我为何这么问的话,出门左转 Java 组欢迎你……
xiangyuecn
2018-09-10 23:51:34 +08:00
@forerunner .net framework 4.5

----
.net core 的实现瞄了一下文档发现没什么区别好像,dns 查询部分都是没纳入 timeout 中
nannanziyu
2018-09-11 00:23:46 +08:00
@xiangyuecn
用 HttpClient 的话,一行解决
await new HttpClient().GetAsync("http://www.baidu.com",new CancellationTokenSource(2000).Token);

要用 WebRequest 的话,也是一行解决
var task = await Task.WhenAny(WebRequest.CreateHttp("http://www.baidu.com").GetResponseAsync(), Task.Delay(2000));

// then
if (task is Task<WebResponse>)
{
var r = await (task as Task<WebResponse>);
}
else
{
// timeout
}

楼主有吐槽的时间,不如多学学基础知识……
longaiwp
2018-09-11 00:36:35 +08:00
快转行吧,.NET 不行了,微软要倒闭了。你去学 PHP 吧,世界上最好的语言
nannanziyu
2018-09-11 00:36:58 +08:00
另外
“ get 里发 body ”
“拿到请求 Stream 才能处理 ConnectTimeout ”
“ task 下来可能额外需要等待 0-50ms ”
我想调侃两句都无从下嘴,这对 http 和 thread 一点概念都没有。。。
20150517
2018-09-11 00:43:26 +08:00
我记得我 15 年前开发.net 就知道这毛病了,我已经不用.net 好多年了,怎么到现在还没修好?
xiaojunjor
2018-09-11 08:47:09 +08:00
keymao
2018-09-11 08:58:38 +08:00
先说下结论,.Net 的 WebRequest... 说实在的 之前做过贴吧的一些辅助工具的开发,后来全是用 HttpClient 自己封装工具,不要指望微软给你做好了。 微软就是这样的风格,习惯了就好了。 能用,够用,谈不上好用,自己封装就完了。也别怪微软了,毕竟 wp 这种本来是战略高度的玩意儿都完全玩崩了。
nannanziyu
2018-09-11 09:20:58 +08:00
@keymao
一行就能搞定的东西也能扯到封装
菜就老老实实承认一句有那么难吗?
guolaopi
2018-09-11 09:35:17 +08:00
@nannanziyu 别说了。。我要转 java。。。(滑稽)
passerbytiny
2018-09-11 09:35:24 +08:00
“严格控制一个 GET 请求耗时在 3 秒以内,超过时间就超时,用来检测 url 是否可以访问”

对于这种不符合惯例的专用需求,任何语言,包括它们的扩展底层的第三方类库,都不会实现。楼主至少需要先学习下 HTTP 协议的连接过程,深入一点的话还要学习 TCP 协议和 OSI 模型。

你第一段转摘的 MSDN 的文档,已经涉及了 HTTP 协议的一部分: 域名系统 (DNS) 查询可能需要最多 15 秒钟才能返回或超时。ReadWriteTimeout 的超时也是完全没问题的,传递大量数据(比如下载文件),你觉得以下这两种情况哪种会提前检测到超时:预估 60 分钟下载完,60 分钟还没下载完,算超时; 5 分钟没有读写数据,算超时。

楼主以为 Java 能实现,我想你只是看了 javadoc,而没有测试。测试完估计你又要骂 Java 废物了。

通常,服务器需要高并发所以必须能精确控制超时,客户端无需高并发所以不需要精确控制超时。楼主现在需要客户端精确控制超时,非惯例需求,就要自己实现。请在开骂前先想想自己是多数还是少数,别一不小心就当了代表。
xuanbg
2018-09-11 09:44:40 +08:00
哎。。。我给 HTTPWebRequest 封装了两层,第一层是通用封装,只是抽象了调用方法,简化了参数。第二层是我项目的专用封装,进一步简化了参数。楼主你想要方便、好用还通用,建议你也做两层封装。
weizhen199
2018-09-11 10:06:32 +08:00
HttpClient 啦,那个早不用了
dhssingle
2018-09-11 10:07:09 +08:00
坐等楼主来喷 Java 和 PHP
arsom
2018-09-11 10:09:08 +08:00
别人喷个 net,非要扯 php。我觉得有些 v 站的人好自卑哈哈
wizardforcel
2018-09-11 10:13:19 +08:00
根据我多年开发辅助的经验,我的实际测试结果是那两个 Timeout 有用。不信你访问一个比较慢的站点,明显能感觉到设了会立即报错。
CRVV
2018-09-11 10:34:31 +08:00
@passerbytiny

curl https://icanhazip.com --max-time 0.2 -v

Python
requests.get('https://icanhazip.com', timeout=0.2)

这俩都可以设不同的时间不同的域名,看到不同的错

别随便用“任何语言”这么强硬的词语
xiangyuecn
2018-09-11 10:36:20 +08:00
@nannanziyu 嗯嗯,还在学,新收到 Task.WhenAny、Task.Delay 新知识。

查询了一下 Task.Delay,https://blog.csdn.net/wushang923/article/details/41015063 本质上是开了一个计时器。

试了一下 WebRequest 的 GetRequestStreamAsync、GetResponseAsync,昨天测试没发现这两个方法,和 BeginGetRequestStream 和 BeginGetResponse 一样是异步方法,功能差不多,前面的两个简洁多了。

现在已经换掉了昨天写的 BeginGetRequestStream、BeginGetResponse,直接用的 BeginGetRequestStream().Wait(timeout),这部分代码少了一半。

隐约发现 HttpWebRequest 好像已经过时了。。

-----------

#45 这几点如果抛开上下文来看,确实很滑稽,我现在看一遍我自己也发笑。

--

ConnectTimeout 是 WebRequest 没有提供的,看文档这个应该是和 GetRequestStream 超时差不多,所以 ConnectTimeout≈Timeout(GetRequestStream 之前设置值),但 Timeout 有点小缺陷( dns 查询部分)。

所以就有了“拿到请求 Stream 才能处理 ConnectTimeout ”

--

GetRequestStream 方法 GET、HEAD method 是禁止调用的,就有了“ get 里发 body ”,并非真要往 stream 里面塞数据。

--

一个 task 对象背后的具体实现由于不明确,认定有 0-50ms 延时不为过,参考 Task.Delay,测试过程中发会多出 30ms 左右。

done.


------------------


其实写这个帖子根源还是我想要一个 ConnectTimeout 设定,因为请求一个 url,如果连接不上服务器应该立即返回错误,而 ConnectTimeout 应该远远小于 GetResponse 的超时。比如:ConnectTimeout=2 秒能满足 99.9%的需求,但 GetResponseTimeout 就需要根据不同业务来定,如访问 V2EX,会 GetResponseTimeout=10 秒,虽然很多情况下 1 秒就能拿到响应,但很多时候 V2 会卡很久才会开始返回响应头(经常刷 V2 就是这个感觉)。

应用场景:用户提交任意 URL,尽快速度测试 URL 是否有效,URL 中 50%可能是被墙地址,比如 google.com ,先决条件:需要让 V2EX 等可能会响应慢的地址尽可能通过测试。

简单有效的解决方案就是使用 ConnectTimeout=2 秒,如果连接不上服务器 2 秒就超时了,不管是被墙还是服务器宕机。GetResponseTimeout=10 秒,让响应慢的地址尽可能通过测试。

回到.net ,POST 可以用 GetRequestStream 来支持 ConnectTimeout。GET 目前学习发现暂时无解,只能等到 GetResponseTimeout 超时。


又洋洋洒洒的写了一篇作文,耗时半小时,划水,划水



----

@passerbytiny 好吧,发现帖子正文和上面楼层自己回复的和想要的功能有点出入,这个里在详细重新描述了一下。缺乏一个功能肯定会带来某些功能的丧失,我这个请求功能是从 java 里写的一个类中移植过来的,java 里面 HttpURLConnection 可控制的参数多些,有 setConnectTimeout。
passerbytiny
2018-09-11 11:00:08 +08:00
@CRVV
centos 7 执行结果
# curl https://icanhazip.com --max-time 0.2 -v
curl: option --max-time: expected a proper numerical parameter
curl: try 'curl --help' or 'curl --manual' for more information

Python 的,请上实际测试结果,你怎么就能确定 timeout 指的是命令超时时间,而不是 GET 请求的超时时间。
keymao
2018-09-11 11:05:58 +08:00
@nannanziyu 嗯? 连我需求都不知道就说一句话能解决? 你说的那是楼主的需求 不是我的 谢谢

我很菜 .Net 很好 这就是你想听的 你听到了 别回复我了 谢谢 再见。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/487915

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX