Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vb.net/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 在KeepAlive模式下理解HttpWebRequest_.net_Vb.net_Httpwebrequest - Fatal编程技术网

.net 在KeepAlive模式下理解HttpWebRequest

.net 在KeepAlive模式下理解HttpWebRequest,.net,vb.net,httpwebrequest,.net,Vb.net,Httpwebrequest,我知道这个话题已经讨论过很多次了,但我需要理解如何以正确的方式编写代码 我在协议版本HTTP1.1中多次使用相同的HttpWebRequest(到相同的url) Method = "POST" KeepAlive = True 但每次我需要发送不同的请求,并得到不同的响应 (注意,下面的代码不正确并引发异常) 当我运行代码时,第一次得到正确的响应,但当我尝试重用HttpWebRequest时,会在以下行引发异常: httpWebReq.ContentLength = byt

我知道这个话题已经讨论过很多次了,但我需要理解如何以正确的方式编写代码

我在协议版本HTTP1.1中多次使用相同的HttpWebRequest(到相同的url)

Method = "POST"
KeepAlive = True
但每次我需要发送不同的请求,并得到不同的响应

(注意,下面的代码不正确并引发异常)

当我运行代码时,第一次得到正确的响应,但当我尝试重用HttpWebRequest时,会在以下行引发异常:

httpWebReq.ContentLength = byteData.Length
例外情况是,在开始写入后无法设置此属性

搜索时,我找到了此主题:

其中解释了要重用HttpWebRequest,必须关闭流和WebResponse,我这样做了,释放了资源

在本主题中也解释了同样的事情:

但在另一个主题中:

一位成员说不可能重用HttpWebRequest。
我混淆了重用和创建新的,我需要理解它指的是什么:连接还是请求

我想当我执行此指令时:

Dim httpWebReq = CType(Net.WebRequest.Create("http://www.contoso.com/"), Net.HttpWebRequest)
Dim newStream As IO.Stream = httpWebReq.GetRequestStream()
我应该创建HttpWebRequest类的一个实例,但我应该建立与此指令的连接:

Dim httpWebReq = CType(Net.WebRequest.Create("http://www.contoso.com/"), Net.HttpWebRequest)
Dim newStream As IO.Stream = httpWebReq.GetRequestStream()

我说的对吗?

我认为这是需要澄清的,因为这句话的措辞可能会被视为误导:

1个WebRequest=>1个WebResponse。一旦WebRequest被初始化,您就不能对其进行任何更改

这在原则上仍然有效,但初始化的术语可能会混淆。更好的定义是:

在发出请求并返回WebResponse之后,您不能更改WebRequest的任何参数,直到WebResponse关闭(释放)之后。

WebResponse
返回结果后,可以显式或隐式关闭它(在
中使用
块),您可以请求另一个,根据需要修改
WebRequest
参数(例如,将方法从POST更改为GET)。
此外,在请求新的WebResponse之前,必须重新初始化WebRequest。如果不这样做,它将返回默认值。

我在下面发布的代码是一个经典上下文(Web登录请求)的示例,当一个
WebRequest
必须在同一过程中多次重新初始化,以接收数量不确定的WebResponse,直到到达目标地址(或登录页)。

这或多或少就是模式:

                  --------------
(GET or POST)     | WebRequest |       (Method is POST)
      |---------> | GET/(POST) | <-----------| <-------------- |
      |           --------------             |                 |
      |                 |                    |                 |
--------------    ---------------    ------------------   --------------
|    New     |    | WebResponse |--> | LogIn Required |-->|   LogIn    |
|  Location  |    ---------------    ------------------   |   Address  |
| (Referer   |          |                                 --------------
|    Set)    |          |
--------------     (Set Cookies)
      |                 |
      |           ---------------
      |           |    LogIn    |
 Redirection <----|     OK      |---NO---|
                  ---------------        |
                        |                |
                       YES               |
                   (Set Cookies)         |
                        |             Request
                  ---------------     Denied
                  |  Response   |        |
                  |    URI      |        |
                  ---------------        |
                        |                |
                       EXIT <------------|
                        |
必须保留
LoginParameters
对象,因为它引用了一个
CookieContainer
,其中包含身份验证后收到的cookie。当初始化新的WebRequest时,这些cookie被传递到服务器,作为请求的凭据已通过身份验证的“证据”。请注意,这些cookie会在一段时间后过期(当发出新的WebRequest时,它们会“刷新”,除非会话有时间限制)。如果是这种情况,登录过程将自动重复。

Imports System.Net
Imports System.Net.Security
Imports System.IO
Imports System.Security
Imports System.Security.Cryptography
Imports System.Security.Cryptography.X509Certificates
Imports System.Text


Public LoginParameters As LoginObject

Public Class LoginObject
    Public Property LogInUrl As String
    Public Property ResponseUrl As String
    Public Property Credentials As Dictionary(Of String, String)
    Public Property StatusCode As HttpStatusCode
    Public Property CookieJar As New CookieContainer()
End Class


