javascript中WebSocket用法
一.概念
WebSocket定义
WebSocket是html5提供的一种在单个TCP连接上进行双向通信的协议,解决了客户端和服务端之间的实时通信问题。浏览器和服务器只需完成一次握手,两者之间就可以创建一个持久性的TCP连接,此后服务器和客户端通过此TCP连接进行双向实时通信。
WebSocket优点
很多网站为了实现数据推送,所用的技术都是ajax轮询。轮询是在特定的时间间隔,由浏览器主动发起请求,将服务器的数据拉回来。轮询需要不断的向服务器发送请求,会占用很多带宽和服务器资源。WebSocket建立TCP连接后,服务器可以主动给客户端传递数据,能够更好的节省服务器资源和带宽,实现更实时的数据通讯
WebSocke的属性
WebSocket的方法
WebSocket的事件
客户端支持WebSocket的浏览器中,在创建socket后,可以通过onopen、onmessage、onclose和onerror四个事件对socket进行响应。WebSocket的所有操作都是采用事件的方式触发的,这样不会阻塞UI,是的UI有更快的响应时间,有更好的用户体验,浏览器通过Javascript向服务器发出建立WebSocket连接的请求,连接建立后,客户端和服务器就可以通过TCP连接直接交换数据。当你获取WebSocket连接后,可以通多send()方法向服务器发送数据,可以通过onmessage事件接收服务器返回的数据.
js实现:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>WebSocket Test</title> <script language="javascript" type="text/javascript"> var wsUri = "ws://localhost:5000/ws"; var output; function init() { output = document.getElementById("output"); document.getElementById('connect').onclick = function (e) { testWebSocket(); }; } function testWebSocket() { websocket = new WebSocket(wsUri); websocket.onopen = function (evt) { onOpen(evt) }; websocket.onclose = function (evt) { onClose(evt) }; websocket.onmessage = function (evt) { onMessage(evt) }; websocket.onerror = function (evt) { onError(evt) }; } function onOpen(evt) { writeToScreen("CONNECTED"); doSend("ping"); } function onClose(evt) { writeToScreen("DISCONNECTED"); } function onMessage(evt) { writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data + '</span>'); websocket.close(); } function onError(evt) { writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); } function doSend(message) { writeToScreen("SENT: " + message); websocket.send(message); } function writeToScreen(message) { var pre = document.createElement("p"); pre.style.wordWrap = "break-word"; pre.innerHTML = message; output.appendChild(pre); } window.addEventListener("load", init, false); </script> </head> <body> <h2>WebSocket Test</h2> <input type="button" id="connect" value="连接WebSocket"/> <div id="output"></div> </body> </html> <script> </script>
后端C#实现:
public class WebsocketHandlerMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; private readonly IWebsocketCollection _websocketCollection; public WebsocketHandlerMiddleware( RequestDelegate next, ILoggerFactory loggerFactory ) { _next = next; _logger = loggerFactory. CreateLogger<WebsocketHandlerMiddleware>(); _websocketCollection = WebsocketCollection.Instance; } public async Task Invoke(HttpContext context, IAppBasicAuthService appBasicAuth, IConfigService configService) { if (context.Request.Path == "/ws") { if (context.WebSockets.IsWebSocketRequest) { if (!await appBasicAuth.ValidAsync(context.Request)) { context.Response.StatusCode = 401; await context.Response.WriteAsync("basic auth failed ."); return; } var appId = context.Request.Headers["appid"]; if (string.IsNullOrEmpty(appId)) { var appIdSecret = appBasicAuth.GetAppIdSecret(context.Request); appId = appIdSecret.Item1; } context.Request.Query.TryGetValue("client_name", out StringValues name); if (!string.IsNullOrEmpty(name)) { name = HttpUtility.UrlDecode(name); } else { _logger.LogInformation("Websocket client request No Name property "); } context.Request.Query.TryGetValue("client_tag", out StringValues tag); if (!string.IsNullOrEmpty(tag)) { tag = HttpUtility.UrlDecode(tag); } else { _logger.LogInformation("Websocket client request No TAG property "); } WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); var clientIp = GetRemoteIp(context.Request); var client = new WebsocketClient() { Client = webSocket, Id = Guid.NewGuid().ToString(), AppId = appId, LastHeartbeatTime = DateTime.Now, Name = name, Tag = tag, Ip = clientIp.ToString() }; _websocketCollection.AddClient(client); _logger.LogInformation("Websocket client {0} Added ", client.Id); try { await Handle(context, client, configService); } catch (Exception ex) { _logger.LogError(ex, "Handle websocket client {0} err .", client.Id); await _websocketCollection.RemoveClient(client, WebSocketCloseStatus.Empty, ex.Message); await context.Response.WriteAsync("500 closed"); } } else { context.Response.StatusCode = 400; } } else { await _next(context); } } public IPAddress GetRemoteIp(HttpRequest httpRequest) { IPAddress ip; var headers = httpRequest.Headers.ToList(); if (headers.Exists((kvp) => kvp.Key == "X-Forwarded-For")) { // when running behind a load balancer you can expect this header var header = headers.First((kvp) => kvp.Key == "X-Forwarded-For").Value.ToString(); IPAddress.TryParse(header, out ip); } else { // this will always have a value (running locally in development won't have the header) ip = httpRequest.HttpContext.Connection.RemoteIpAddress; } return ip; } private async Task Handle(HttpContext context, WebsocketClient socketClient, IConfigService configService) { var buffer = new byte[1024 * 2]; WebSocketReceiveResult result = null; do { result = await socketClient.Client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); socketClient.LastHeartbeatTime = DateTime.Now; var message = await ReadWebsocketMessage(result, buffer); if (message == "ping") { //如果是ping,回复本地数据的md5版本 var appId = context.Request.Headers["appid"]; var md5 = await configService.AppPublishedConfigsMd5CacheWithInheritanced(appId); await SendMessage(socketClient.Client, $"V:{md5}"); } else { //如果不是心跳消息,回复0 await SendMessage(socketClient.Client, "0"); } } while (!result.CloseStatus.HasValue); _logger.LogInformation($"Websocket close , closeStatus:{result.CloseStatus} closeDesc:{result.CloseStatusDescription}"); await _websocketCollection.RemoveClient(socketClient, result.CloseStatus, result.CloseStatusDescription); } private async Task SendMessage(WebSocket webSocket, string message) { var data = Encoding.UTF8.GetBytes(message); await webSocket.SendAsync(new ArraySegment<byte>(data, 0, data.Length), WebSocketMessageType.Text, true, CancellationToken.None); } private async Task<string> ReadWebsocketMessage(WebSocketReceiveResult result, ArraySegment<Byte> buffer) { using (var ms = new MemoryStream()) { ms.Write(buffer.Array, buffer.Offset, result.Count); if (result.MessageType == WebSocketMessageType.Text) { ms.Seek(0, SeekOrigin.Begin); using (var reader = new StreamReader(ms, Encoding.UTF8)) { return await reader.ReadToEndAsync(); } } return ""; } } }
这里我用了.NetCore中自定义中间件进行拦截请求,判断请求的路径如果是ws,则做出响应的Response。
本文来自博客园,作者:可乐加冰-Mr-Wang,转载请注明原文链接:https://www.cnblogs.com/helloworld-wang/p/15104801.html