璐总的社区 發表於 2021-8-5 18:33:00

javascript中WebSocket用法

<h1>一.概念</h1>
<h2><strong><span style="color: rgba(255, 102, 0, 1); font-size: 15px">WebSocket定义</span></strong></h2>
<p><span style="color: rgba(0, 0, 0, 1)"><span style="font-size: 15px">Web<span style="font-size: 15px">Socket是html5提供的一种在单个TCP连接上进行双向通信的协议,解决了客户端和服务端之间的实时通信问题。浏览器和服务器只需完成一次握手,两者之间就可以创建一个持久性的TCP连接,此后服务器和客户端通过此TCP连接进行双向实时通信</span></span></span>。</p>
<h2><strong><span style="color: rgba(255, 102, 0, 1); font-size: 15px">WebSocket优点</span></strong></h2>
<p><span style="color: rgba(255, 102, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="font-size: 15px">很多网站为了实现数据推送,所用的技术都是ajax轮询。轮询是在特定的时间间隔,由浏览器主动发起请求,将服务器的数据拉回来。轮询需要不断的向服务器发送请求,会占用很多带宽和服务器资源。WebSocket建立TCP连接后,服务器可以主动给客户端传递数据,<span style="font-size: 15px">能够更好的节省服务器资源和带宽,实现更实时的数据通讯</span></span></span></span></p>
<h2><span style="color: rgba(255, 102, 0, 1)"><span style="font-size: 15px"><strong>WebSocke的属性</strong></span></span></h2>
<p><span style="color: rgba(255, 102, 0, 1)"><span style="font-size: 15px"><strong><img src="https://img2020.cnblogs.com/blog/1340649/202108/1340649-20210805182415339-293474346.png" alt="" loading="lazy"></strong></span></span></p>
<h2><span style="color: rgba(255, 102, 0, 1)"><span style="font-size: 15px"><strong><strong>WebSocket的方法</strong></strong></span></span></h2>
<p><span style="color: rgba(255, 102, 0, 1)"><span style="font-size: 15px"><strong><strong><img src="https://img2020.cnblogs.com/blog/1340649/202108/1340649-20210805182449308-2121422419.png" alt="" loading="lazy"></strong></strong></span></span></p>
<h2><span style="color: rgba(255, 102, 0, 1)"><span style="font-size: 15px"><strong>WebSocket的事件</strong></span></span></h2>
<p><span style="color: rgba(255, 102, 0, 1)"><span style="font-size: 15px"><strong><span style="font-size: 15px; color: rgba(153, 51, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">客户端支持WebSocket的浏览器中,在创建socket后,可以通过onopen、onmessage、onclose和onerror四个事件对socket进行响应。WebSocket的所有操作都是采用事件的方式触发的,这样不会阻塞UI,是的UI有更快的响应时间,有更好的用户体验</span></span></strong></span></span><span style="font-size: 15px"><strong>,</strong></span><span style="color: rgba(255, 102, 0, 1)"><span style="font-size: 15px"><strong><span style="font-size: 15px; color: rgba(153, 51, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">浏览器通过Javascript向服务器发出建立WebSocket连接的请求,连接建立后,客户端和服务器就可以通过TCP连接直接交换数据。当你获取WebSocket连接后,可以通多send()方法向服务器发送数据,可以通过onmessage事件接收服务器返回的数据.</span></span></strong></span></span></p>
<p><span style="color: rgba(255, 102, 0, 1)"><span style="font-size: 15px"><strong><span style="font-size: 15px; color: rgba(153, 51, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">js实现:</span></span></strong></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;title&gt;WebSocket Test&lt;/title&gt;
    &lt;script language="javascript" type="text/javascript"&gt;
      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('&lt;span style="color: blue;"&gt;RESPONSE: ' + evt.data + '&lt;/span&gt;');
            websocket.close();
      }

      function onError(evt) {
            writeToScreen('&lt;span style="color: red;"&gt;ERROR:&lt;/span&gt; ' + 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);
    &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;WebSocket Test&lt;/h2&gt;
    &lt;input type="button" id="connect" value="连接WebSocket"/&gt;
    &lt;div id="output"&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
&lt;script&gt;
   
&lt;/script&gt;
</pre>
</div>
<h2><span style="color: rgba(208, 88, 46, 1)"><strong><span style="font-size: 15px">后端C#实现:</span></strong></span></h2>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"> 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&lt;WebsocketHandlerMiddleware&gt;();
            _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) =&gt; kvp.Key == "X-Forwarded-For"))
            {
                // when running behind a load balancer you can expect this header
                var header = headers.First((kvp) =&gt; 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;
            WebSocketReceiveResult result = null;
            do
            {
                result = await socketClient.Client.ReceiveAsync(new ArraySegment&lt;byte&gt;(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&lt;byte&gt;(data, 0, data.Length), WebSocketMessageType.Text, true, CancellationToken.None);
      }

      private async Task&lt;string&gt; ReadWebsocketMessage(WebSocketReceiveResult result, ArraySegment&lt;Byte&gt; 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 "";
            }
      }
    }
</pre>
</div>
<p>&nbsp;这里我用了.NetCore中自定义中间件进行拦截请求,判断请求的路径如果是ws,则做出响应的Response。</p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:可乐加冰-Mr-Wang,转载请注明原文链接:https://www.cnblogs.com/helloworld-wang/p/15104801.html</p><br><br>
来源:https://www.cnblogs.com/helloworld-wang/p/15104801.html
頁: [1]
查看完整版本: javascript中WebSocket用法