本文将使用WebSockets(SignalR的一部分)构建一个可以双向通信的ASP.NETCore5应用。(预告:下一期将重点对比gRPC和WebSockets的区别和使用场景)我们先深入基本概念,了解一下WebSockets的幕后情况。WebSockets简介为了支持客户端/服务器端的双向通信,引入了WebSockets.HTTP1.0:每次我们向服务器发送请求时,都需要重新创建连接(关闭之前的连接)。HTTP1.1:新的keep-alive语法引入了持久连接机制,使连接可以重用---这样可以减少通信延迟(因为服务端可以感知客户端,不需要为每次请求重新握手过程)WebSockets依附于HTTP1.1协议的持久连接机制,所以如果你第一次发起WebSockets连接,这实际上是一个HTTP1.1请求,协商成功后开始全双工通信。下图描述了初始化(握手)、数据传输和关闭WebSockets的过程。该协议有两部分:握手和数据传输握手WebSocket与HTTP协议有很好的兼容性。“握手”阶段采用Http协议,默认端口为80/443,握手时不易屏蔽,可通过各种HTTP代理服务器。协议标识符是ws(如果加密则为wss),服务器URL是URL。ws://example.com:80/some/path简而言之,WebSocket连接基于单个端口上的HTTP(以TCP传输):1.服务器在指定端口(例如80/443)上侦听传入TCP套接字连接2.客户端通过HTTPGET请求发起握手(这就是“WebSockets”中“Web”的来源)。在请求头中,客户端会要求服务器将连接升级为WebSocket。3.服务器发送握手响应,通知客户端它将协议从HTTP更改为WebSocket。4.客户端/服务器协商连接细节。如果条款不匹配,任何一方都可以退出。GET/ws-endpointHTTP/1.1Host:example.com:80Upgrade:websocketConnection:UpgradeSec-WebSocket-Key:L4kHN+1Bx7zKbxsDbqgzHw==Sec-WebSocket-Version:13注意:客户端发送Connection:Upgrade和Upgrade:websocket请求头握手服务器响应:HTTP/1.1101SwitchingProtocolsUpgrade:websocketConnection:UpgradeSec-WebSocket-Accept:CTPN8jCb3BUjBjBtdjwSQCytuBo=注意:服务器返回HTTP/1.1101SwitchingProtocols状态码,除101以外的其他状态码表示握手失败。数据传输的任何一方都可以随时发送消息,因为这是一种全双工通信协议。一条消息由一个或多个帧组成。帧可以是二进制、文本或控制帧(0x8Close、0x9Ping、0xAPong)。将讨论SignalR(集线器和其他东西)。这次将完全基于WebSocket通信。app.UseWebSockets();新增WebSocketsController.cs,添加如下代码:usingSystem;usingSystem.Net.WebSockets;usingSystem.Text;usingSystem.Threading;usingSystem.Threading.Tasks;usingMicrosoft.AspNetCore.Mvc;usingMicrosoft.Extensions.Logging;namespaceWebSocketsTutorial.Controllers{[ApiController][Route([controller]")]publicclassWebSocketsController:ControllerBase{privatereadonlyILogger_logger;publicWebSocketsController(ILoggerlogger){_logger=logger;}[HttpGet("/ws")]publicasyncTaskGet(){if(HttpContext.WebSockets.IsWebSocketRequest){usingvarwebSocket=awaitHttpContext.WebSockets.AcceptWebSocketAsync();_logger.Log(LogLevel.Information,"WebSocketconnectionestablished");awaitEcho(webSocket);}else{HttpContext.Response.StatusCode=400;}}privateasyncTaskEcho(WebSocketwebSocket){varbuffer=newbyte[1024*4];varresult=awaitwebSocket.ReceiveAsync(newArraySegment(buffer),CancellationToken.None);_logger.Log(LogLevel.Information,"MessagereceivedfromClient");while(!result.CloseStatus.HasValue){varserverMsg=Encoding.UTF8.GetBytes($"Server:Hello.Yousaid:{Encoding.UTF8.GetString(buffer)}");awaitwebSocket.SendAsync(newArraySegment(serverMsg,0,serverMsg.Length),result.MessageType,result.EndOfMessage,CancellationToken.None);_logger.Log(LogLevel.Information,"MessagesenttoClient");result=awaitwebSocket.ReceiveAsync(newArraySegment(buffer),CancellationToken.None);_logger.Log(LogLevel.Information,"MessagereceivedfromClient");}awaitwebSocket.CloseAsync(result.CloseStatus.Value,result.CloseStatusDescription,CancellationToken.None);_logger.Log(LogLevel.Information,"WebSocketconnectionclosed");}}}握手后,服务器不需要等待客户端发起消息,它可以将消息推送给客户端启动ASP.NETCore服务器,程序在/ws路由地址监听WebSockets连接并发回消息sent由客户端。浏览器客户端使用WebSocketsapi在浏览器Console中编写js代码发起客户端websockets请求:letwebSocket=newWebSocket('wss://localhost:5001/ws');在request的network-Messages标签页,可以观察到双向通信:除此之外,server/client维护了一个pingpong机制来确认client是否还活着。如果你真的想查看这些数据包,可以使用WireShark之类的工具查看。整个过程在Chrome-Network上只会有一个记录,所以如果你想看“握手过程”,请在刚才的tab页面也查看一下??。最后,如果你有兴趣了解WebSocket的协议规范,请前往RFC6455阅读。本文只是对WebSockets的一个小测试,还有很多其他的东西我们可以讨论,比如安全、负载平衡、代理等等??。