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实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | <!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#实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | 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
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步