Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.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
C# 如何在传输期间请求流关闭时获取HTTP响应_C#_.net_Http_Tomcat_Httpwebrequest - Fatal编程技术网

C# 如何在传输期间请求流关闭时获取HTTP响应

C# 如何在传输期间请求流关闭时获取HTTP响应,c#,.net,http,tomcat,httpwebrequest,C#,.net,Http,Tomcat,Httpwebrequest,TL;DR版本 当写入请求流时发生传输错误时,即使服务器发送响应,我也无法访问响应 完整版本 我有一个.NET应用程序,它使用HttpWebRequest将文件上传到Tomcat服务器。在某些情况下,服务器会过早地关闭请求流(因为它出于某种原因拒绝文件,例如无效的文件名),并发送一个带有自定义头的400响应,以指示错误的原因 问题是,如果上传的文件很大,那么在我完成请求正文的编写之前,请求流就关闭了,并且我得到了一个IOException: 消息:无法将数据写入传输连接:远程主机强制关闭了现有

TL;DR版本

当写入请求流时发生传输错误时,即使服务器发送响应,我也无法访问响应


完整版本

我有一个.NET应用程序,它使用
HttpWebRequest
将文件上传到Tomcat服务器。在某些情况下,服务器会过早地关闭请求流(因为它出于某种原因拒绝文件,例如无效的文件名),并发送一个带有自定义头的400响应,以指示错误的原因

问题是,如果上传的文件很大,那么在我完成请求正文的编写之前,请求流就关闭了,并且我得到了一个
IOException

消息:无法将数据写入传输连接:远程主机强制关闭了现有连接。
InnerException
SocketException
:远程主机强制关闭了现有连接

我可以捕获这个异常,但是当我调用
GetResponse
时,我会得到一个
WebException
,前面的
IOException
作为其内部异常,以及一个null
Response
属性因此,即使服务器发送响应,我也无法获得响应(已通过WireShark检查)

由于我无法得到回应,我不知道实际的问题是什么。从我的应用程序的角度来看,连接似乎中断了,所以我将其视为与网络相关的错误,然后重试上载。。。当然,这又失败了

如何解决此问题并从服务器检索实际响应?有可能吗?对我来说,当前的行为看起来像是
HttpWebRequest
中的一个bug,或者至少是一个严重的设计问题


下面是我用来重现问题的代码:

var request = HttpWebRequest.CreateHttp(uri);
request.Method = "POST";
string filename = "foo\u00A0bar.dat"; // Invalid characters in filename, the server will refuse it
request.Headers["Content-Disposition"] = string.Format("attachment; filename*=utf-8''{0}", Uri.EscapeDataString(filename));
request.AllowWriteStreamBuffering = false;
request.ContentType = "application/octet-stream";
request.ContentLength = 100 * 1024 * 1024;

// Upload the "file" (just random data in this case)
try
{
    using (var stream = request.GetRequestStream())
    {
        byte[] buffer = new byte[1024 * 1024];
        new Random().NextBytes(buffer);
        for (int i = 0; i < 100; i++)
        {
            stream.Write(buffer, 0, buffer.Length);
        }
    }
}
catch(Exception ex)
{
    // here I get an IOException; InnerException is a SocketException
    Console.WriteLine("Error writing to stream: {0}", ex);
}

