C# ASP.NET在切换到IIS8后断开连接时处理请求

C# ASP.NET在切换到IIS8后断开连接时处理请求,c#,asp.net,.net,iis,webforms,C#,Asp.net,.net,Iis,Webforms,我有一个使用WebForms的ASP.NET 3.5应用程序,目前正在IIS6上托管。一切都很好 但是,在切换到安装了IIS8的Windows 2012服务器后,我们会间歇性地收到截断的请求。大多数情况下,这会在事件日志中的viewstate异常中显示,但是,在没有viewstate的表单上,我们会收到不完整的帖子(最后几个字段丢失/部分截断) 这个问题变得如此严重,以至于我们升级到Microsoft支持部门,经过数周的调试,他们说这是II7及以上版本的“正确”行为。他们的解释是IIS管道从6变

我有一个使用WebForms的ASP.NET 3.5应用程序,目前正在IIS6上托管。一切都很好

但是,在切换到安装了IIS8的Windows 2012服务器后,我们会间歇性地收到截断的请求。大多数情况下,这会在事件日志中的viewstate异常中显示,但是,在没有viewstate的表单上,我们会收到不完整的帖子(最后几个字段丢失/部分截断)

这个问题变得如此严重,以至于我们升级到Microsoft支持部门,经过数周的调试,他们说这是II7及以上版本的“正确”行为。他们的解释是IIS管道从6变为7

IIS6及以下版本将在将整个请求传递给Asp.net之前对其进行缓冲,截断的请求将被忽略。
IIS7及以上版本将在发送初始头之后将请求发送到Asp.net,由应用程序处理截断的请求

当存在连接问题(用户在传输过程中拔下电缆)或用户在post过程中按下停止/重新加载页面时,这会成为问题

在我们的HTTP日志中,我们看到与被截断的请求相关的“connection_dropped”消息

我很难相信这种行为是有意的,但我们已经在几个不同的服务器上进行了测试,并使用IIS7及以上版本(Windows 2008、2008 R2和2012)获得了相同的结果

我的问题是:

1) 这种行为有意义吗

2) 如果这是“正确”的行为,您如何保护您的应用程序不受可能处理不完整数据的影响

3) 为什么应用程序开发人员有责任检测不完整的请求?假设,为什么应用程序开发人员会处理不完整的请求而不是忽略它

更新 我编写了一个小型asp.net应用程序和网站来演示这个问题

服务器 Handler.ashx.cs 客户 Program.cs 1) 如果您正在以集成模式为3.5应用程序分配到的应用程序池运行管道,您可能会遇到处理请求的问题。您可能正在生成它无法正确理解的请求,然后它会将它们截断为默认值。您是否尝试过在经典模式下运行应用程序池

2) 功能测试。大量的功能测试。创建一个测试线束,并进行应用程序可以进行的所有调用,以确保其正常工作。这不是100%的解决方案,但实际上什么都不是。有许多计算机科学论文解释了为什么不可能测试应用程序可能运行的每一种情况


3) 因为代码是你写的。您不应该有不完整的请求,因为该请求可能是针对一个重要数据段的,您需要发回一个错误,说明处理请求时出现问题,否则发出请求的一方只会认为该请求神秘地消失了。

IIS之所以改变其行为,是因为我们(开发人员)需要对请求处理进行更多控制。在请求被破坏的情况下,我们无法调查不可见请求的原因。我们需要在应用程序级别记录请求,以便进行调查和记录。例如,如果请求涉及像信用卡交易这样的金融交易,我们需要更多的控制,我们需要记录每一步以符合要求

IIS是一个web服务器框架,应用程序级数据验证不是他们的责任。如果请求被破坏,这意味着输入不完整&应用程序级逻辑将决定该做什么。应用程序必须响应正确的错误代码和错误。这就是ASP.NET mvc具有模型验证的原因,它允许您在应用程序级别验证完整的输入

您可以使用iClientConnection检查基础套接字是否仍处于连接状态


随着web变得越来越AJAX,越来越移动,我们有时使用ping来检查远程服务的运行状况,我们不一定认为中断的请求是错误的,必须删除。我们可能仍然希望接受破碎的请求。这是应用程序级开发人员可以做出的选择,而不是IIS。

