在现代Web应用中,实时数据传输和高效的数据流处理变得越来越重要。AspNetCore 提供了多种流式响应机制,以满足不同场景下的需求。
在使用ChatGpt,deepseek的适合有没有想过ai的逐字显示回答是怎么实现的,下面将介绍三种主要的流式响应来实现此功能。
Server-Sent Events (SSE)
Server-Sent Events (SSE) 是一种允许服务器主动向客户端推送数据的机制,适用于实时更新的应用(如聊天应用、实时监控、新闻推送等)。
通过设置Content-Type: text/event-stream
来使用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秒}}}
}
客户端-html:
<div id="messages"></div><script>// 创建SSE连接const eventSource = new EventSource('https://localhost:7148/WeatherForecast/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>
说明:
Response.ContentType = "text/event-stream"
:设置响应的类型为SSE。- 通过
data: {message}\n\n
格式发送消息,每次都以“data:”开头。 - 使用
EventSource
API在客户端接收事件流。
SSE是一种轻量级且简单的流式响应方式,尤其适用于向客户端推送消息的场景,如实时数据更新。
WebSocket
WebSocket 是一种全双工通信协议,适用于需要双向实时通信的应用,如在线聊天、多人游戏等。优势在于可以同时接收和发送消息,且没有HTTP请求/响应的开销。
通过WebSocket,客户端和服务器之间建立持久连接,双向传输消息。
使用WebSocket进行流式传输服务器端:
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;namespace StreamedResponseExample.Controllers
{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秒}}}
}
客户端:
const connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();connection.on("ReceiveMessage", function (message) {console.log("Received message: ", message);
});connection.start().catch(function (err) {return console.error(err.toString());
});
- WebSocket的优点在于能够进行实时的双向通信,适用于需要交互的场景。
- 使用SignalR库可以简化WebSocket的实现,SignalR封装了WebSocket等协议,提供了更易用的API。
Chunked Transfer Encoding (分块传输编码)
分块传输编码是HTTP/1.1的一种机制,它允许服务器以多个“块”的形式将响应数据发送给客户端。客户端接收到每个块后可以立即处理,而不是等待所有数据传输完成。
ASP.NET Core默认支持分块传输编码,当响应体的内容未知时,分块传输会自动启用。
使用分块传输编码(Chunked 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); // 每块之间的延时}}}
}
客户端:
客户端可以像普通HTTP请求一样使用fetch
或XMLHttpRequest
接收分块数据,并逐步处理每个数据块。
<body><div id="content"></div><script>fetch('https://localhost:7128/WeatherForecast/ChunkedResponse/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>
说明:
- 分块传输编码:这是HTTP协议中的一个标准机制。它可以在响应数据还没有完全生成时就开始传输,这样客户端可以逐步接收并处理数据。
结论:
- SSE:适合服务器向客户端推送数据(单向流)专门用于事件推送。
- WebSocket:适合需要双向实时通信的应用。
- 分块传输编码:适合大数据流的分块传输,支持逐步发送响应。