【问题标题】:Chunked compressed response with NancyFX self hosting使用 NancyFX 自托管分块压缩响应
【发布时间】:2014-10-10 13:41:45
【问题描述】:

我有一个系统应该将行写入 HTTP 响应流。该系统中的每一行都代表某种事件,因此您可以将其视为通知流。我在使用 NancyFX 和 Nancy 自托管 (0.23) 的 Windows 7 上使用 .NET4。以下代码是有效的:

using System;
using System.IO;
using System.Threading;
using Nancy;
using Nancy.Hosting.Self;

namespace TestNancy
{
    public class ChunkedResponse : Response
    {
        public ChunkedResponse()
        {
            ContentType = "text/html; charset=utf-8";
            Contents = stream =>
            {
                using (var streamWriter = new StreamWriter(stream))
                {
                    while (true)
                    {
                        streamWriter.WriteLine("Hello");
                        streamWriter.Flush();
                        Thread.Sleep(1000);
                    }
                }
            };
        }
    }

    public class HomeModule : NancyModule
    {
        public HomeModule()
        {
            Get["/"] = args => new ChunkedResponse();
        }
    }

    public class Program
    {
        public static void Main()
        {
            using (var host = new NancyHost(new Uri("http://localhost:1234")))
            {
                host.Start();
                Console.ReadLine();
            }
        }
    }
}

现在我想对流添加压缩以压缩带宽量。出于某种原因,在浏览器中进行测试时,我看不到任何结果。我尝试了很多组合来达到预期的效果,但这就是我目前所拥有的:

using System; using System.IO; using System.IO.Compression; using System.Threading; using Nancy; using Nancy.Hosting.Self;

namespace TestNancy {
    public class ChunkedResponse : Response
    {
        public ChunkedResponse()
        {
            Headers["Content-Encoding"] = "gzip";
            ContentType = "text/html; charset=utf-8";
            Contents = stream =>
            {
                using (var gzip = new GZipStream(stream, CompressionMode.Compress))
                using (var streamWriter = new StreamWriter(gzip))
                {
                    while (true)
                    {
                        streamWriter.WriteLine("Hello");
                        streamWriter.Flush();
                        Thread.Sleep(1000);
                    }
                }
            };
        }
    }

    public class HomeModule : NancyModule
    {
        public HomeModule()
        {
            Get["/"] = args => new ChunkedResponse();
        }
    }

    public class Program
    {
        public static void Main()
        {
            using (var host = new NancyHost(new Uri("http://localhost:1234")))
            {
                host.Start();
                Console.ReadLine();
            }
        }
    } }

我正在寻求帮助,要么告诉我我在 HTTP 协议方面做错了什么(例如,我尝试按照 HTTP1.1 中的描述添加块长度,但没有奏效),或者关于 Nancy 的帮助没有考虑。

【问题讨论】:

  • 使用 NancyFx 自托管,您会使用 HttpListener 对吗?通常,如果您在写入内容之前省略内容长度,则内容将作为分块发送而无需进一步干预。我不知道南希在写作之前做了什么魔法,所以这可能是相关的,也可能是不相关的。

标签: c# .net http hosting nancy


【解决方案1】:

问题似乎出在框架的 Gzip 实现中,因为它在关闭之前从不写入输出流,

我只是使用了 SharpZiplib,你的代码似乎对我有用,这是我的修改

public class ChunkedResponse : Response
{
    public ChunkedResponse()
    {
        Headers["Transfer-Encoding"] = "chunked";
        Headers["Content-Encoding"] = "gzip";
        ContentType = "text/html; charset=utf-8";
        Contents = stream =>
        {
            var gzip = new ICSharpCode.SharpZipLib.GZip.GZipOutputStream(stream);
            using (var streamWriter = new StreamWriter(gzip))
            {
                while (true)
                {
                    streamWriter.WriteLine("Hello");
                    gzip.Flush();
                    streamWriter.Flush();
                    Thread.Sleep(1000);
                }
            }

        };
    }
}


public class HomeModule : NancyModule
{
    public HomeModule()
    {
        Get["/"] = args => new ChunkedResponse();
    }
}

public class Program
{
    public static void Main()
    {
        using (var host = new NancyHost(new HostConfiguration{AllowChunkedEncoding = true},new Uri("http://localhost:1234")))
        {

            host.Start();
            Console.ReadLine();
        }
    }
}

SharpZipLib 的 Nuget 包:PM> Install-Package SharpZipLib

【讨论】:

    【解决方案2】:

    看起来,由于while(true),您提供为ChunkedReponse.Contents 的任何调用代理都将永远不会返回。这是预期的行为吗?不知道这个框架对那个委托做了什么,我猜不出来。

    乍一看,我确实想知道构造函数是否永远不会返回——我想这肯定会导致问题——但没过多久我就注意到它是一个 lambda。幸运。

    编辑#1:

    GZipStream.Flush() 的文档说:

    此方法的当前实现没有功能。 (覆盖 Stream.Flush()。)

    暗示 GZipStream 在关闭之前不会向传输中写入任何内容。如果您不永远运行提到的委托,而是在某个时候关闭流,您是否会遇到不同的行为?

    【讨论】:

    • 意在永不停止。 Nancy 调用委托来写入流 - 我这样做是分块的,因此您每秒将获得大约一行。请注意,第一个示例按预期执行此操作。
    【解决方案3】:

    我自己测试过,我认为问题在于浏览器如何处理这些分块和压缩的响应。这是我尝试过的,基本上是有效的:

    Contents = stream =>
            {
                using (var gzip = new GZipStream(stream, CompressionMode.Compress))
                using (var streamWriter = new StreamWriter(gzip))
                {
                    for (int i = 0; i < 5;i++ )
                    {
                        string txt = "Hello";
                        streamWriter.WriteLine(txt);
                        streamWriter.Flush();
                        Thread.Sleep(1000);
                    }
                }
            };
    

    问题是浏览器在显示结果之前等待分块响应准备好。尽管gzip supports streaming,它可能会等待解压缩,直到所有数据都发送完毕。 Here 是支持我假设的第一个提示。

    【讨论】:

      猜你喜欢
      • 2011-07-13
      • 2015-04-29
      • 2012-12-02
      • 2015-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-13
      • 1970-01-01
      相关资源
      最近更新 更多