当用户在其浏览器上按stop时,可能会出现问题。无论我处于集成模式还是经典模式,这都无关紧要。至于你的第二点,这似乎是框架应该保护你的东西,asp.net应该是HTTP之上的抽象。虽然这绝对是一个漏洞百出的抽象概念,但我觉得这是一个我不应该反对的问题。针对这个问题写作是非常困难的。我将用一个测试程序更新原始问题,以演示该问题。测试程序可能是最好的。同时,是的,我可以理解你对不得不预测像这样的低水平错误的沮丧,但正如你所说的,这是一个泄漏的抽象,泄漏的抽象只能保护你免受这么多。FWIW,我从未在4.0或4.5中看到过这种情况。我只是想澄清一下,当用户单击浏览器上的“停止”按钮时,或者当存在连接问题时,是否会出现这种问题,或者除了被截断的请求之外,您是否还有其他一切进展顺利的报告?在移动到IIS8服务器后,应用程序在没有修改的情况下工作,在IIS6中从未有过这种复制品。应用程序可以处理此异常。通常,当用户提交的页面具有较大的viewstate或大量post数据,并且他们在浏览器上按STOP或在页面完成发布之前单击其他链接时,会发生此错误。如果用户等待post完成,则不会产生错误。这只是一个普通的WebForms表单post。post数据大约是10万
public class Handler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        if (context.Request.HttpMethod == "POST")
        {
            var lengthString = context.Request.Form["Length"];
            var data = context.Request.Form["Data"];

            if (lengthString == null)
            {
                throw new Exception("Missing field: Length");
            }

            if (data == null)
            {
                throw new Exception("Missing field: Data");
            }

            var expectedLength = int.Parse(lengthString);

            if (data.Length != expectedLength)
            {
                throw new Exception(string.Format("Length expected: {0}, actual: {1}, difference: {2}", expectedLength, data.Length, expectedLength - data.Length));
            }
        }

        context.Response.ContentType = "text/plain";
        context.Response.Write("Hello World, Request.HttpMethod=" + context.Request.HttpMethod);
    }

    public bool IsReusable
    {
        get { return false; }
    }
}
static void Main(string[] args)
{
    var uri = new Uri("http://localhost/TestSite/Handler.ashx");
    var data = new string('a', 1024*1024); // 1mb

    var payload = Encoding.UTF8.GetBytes(string.Format("Length={0}&Data={1}", data.length, data));

    // send request truncated by 256 bytes
    // my assumption here is that the Handler.ashx should not try and handle such a request
    Post(uri, payload, 256);
}

private static void Post(Uri uri, byte[] payload, int bytesToTruncate)
{
    var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
    {
        // this allows us to disconnect unexpectedly
        LingerState = new LingerOption(true, 0)
    };

    socket.Connect(uri.Host, uri.Port);

    SendRequest(socket, uri, payload, bytesToTruncate);

    socket.Close();
}

private static void SendRequest(Socket socket, Uri uri, byte[] payload, int bytesToTruncate)
{
    var headers = CreateHeaders(uri, payload.Length);

    SendHeaders(socket, headers);

    SendBody(socket, payload, Math.Max(payload.Length - bytesToTruncate, 0));
}

private static string CreateHeaders(Uri uri, int contentLength)
{
    var headers = new StringBuilder();

    headers.AppendLine(string.Format("POST {0} HTTP/1.1", uri.PathAndQuery));
    headers.AppendLine(string.Format("Host: {0}", uri.Host));
    headers.AppendLine("Content-Type: application/x-www-form-urlencoded");
    headers.AppendLine("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:26.0) Gecko/20100101 Firefox/99.0");
    headers.AppendLine("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    headers.AppendLine("Connection: Close");
    headers.AppendLine(string.Format("Content-Length: {0}", contentLength));

    return headers.ToString();
}

private static void SendHeaders(Socket socket, string headers)
{
    socket.Send(Encoding.ASCII.GetBytes(headers));
    socket.Send(Encoding.ASCII.GetBytes("\n"));
}

private static void SendBody(Socket socket, byte[] payload, int numBytesToSend)
{
    socket.Send(payload, 0, numBytesToSend, SocketFlags.None);
}