也许这似乎是一个奇怪的问题,但我遇到了以下情况:
我尝试向服务发出一个帖子请求,并添加我选择的后期数据,从请求中创建一个Stream,并使用StreamWriter在其上写入正文。
但是,在我实际执行请求之前(使用GetResponse),甚至在我写入流对象之前,我就得到了“无法连接异常”
var stream = request.GetRequestStream();
经过一番调查,我意识到 request.GetRequestStream()实际上是在尝试连接。我的问题是与服务器的网络连接(防火墙问题)。
但我的问题是为什么HttpWebRequest.GetRequestStream()尝试连接 ???
我的简单想法是,在请求创建时,没有与请求的服务器的连接。
我发现了一些相关问题,例如this
但似乎并没有完全回答我的问题。
请问有什么解释吗?
PS:非常感谢任何关于如何避免这种“早期”连接效应的建议。
答案 0 :(得分:1)
.NET I / O API通常在streams上运行,这是允许开发人员读取和写入有序数据序列的API。通过读取和写入通用API,它使通用库可以在流上运行以完成强大的功能:压缩,加密,编码等。(顺便说一句,类似地对待各种I / O的历史由来已久,在UNIX中最为著名everything is a file。)
尽管在许多不同种类的流中读取和写入数据的方式非常相似,但打开流很难使它通用。想想您用来打开文件,进行HTTP请求,执行数据库查询的API截然不同。
因此,.NET的Stream
类没有通用的Open()
方法,因为在不同类型的流之间,使流进入打开状态是非常不同的。相反,流API希望获得已打开的流,其中“打开”表示已准备好将其写入和/或读取。
因此,.NET中有一个典型的I / O模式:
现在考虑一下上面的模式如何与HTTP请求对齐,该步骤包括以下步骤:
(我在上面的步骤中忽略了许多现实世界中的复杂性,例如SSL,保持活动连接,缓存的响应等,但是基本的工作流程足够准确地回答您的问题。)
好吧,现在让您沉迷于.NET团队,他们试图构建HTTP客户端API,记得记住将非通用部分(“获取开放流”)与通用部分分开:读取和/或写入,然后关闭流。
如果您的API只需要处理GET请求,那么您可能会在执行返回响应流的相同API时进行连接。 HttpWebRequest.GetResponse
正是这样做的。
但是,如果要发送POST请求(或PUT或其他类似方法),则必须将数据上传到服务器。与只有几个KB的HTTP标头不同,您在POST中上传的数据可能非常庞大。如果您要上传10GB的文件,则不希望在上传到服务器的过程中将其停放在RAM中。同时,这会损害客户的表现。相反,您需要一种获取Stream
的方法,因此您只需在发送到服务器之前将一小部分数据加载到RAM中即可。请记住,Stream
没有Open()
方法,因此您的API必须提供开放流。
现在,您对第一个问题有一个答案:HttpWebRequest.GetRequestStream
必须建立网络连接,因为如果不建立网络连接,则流将被关闭,您将无法对其进行写入。
第二个问题:如何延迟连接?我假设您的意思是连接应该在第一次写入请求流时发生。一种实现方法是编写一个从Stream
继承的类,该类仅尽可能晚地调用GetRequestStream
,然后将所有方法委派给基础请求流。像这样的起点:
using System.Net;
using System.Threading.Tasks;
using System.Threading;
class DelayConnectRequestStream : Stream
{
private HttpWebRequest _req;
private Stream _stream = null;
public DelayConnectRequestStream (HttpWebRequest req)
{
_req = req;
}
public void Write (byte[] buffer, int offset, int count)
{
if (_stream == null)
{
_stream = req.GetRequestStream();
}
return _stream.Write(buffer, offset, count);
}
public override WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_stream == null)
{
// TODO: figure out if/how to make this async
_stream = req.GetRequestStream();
}
return _stream.WriteAsync(buffer, offset, count, cancellationToken);
}
// repeat the pattern above for all needed methods on Stream
// you may need to decide by trial and error which properties and methods
// must require an open stream. Some properties/methods you can probably just return
// without opening the stream, e.g. CanRead which will always be false so no need to
// create a stream before returning from that getter.
// Also, the code sample above is not thread safe. For
// thread safety, you could use Lazy<T> or roll your own locking.
}
但是,老实说,上述方法似乎过大了。如果我穿上你的鞋子,我会看看为什么我试图推迟流的开放,看看是否还有另一种方法可以解决这个问题。