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。

 

posted @   可乐加冰-Mr-Wang  阅读(3980)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示