我正在研究一种解决方案,它使用web socket协议来通知客户端(Web浏览器)当服务器(MVC Core Web应用程序)上发生某些事件时。我使用Microsoft.AspNetCore.WebSockets nuget。
这是我的客户端代码:
$(function () {
var socket = new WebSocket("ws://localhost:61019/data/openSocket");
socket.onopen = function () {
$(".socket-status").css("color", "green");
}
socket.onmessage = function (message) {
$("body").append(document.createTextNode(message.data));
}
socket.onclose = function () {
$(".socket-status").css("color", "red");
}
});
当此视图被加载时,套接字请求将立即发送到MVC Core应用程序。以下是控制器操作:
[Route("data")]
public class DataController : Controller
{
[Route("openSocket")]
[HttpGet]
public ActionResult OpenSocket()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
WebSocket socket = HttpContext.WebSockets.AcceptWebSocketAsync().Result;
if (socket != null && socket.State == WebSocketState.Open)
{
while (!HttpContext.RequestAborted.IsCancellationRequested)
{
var response = string.Format("Hello! Time {0}", System.DateTime.Now.ToString());
var bytes = System.Text.Encoding.UTF8.GetBytes(response);
Task.Run(() => socket.SendAsync(new System.ArraySegment<byte>(bytes),
WebSocketMessageType.Text, true, CancellationToken.None));
Thread.Sleep(3000);
}
}
}
return new StatusCodeResult(101);
}
}
这段代码运行得很好。WebSocket仅用于发送,不接收任何内容。然而,问题在于while循环会一直占用DataController线程,直到检测到取消请求。
Web socket绑定到HttpContext对象。一旦Web请求的HttpContext被销毁,套接字连接就会立即关闭。
问题1:有没有办法将套接字保留在控制器线程之外?我尝试将其放入单例中,该单例位于运行在主应用程序线程上的MVC Core启动类中。是否有任何方法可以保持套接字打开或从主应用程序线程内重新建立连接,而不是使用while循环一直占用控制器线程?即使认为在套接字连接保持打开的情况下,暂停控制器线程也是可以接受的,我仍无法想到在OpenSocket的while循环中放置任何良好代码的好方法。您认为在控制器中设置手动重置事件并等待它在OpenSocket操作中的while循环内被设置怎么样呢?
问题2:如果无法在MVC中分离HttpContext和WebSocket对象,还可以利用哪些替代技术或开发模式来实现套接字连接的复用?如果有人认为SignalR或类似库有一些代码可以使套接字独立于HttpContext,可以分享一些示例代码。如果有人认为MVC不具备处理独立套接字通信的能力,则提供一个示例,我不介意切换到纯ASP.NET或Web API,
问题3:要求保持套接字连接处于活动状态或能够重新连接,直到用户明确的超时或取消请求。想法是服务器上发生某些独立事件触发建立的套接字发送数据。如果您认为其他技术(如HTML/2或流媒体)对于此场景更有用,您可以描述您将使用的模式和框架吗?
P.S. 可能的解决方案是每秒发送一个AJAX请求,以询问服务器上是否有新数据。这是最后的手段。