Public Async Function HttpLogIn(LogInParameters As LoginObject) As Task(Of LoginObject)

    Dim httpRequest As HttpWebRequest
    Dim StatusCode As HttpStatusCode
    Dim MaxHops As Integer = 20

    ' Windows 7 (.Net 4.5.1+ required):     
    'ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

    ' Windows 10 (.Net 4.5.1+ required):    
    ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault

    'If needed or for testing
    'ServicePointManager.ServerCertificateValidationCallback = AddressOf CertificateValidation

    httpRequest = WebRequest.CreateHttp(LogInParameters.LogInUrl)

    Try
        HTTP_RequestHeadersInit(httpRequest, String.Empty, LogInParameters.CookieJar)
        Using httpResponse As HttpWebResponse = CType(Await httpRequest.GetResponseAsync(), HttpWebResponse)
            StatusCode = httpResponse.StatusCode
        End Using

        If StatusCode = HttpStatusCode.OK OrElse StatusCode = HttpStatusCode.NoContent Then
            'POST Parameters are URLEncoded and the encoded strings converted to a Byte array of UTF8 chars
            Dim EncodedParameters As Byte() = HTTP_EncodePOSTParameters(LogInParameters.Credentials)

            httpRequest = WebRequest.CreateHttp(LogInParameters.LogInUrl)
            httpRequest.Method = WebRequestMethods.Http.Post
            httpRequest.ContentType = "application/x-www-form-urlencoded"
            httpRequest.ContentLength = EncodedParameters.Length
            HTTP_RequestHeadersInit(httpRequest, String.Empty, LogInParameters.CookieJar)

            Using stream As Stream = Await httpRequest.GetRequestStreamAsync()
                stream.Write(EncodedParameters, 0, EncodedParameters.Length)
            End Using


            Dim Hops As Integer = 0
            Dim Referer As String = LogInParameters.LogInUrl
            Dim LastHttpMethod As String = httpRequest.Method

            Do
                'Evaluate Authentication redirect or page moved
                Using httpResponse As HttpWebResponse = CType(Await httpRequest.GetResponseAsync(), HttpWebResponse)
                    StatusCode = httpResponse.StatusCode
                    LogInParameters.ResponseUrl = URIFromResponseLocation(httpResponse).ToString()
                End Using

                If (StatusCode = HttpStatusCode.Moved) OrElse
                   (StatusCode = HttpStatusCode.Found) OrElse
                   (StatusCode = HttpStatusCode.RedirectMethod) OrElse
                   (StatusCode = HttpStatusCode.RedirectKeepVerb) Then

                    httpRequest = WebRequest.CreateHttp(LogInParameters.ResponseUrl)
                    HTTP_RequestHeadersInit(httpRequest, Referer, LogInParameters.CookieJar)
                    If StatusCode = HttpStatusCode.RedirectKeepVerb Then
                        httpRequest.Method = LastHttpMethod
                    Else
                        LastHttpMethod = httpRequest.Method
                    End If
                End If

                If (CType(StatusCode, Integer) > 320) OrElse Hops >= MaxHops Then
                    Exit Do
                End If
                Hops += 1
            Loop While (StatusCode <> HttpStatusCode.OK)

            If StatusCode = HttpStatusCode.OK Then
                LogInParameters.CookieJar = httpRequest.CookieContainer
            End If

        End If

    Catch exW As WebException
        StatusCode = If(exW.Response IsNot Nothing,
                        CType(exW.Response, HttpWebResponse).StatusCode,
                        CType(exW.Status, HttpStatusCode))

    Catch exS As System.Exception
        StatusCode = CType(WebExceptionStatus.RequestCanceled, HttpStatusCode)

    Finally
        ServicePointManager.ServerCertificateValidationCallback = Nothing
    End Try

    LogInParameters.StatusCode = StatusCode
    Return LogInParameters

End Function

Private Sub HTTP_RequestHeadersInit(ByRef httpReq As HttpWebRequest,
                                            Referer As String,
                                            CookiesJar As CookieContainer)

    httpReq.Date = DateTime.Now
    httpReq.CookieContainer = CookiesJar
    httpReq.KeepAlive = True
    httpReq.ConnectionGroupName = Guid.NewGuid().ToString()
    httpReq.AllowAutoRedirect = False
    httpReq.AutomaticDecompression = DecompressionMethods.GZip Or DecompressionMethods.Deflate
    httpReq.ServicePoint.Expect100Continue = False
    httpReq.Referer = Referer
    httpReq.UserAgent = "Mozilla/5.0 (Windows NT 10; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0"
    httpReq.Accept = "ext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
    httpReq.Headers.Add(HttpRequestHeader.AcceptLanguage, "en-US;q=0.9,en;q=0.5")
    httpReq.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate;q=0.8")
    httpReq.Headers.Add(HttpRequestHeader.CacheControl, "no-cache")
End Sub

Private Function HTTP_EncodePOSTParameters(PostParameters As Dictionary(Of String, String)) As Byte()

    Dim Encoder As New System.Text.UTF8Encoding()
    Dim CredentialValues As New StringBuilder()
    Dim _first As Boolean = True

    For Each CurrentKeyPair As KeyValuePair(Of String, String) In PostParameters
        If _first = False Then CredentialValues.Append("&")
        CredentialValues.AppendFormat("{0}={1}", WebUtility.UrlEncode(CurrentKeyPair.Key),
                                                 WebUtility.UrlEncode(CurrentKeyPair.Value))
        _first = False
    Next

    Return Encoder.GetBytes(CredentialValues.ToString())

