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 条回复
LokiSharp
2018-09-11 11:12:00 +08:00
C# 挺好,但是 .net 是我见过最垃圾的环境
xiangyuecn
2018-09-11 11:13:04 +08:00
@wizardforcel 98%情况下是有用的,我需要那 2%,嘿嘿
stnaw
2018-09-11 11:26:52 +08:00
C#,你已经是一门成熟的语言了。
该学会自己码代码了
passerbytiny
2018-09-11 11:27:47 +08:00
@CRVV
curl 我测试了,自己看结果吧。复现很简单,把 DNS 服务器改成无效服务器,然后执行 curl 命令。

2018 年 09 月 11 日 星期二 11:23:18 CST
[root@lxl:~]# ping www.sogou.com
ping: www.sogou.com: 未知的名称或服务
[root@lxl:~]# date
2018 年 09 月 11 日 星期二 11:23:41 CST

[root@lxl:~]# date
2018 年 09 月 11 日 星期二 11:24:06 CST
[root@lxl:~]# curl --connect-timeout 1 http://www.sogou.com
curl: (28) Resolving timed out after 1510 milliseconds
[root@lxl:~]# date
2018 年 09 月 11 日 星期二 11:24:29 CST

[root@lxl:~]# date
2018 年 09 月 11 日 星期二 11:25:07 CST
[root@lxl:~]# curl --max-time 1 http://www.sogou.com
curl: (28) Resolving timed out after 1510 milliseconds
[root@lxl:~]# date
2018 年 09 月 11 日 星期二 11:25:30 CST
CRVV
2018-09-11 11:36:58 +08:00
@passerbytiny

man curl
-m, --max-time <time>
Maximum time in seconds that you allow the whole operation to take. This is useful for preventing your batch jobs
from hanging for hours due to slow networks or links going down. Since 7.32.0, this option accepts decimal val-
ues, but the actual timeout will decrease in accuracy as the specified timeout increases in decimal precision.

请用一个稍微新一点的 curl


看了一下 requests 的文档,这个确实不是整个操作的超时
hibobby
2018-09-11 11:42:16 +08:00
这喷的毫无道理了吧,你感觉.net 提供的类库不好用自己拿 C#实现一个啊。
http 协议的底层也是 tcp/IP 自己拿 socket 实现一个 http 协议的 client 不就行了 来喷干啥
xiangyuecn
2018-09-11 12:00:04 +08:00
#66 还有 #29 我站着不动,你们去拿刀。

要用 socket 实现 httpclient,不否认可以实现,里面的工作量有多大,写出来比框架自带的是好还是坏,我就不知道了。老是诱导我们这群小白去磨一些很基础但很重要,深入发现里面异常庞大的东西。
xiangyuecn
2018-09-11 12:03:21 +08:00
@oyjc @hibobby 想要的功能实现不了,新手吐槽一下而已,别这么认真
CRVV
2018-09-11 12:07:41 +08:00
@passerbytiny

这个我也可以复现,看起来像一个 curl 的 bug,他的网站还专门写了 “ When the set time has elapsed, curl will exit no matter what is going on at that moment ”
V2EX 说“请不要在每一个回复中都包括外链,这看起来像是在 spamming ”,所以没有加上链接


可以给你另找一个

```
package main

import (
"context"
"log"
"net/http"
"time"
)

func main() {
req, err := http.NewRequest("GET", "url", nil)
if err != nil {
log.Fatal(err)
}
ctx, _ := context.WithTimeout(context.Background(), time.Second)
req = req.WithContext(ctx)
_, err = http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
}
```
把 DNS 设成不能用的服务器之后,这段 Go 的输出是
$ time ./timeout
2018/09/11 03:56:53 Get url: context deadline exceeded
./timeout 0.00s user 0.02s system 2% cpu 1.020 total
karllynn
2018-09-11 14:16:26 +08:00
有发帖的时间都解决问题了。。
rwecho
2018-09-11 16:57:00 +08:00
Task 是 C#灵魂了, 代码比 Begin 更易懂
mritd
2018-09-11 19:26:50 +08:00
哈哈,有点意思,没用过 C#,不过如果真如楼主所说这个 API 倒是真的反人类,写了好几年 java,最近在写 go,楼主可以看看 go 的标准库 http 请求发送,保准你开森
mmdsun
2018-09-11 19:37:04 +08:00
检测 url 可否访问方式很多。。。你开个定时器就好 2 秒没反应直接跳出。或者用 ping 命令。java 一般用第三方的库
snw
2018-09-11 20:42:03 +08:00
被 Windows (尤其 Win10 )花式卡住甚至挂死的那么多年了,就知道不该对微软家产品的这方面有任何期待(滑稽)

WebClient 确实最简单易用,其次是 HttpClient,这两个完不成的话那就只能用 HttpWebRequest 写冗长又丑陋的代码了,怨念是必然的。
reus
2018-09-11 21:38:00 +08:00
@passerbytiny “任何语言”?呵呵。看看 go 的 https://godoc.org/net/http#Transport,里面的 ResponseHeaderTimeout 字段就是控制你写完请求之后,到收到第一个响应头的超时时间,完全切合需求。为什么 go 可以实现?难道 go 实现的 HTTP 协议有什么不同?
reus
2018-09-11 21:41:44 +08:00
@passerbytiny 更简单的,http.Client 本身就有一个 Timeout 字段: https://godoc.org/net/http#Client ,而且 GET 请求也能带 body。
paranoiagu
2018-09-11 23:15:48 +08:00
.net 的网络请求容易卡死。
wizardforcel
2018-09-12 00:08:33 +08:00
@xiangyuecn 如果这 2%不是随机出现的,那么就可以自己定制一套东西。比如拿 Socket 或者 TCPClient 封装出来。
xeaglex
2018-09-12 11:15:09 +08:00
我就想问一个问题,httpClient 多线程下内存泄漏的毛病什么时候修好?
xiangyuecn
2018-09-12 20:50:30 +08:00
@rwecho @paranoiagu @xeaglex 从发起到完成一个请求,.net 背后套入太多。


顺带发现一个好玩的:

昨天开 50 个线程(Task 实现的)去请求数据,请求数据中用到了 GetResponseAsync,会发现开始时速度极慢,而且会产生超时请求,2 分钟后请求速度达到最高速率并且稳定,最后发现是线程池的锅。

.net 4.5 中线程池中最小线程数饱和后,会新开新线程。但不是需要多少立即开多少,基本上 1 秒开一个新线程,启动异常缓慢。导致突然增加的 50 个线程只有少量在工作,其他排队。进而导致 GetResponseAsync 没有线程来处理,导致超时。

https://bbs.csdn.net/topics/390454205?page=1。

感觉用.net 来做定时采集任务是要废了,请求不好用,线程池还不能进行突发操作,略略略。。。

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

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

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

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

© 2021 V2EX