C# - 三种流式响应机制详解

C# - 三种流式响应机制详解

在现代Web应用中,实时数据传输和高效的数据流处理变得越来越重要。ASP.NET Core提供了多种流式响应机制,以满足不同场景下的需求。

1. Server-Sent Events (SSE)

适用场景

  • 实时更新的应用,如聊天应用、实时监控、新闻推送等。
  • 单向通信(服务器到客户端)。

服务器端实现

using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace StreamedResponseExample.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class StreamController : ControllerBase
    {
        [HttpGet("sse")]
        public async Task StreamSse()
        {
            Response.ContentType = "text/event-stream";
            Response.Headers.Add("Cache-Control", "no-cache");
            Response.Headers.Add("Connection", "keep-alive");

            var messages = new string[] { 
                "Hello, ", 
                "this is an SSE message!", 
                "Here's another message."
            };

            foreach (var message in messages)
            {
                await Response.WriteAsync($"data: {message}\n\n");
                await Response.Body.FlushAsync();  // 强制立即发送
                await Task.Delay(1000);  // 每条消息间隔1秒
            }
        }
    }
}

客户端实现

<div id="messages"></div>

<script>
    // 创建SSE连接
    const eventSource = new EventSource('https://localhost:7148/api/stream/sse');

    // 监听消息事件
    eventSource.onmessage = function (event) {
      const messageContainer = document.getElementById('messages');
      const newMessage = document.createElement('p');
      newMessage.textContent = event.data;
      messageContainer.appendChild(newMessage);

      // 滚动到最新消息
      messageContainer.scrollTop = messageContainer.scrollHeight;
    };

    // 监听打开连接事件
    eventSource.onopen = function () {
      console.log("连接已打开");
    };

    // 监听错误事件
    eventSource.onerror = function (error) {
      console.error("发生错误", error);
      eventSource.close(); // 关闭连接
    };
</script>

2. WebSocket

适用场景

  • 需要双向实时通信的应用,如在线聊天、多人游戏等。

服务器端实现

using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace StreamedResponseExample.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", message);
        }
    }
}

[Route("api/[controller]")]
[ApiController]
public class StreamController : ControllerBase
{
    private readonly IHubContext<ChatHub> _hubContext;

    public StreamController(IHubContext<ChatHub> hubContext)
    {
        _hubContext = hubContext;
    }

    [HttpGet("websocket")]
    public async Task WebSocketStream()
    {
        var messages = new string[] { 
            "Hello, ", 
            "this is a WebSocket message!", 
            "Here's another one."
        };

        foreach (var message in messages)
        {
            await _hubContext.Clients.All.SendAsync("ReceiveMessage", message);
            await Task.Delay(1000);  // 每条消息间隔1秒
        }
    }
}

客户端实现

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/5.0.13/signalr.min.js"></script>

<script>
    const connection = new signalR.HubConnectionBuilder()
        .withUrl("/chatHub")
        .build();

    connection.on("ReceiveMessage", function (message) {
        console.log("Received message: ", message);
        const contentDiv = document.getElementById('content');
        contentDiv.innerHTML += message + '<br>';
    });

    connection.start().catch(function (err) {
        return console.error(err.toString());
    });
</script>

<body>
  <div id="content"></div>
</body>

3. 分块传输编码

Chunked Transfer Encoding

适用场景

  • 大数据流的分块传输,支持逐步发送响应。

服务器端实现

using Microsoft.AspNetCore.Mvc;
using System.Text;
using System.Threading.Tasks;

namespace StreamedResponseExample.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class StreamController : ControllerBase
    {
        [HttpGet("chunked")]
        public async Task ChunkedResponse()
        {
            Response.ContentType = "text/plain";
            var phrases = new string[] { 
                "This is ", 
                "a chunked ", 
                "response! ", 
                "Enjoy it."
            };

            foreach (var phrase in phrases)
            {
                byte[] buffer = Encoding.UTF8.GetBytes(phrase);
                await Response.Body.WriteAsync(buffer, 0, buffer.Length);
                await Response.Body.FlushAsync();  // 强制发送数据块
                await Task.Delay(1000);  // 每块之间的延时
            }
        }
    }
}

客户端实现

<body>
  <div id="content"></div>
  <script>
    fetch('https://localhost:7128/api/stream/chunked')
      .then(response => {
        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        const contentDiv = document.getElementById('content');

        function readChunk() {
          reader.read().then(({ done, value }) => {
            if (done) return;
            contentDiv.innerHTML += decoder.decode(value, { stream: true }) + '<br>';
            readChunk();  // 继续读取下一个数据块
          });
        }

        readChunk();
      });
  </script>
</body>

结论

  • SSE:适合服务器向客户端推送数据(单向流),专门用于事件推送。
  • WebSocket:适合需要双向实时通信的应用。
  • 分块传输编码:适合大数据流的分块传输,支持逐步发送响应。

本文作者:ShenHaoCore

本文链接:https://www.cnblogs.com/ShenhaoCore/p/18712754

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ShenHaoCore  阅读(36)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开