C#实现HTTP服务器:(4)使用Transfer-Encoding标头向客户端发送响应

完整项目托管地址:https://github.com/hooow-does-it-work/http

前面文章封装了一个响应应答器:C#实现HTTP服务器:(3)封装用于响应请求的应答器
现在我们基于应答器,尝试使用Chunked方式,向浏览器发送响应。

修改前面文章NewClient重载的内容如下。

public class HttpServer : TcpIocpServer
{
    protected override void NewClient(Socket client)
    {
        Stream stream = new NetworkStream(client, true);

        //捕获一个HttpRequest
        HttpRequest request = HttpRequest.Capture(stream);

        //实例化一个Chunked模式的应答器
        HttpResponser responser = new ChunkedResponser();

        //发送个Server标头给客户端,验证下标头是否正确
        responser.Server = "IServer/1.0";

        responser.ContentType = "text/html";
        responser.KeepAlive = false;

        //发送响应内容
        //ChunkedResponser的Write方法会对数据进行Chunked封装
        //可以多次调用Write向客户端发送数据
        responser.Write(stream, "<p>hello world!</p>");
        responser.Write(stream, $"<pre>{request.GetAllRequestHeaders()}</pre>");

        //必须调用End方法,向客户端发送结束包
        responser.End(stream);

        stream.Close();
    }
}

服务器启动成功后,浏览器访问:http://127.0.0.1:4189/浏览器响应
可以看到,浏览器正确解析了我们发送的响应以及Server标头。
使用Chunked方式发送数据,无需关心发送的内容长度,只要在最终发送一个结束包到客户端,客户端即可接收到完整内容。
下面的源码说明了Chunked包如何封装。
下篇文章,我们将实现发送Gzip压缩数据到客户端。

ChunkedResponser类源码
/// <summary>
/// 实现发送Chunked类型数据的应答器
/// </summary>
public class ChunkedResponser : HttpResponser
{
    //回车换行
    private static byte[] _crlf = new byte[] { 13, 10 };

    //结束包内容
    private static byte[] _endingChunk = Encoding.ASCII.GetBytes("0\r\n\r\n");

    public ChunkedResponser() : this(200) { }

    public ChunkedResponser(int statusCode) : base(statusCode) { }

    protected override void WriteHeader(Stream stream)
    {
        //移除长度标头,确保Chunked传输
        Response.RemoveHeader("Content-Length");

        //设置传输方式为Chunked
        this["Transfer-Encoding"] = "Chunked";

        base.WriteHeader(stream);
    }

    /// <summary>
    /// 对数据封包后,向基础流写入Chunk包
    /// </summary>
    /// <param name="stream"></param>
    /// <param name="buffer"></param>
    /// <param name="offset"></param>
    /// <param name="size"></param>
    public override void Write(Stream stream, byte[] buffer, int offset, int size)
    {
        ///组装包头,包含长度数据
        ///数据长度的16进制表示形式+\r\n
        ///举例:
        ///如果数据长度是10,那么包头是:a\r\n
        ///如果数据长度是20,那么包头是:14\r\n
        ///如果数据长度是256,那么包头是:100\r\n
        string length = size.ToString("x") + "\r\n";
        byte[] lengthBuffer = Encoding.ASCII.GetBytes(length);

        ///注意调用的是父类Write
        ///写入长度数据
        base.Write(stream, lengthBuffer, 0, lengthBuffer.Length);

        ///写入包数据
        base.Write(stream, buffer, offset, size);

        ///写入包尾,固定的回车换行
        base.Write(stream, _crlf, 0, 2);
    }

    /// <summary>
    /// 向基础流写入结束包,即长度为0的Chunk包,编码后的值是0\r\n\r\n
    /// 这里直接写固定值,省去封包
    /// 必须向客户端发送结束包,不然客户端会一直等待数据。
    /// </summary>
    /// <param name="stream">基础流</param>
    public override void End(Stream stream)
    {
        base.Write(stream, _endingChunk, 0, 5);
    }

    /// <summary>
    /// 打开一个流,用于写入数据
    /// </summary>
    /// <param name="baseStream"></param>
    /// <returns>ChunkedWriteStream流</returns>
    public override Stream OpenWrite(Stream baseStream)
    {
        return new ChunkedWriteStream(base.OpenWrite(baseStream), true);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anlige

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值