End Function

Private Function URIFromResponseLocation(Response As HttpWebResponse) As System.Uri
    Dim uri As Uri
    Dim Location As String = Response.Headers("Location")

    Try
        If uri.IsWellFormedUriString(Location, UriKind.Absolute) Then
            uri = New Uri(Location, UriKind.Absolute)
        Else
            Dim HostUri As String = Response.ResponseUri.GetComponents(UriComponents.SchemeAndServer,
                                                                        UriFormat.Unescaped) + Location
            uri = If(uri.IsWellFormedUriString(HostUri, UriKind.Absolute),
                        New Uri(HostUri),
                        New Uri(Response.ResponseUri.GetComponents(UriComponents.Scheme, UriFormat.Unescaped) +
                                Response.ResponseUri.Host + Location))
        End If
    Catch ExceptionOnInvalidUri As Exception
        uri = New Uri(Location, UriKind.Relative)
    End Try

    Return uri

End Function

Private Function CertificateValidation(sender As Object,
                                        CACert As X509Certificate,
                                        CAChain As X509Chain,
                                        PolicyErrors As SslPolicyErrors) As Boolean

    'This method, as it is, accepts a Server certificate in any case
    'It could be eventually adapted to refuse a connection (returning false) 
    'if the certificate is invalid, expired or from a untrusted path
    If (PolicyErrors = SslPolicyErrors.None) Then Return True

    'If a Certificated must be added to the Chain, uncomment the code below,
    'selecting a Certificate in the Local (or other) Storage
    'Dim MyCert As X509Certificate2 = New X509Certificate2("[localstorage]/[ca.cert]")
    'CAChain.ChainPolicy.ExtraStore.Add(MyCert)

    'CAChain.Build(MyCert)
    'For Each CACStatus As X509ChainStatus In CAChain.ChainStatus
    '    If (CACStatus.Status <> X509ChainStatusFlags.NoError) And
    '       (CACStatus.Status <> X509ChainStatusFlags.UntrustedRoot) Then
    '        Return False
    '    End If
    'Next

    Return True

End Function
导入系统.Net
导入System.Net.Security
导入System.IO
导入系统。安全
导入System.Security.Cryptography
导入System.Security.Cryptography.X509证书
导入系统文本
作为LoginObject的公共登录参数
公共类LoginObject
公共属性LogInUrl作为字符串
公共属性ResponseUrl作为字符串
作为字典的公共属性凭据(字符串的,字符串的)
公共属性状态代码作为HttpStatusCode
作为新CookieContainer()的公共属性CookieJar
末级
公共异步函数HttpLogIn(LogInParameters作为LoginObject)作为(LoginObject的)任务
将httpRequest设置为HttpWebRequest
Dim状态代码为HttpStatusCode
将最大跳数设置为整数=20
“Windows 7(.Net 4.5.1+必需):
'ServicePointManager.SecurityProtocol=SecurityProtocolType.Tls12
“Windows 10(.Net 4.5.1+必需):
ServicePointManager.SecurityProtocol=SecurityProtocolType.SystemDefault
“如果需要或用于测试
'ServicePointManager.ServerCertificateValidationCallback=CertificateValidation的地址
httpRequest=WebRequest.CreateHttp(LogInParameters.LogInUrl)
尝试
HTTP_RequestHeaderInstant(httpRequest,String.Empty,LogInParameters.CookieJar)
使用httpResponse作为HttpWebResponse=CType(等待httpRequest.GetResponseAsync(),HttpWebResponse)
StatusCode=httpResponse.StatusCode
终端使用
如果StatusCode=HttpStatusCode.OK或LSE StatusCode=HttpStatusCode.NoContent,则
'POST参数是URL编码的,编码的字符串转换为UTF8字符的字节数组
Dim EncodedParameters As Byte()=HTTP_EncodePOSTParameters(LogInParameters.Credentials)
httpRequest=WebRequest.CreateHttp(LogInParameters.LogInUrl)
httpRequest.Method=WebRequestMethods.Http.Post
httpRequest.ContentType=“应用程序/x-www-form-urlencoded”
httpRequest.ContentLength=EncodedParameters.Length
HTTP_RequestHeaderInstant(httpRequest,String.Empty,LogInParameters.CookieJar)
使用stream作为stream=wait-httpRequest.GetRequestStreamAsync()
stream.Write(EncodedParameters,0,EncodedParameters.Length)
终端使用
将跳数调整为整数=0
Dim Referer As String=LogInParameters.LogInUrl
Dim LastHttpMethod为String=httpRequest.Method
做
'评估身份验证重定向或页面移动
使用httpResponse作为HttpWebResponse=CType(等待httpRequest.GetResponseAsync(),HttpWebResponse)
StatusCode=httpResponse.StatusCode
LogInParameters.ResponseUrl=URIFromResponseLocation(httpResponse.ToString())