1. 什么是Web Sockethtml
Web Socket是Html5中引入的通讯机制,它为浏览器与后台服务器之间提供了基于TCP的全双工的通讯通道。用以替代以往的LongPooling等comet style的实时解决方案。基于它们之间的比较以及Web Socket的优点参考https://www.websocket.org/quantum.html.git
2. Web Socket如何工做github
Connectweb
Web Socket在创建以前须要先与后台服务器进行握手。具体来讲经过以下Http请求:api
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com
后台服务器若是支持切换到WebSocket,会返回以下Response:浏览器
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
浏览器收到该Response会切换到基于当前TCP链接的WebSocket通道。服务器
Data Transferwebsocket
链接创建后,浏览器端能够和服务器发送Text类型的消息进行全双工的通讯,相似于基于TCP的Socket通讯。异步
Disconnectsocket
当浏览器或者后台服务器想终止通讯,需向对方发送类型为Close的消息,并等待对方收到消息并确认后链接断开。
3. 浏览器,服务器支持状况
参考维基百科,目前浏览器的支持状况以下:
IE | Chrome | Firefox | Safari | Opera | ||
10+ | 16+ | 11+ | 6+ | 12.10+ |
IIS从8.0开始支持Web Socket
好了,很长的铺垫以后正式进入Coding:
1. 为了接受浏览器端的握手请求,咱们须要定义一个Web Api接口接受握手请求
[RoutePrefix("api/chat")] public class ChatController : ApiController { private static List<WebSocket> _sockets = new List<WebSocket>(); [Route] [HttpGet] public HttpResponseMessage Connect(string nickName) { HttpContext.Current.AcceptWebSocketRequest(ProcessRequest); //在服务器端接受Web Socket请求,传入的函数做为Web Socket的处理函数,待Web Socket创建后该函数会被调用,在该函数中能够对Web Socket进行消息收发 return Request.CreateResponse(HttpStatusCode.SwitchingProtocols); //构造赞成切换至Web Socket的Response. } }
2. 为了对Web Socket进行消息收发,须要定义Web Socket的消息收发函数(之前的.net 版本是接受一个实现特定接口的对象,新版改为了接受一个函数,总以为怪怪的),代码以下:
public async Task ProcessRequest(AspNetWebSocketContext context) { var socket = context.WebSocket;//传入的context中有当前的web socket对象 _sockets.Add(socket);//此处将web socket对象加入一个静态列表中
//进入一个无限循环,当web socket close是循环结束 while (true) { var buffer = new ArraySegment<byte>(new byte[1024]); var receivedResult = await socket.ReceiveAsync(buffer, CancellationToken.None);//对web socket进行异步接收数据 if (receivedResult.MessageType == WebSocketMessageType.Close) { await socket.CloseAsync(WebSocketCloseStatus.Empty, string.Empty, CancellationToken.None);//若是client发起close请求,对client进行ack _sockets.Remove(socket); break; } if (socket.State == System.Net.WebSockets.WebSocketState.Open) { string recvMsg = Encoding.UTF8.GetString(buffer.Array, 0, receivedResult.Count); var recvBytes = Encoding.UTF8.GetBytes(recvMsg); var sendBuffer = new ArraySegment<byte>(recvBytes); foreach (var innerSocket in _sockets)//当接收到文本消息时,对当前服务器上全部web socket链接进行广播 { if (innerSocket != socket) { await innerSocket.SendAsync(sendBuffer, WebSocketMessageType.Text, true, CancellationToken.None); } } } }
服务器端代码就绪,浏览器端如何去调用呢,继续看代码:
var webSocket = new WebSocket("ws://localhost/api/chat?nickName=" + nickName.value); webSocket.onopen = function () { console.log("opened"); } webSocket.onerror = function () { console.log("web socket error"); } webSocket.onmessage = function (event) {
console.log("web socket error");
}
self.webSocket.onclose = function () { console.log("closed"); }
这样就创建了一个web socket链接并能收到消息了,固然也会有发送消息的接口:
webSocket.send(“Hello Web Socket”);
到这里,基于web socket的全双工通讯机制已经创建好了。文中的demo是一个简易的聊天室程序。代码连接:https://github.com/lbwxly/WebSocketSample
补充:
HttpContext的AcceptWebSocketRequest方法只能接受一个函数确实有点不方便,也不利于封装与复用。所以为其添加了一个接受对象的重载extension方法,而后定义一个类WebSocketHandler来处理消息的收发和链接的关闭。详见上面的代码连接。