// Now try to read the response
try
{
    using (var response = (HttpWebResponse)request.GetResponse())
    {
        Console.WriteLine("{0} - {1}", (int)response.StatusCode, response.StatusDescription);
    }
}
catch(Exception ex)
{
    // here I get a WebException; InnerException is the IOException from the previous catch
    Console.WriteLine("Error getting the response: {0}", ex);
    var webEx = ex as WebException;
    if (webEx != null)
    {
        Console.WriteLine(webEx.Status); // SendFailure
        var response = (HttpWebResponse)webEx.Response;
        if (response != null)
        {
            Console.WriteLine("{0} - {1}", (int)response.StatusCode, response.StatusDescription);
        }
        else
        {
            Console.WriteLine("No response");
        }
    }
}
var-request=HttpWebRequest.CreateHttp(uri);
request.Method=“POST”;
字符串filename=“foo\u00A0bar.dat”;//文件名中的字符无效,服务器将拒绝它
request.Headers[“Content Disposition”]=string.Format(“附件;文件名*=utf-8”{0}”,Uri.EscapeDataString(文件名));
request.AllowWriteStreamBuffering=false;
request.ContentType=“应用程序/八位字节流”;
request.ContentLength=100*1024*1024;
//上传“文件”(本例中仅为随机数据)
尝试
{
使用(var stream=request.GetRequestStream())
{
字节[]缓冲区=新字节[1024*1024];
新的Random().NextBytes(缓冲区);
对于(int i=0;i<100;i++)
{
stream.Write(buffer,0,buffer.Length);
}
}
}
捕获(例外情况除外)
{
//这里我得到一个IOException;InnerException是一个SocketException
WriteLine(“写入流时出错:{0}”,ex);
}
//现在试着读一下答案
尝试
{
使用(var response=(HttpWebResponse)request.GetResponse())
{
Console.WriteLine(“{0}-{1}”,(int)response.StatusCode,response.StatusDescription);
}
}
捕获(例外情况除外)
{
//这里我得到一个WebException;InnerException是来自上一个捕获的IOException
WriteLine(“获取响应时出错:{0}”,ex);
var webEx=ex作为WebException;
如果(webEx!=null)
{
Console.WriteLine(webEx.Status);//发送失败
var response=(HttpWebResponse)webEx.response;
if(响应!=null)
{
Console.WriteLine(“{0}-{1}”,(int)response.StatusCode,response.StatusDescription);
}
其他的
{
Console.WriteLine(“无响应”);
}
}
}

附加说明:

如果我正确理解了
100 Continue
状态的作用,那么如果服务器要拒绝该文件,就不应该将其发送给我。但是,这个状态似乎是由Tomcat直接控制的,而不是由应用程序控制的。理想情况下,在这种情况下,我希望服务器不向我发送
100 Continue
,但据我负责后端的同事说,没有简单的方法可以做到这一点。所以我现在正在寻找一个客户端解决方案;但是如果您碰巧知道如何解决服务器端的问题,我们也将不胜感激

我遇到这个问题的应用程序的目标是.NET4.0,但我也用4.5复制了它

我不会超时的。在超时之前很久就会引发异常

我尝试了一个异步请求。这不会改变任何事情

我尝试将请求协议版本设置为HTTP1.0,结果相同



其他人已经针对此问题在Connect上提交了一个错误:

您在第二个异常(而不是内部异常)的状态代码和响应中得到了什么

如果引发WebException,请使用异常的响应和状态属性来确定服务器的响应


您没有说明您使用的Tomcat 7的确切版本

与WireShark核实

你在WireShark身上看到了什么

您看到响应的状态行了吗

您是否看到完整的状态行,在其末尾最多包含CR-LF字符

Tomcat是在请求身份验证凭据(401),还是因为其他原因拒绝文件上传(先用100表示确认,然后中途中止)

问题是,如果上传的文件很大,请求流 在我写完请求正文之前关闭,我得到一个IOException:

如果您不希望关闭连接,但希望所有数据通过导线传输并在服务器端吞咽,则在Tomcat 7.0.55及更高版本上,可以在HTTP连接器上配置MaxThanksize属性,例如MaxThanksize=“-1”

如果您想讨论Tomcat端的连接处理,最好在Tomcat用户的邮件列表上询问

在.Net端:

- Upon receiving a request which includes an Expect request-header field with the "100-continue" expectation, an origin server MUST either respond with 100 (Continue) status and continue to read from the input stream, or respond with a final status code. The origin server MUST NOT wait for the request body before sending the 100 (Continue) response. If it responds with a final status code, it MAY close the transport connection or it MAY continue to read and discard the rest of the request. It MUST NOT perform the requested method if it returns a final status code.
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ErrorReportValve;

public class CustomUploadHandlerValve extends ValveBase {

    @Override
    public void invoke(Request request, Response response) throws IOException, ServletException {
         HttpServletRequest httpRequest = (HttpServletRequest) request;
         String fileName = httpRequest.getHeader("Filename");  // get the filename or whatever other parameters required as per your code
         bool validationSuccess = Validate(); // perform filename check or anyother validation here
         if(!validationSuccess)
         {
             response = CreateResponse(); //create your custom 400 response here
             request.SetResponse(response);
             // return the response here
         }
         else
         {
             getNext().invoke(request, response); // to pass to the next valve/ servlet in the chain
         }
    }
    ...
}
 Uri.EscapeDataString(filename) // a problematic .net implementation
 HttpServerUtility.UrlEncode(filename) // the proper way to do it