首先说明:这里的tomcat用的是tomcat8.0.36,并不适合tomcat7以及如下版本,(没办法websocket的api一直在变,到8以后貌似稳定下来了)javascript
websocket也是html5的新增长内容之一,号称是下一代客户端/服务器异步通讯办法,私觉得虽然有点吹牛的成分,可是之后说不定能成为异步通讯的半壁江山,至于取代ajax,我觉的应该不会html
websocket的一个颇有意思的特色就是双向通讯,这一点其实也不稀奇,跟socket同样的。
我记得大二上学期的java课程设计我作的是一个仿照QQ的用户程序socket通讯,写起来虽然顺畅可是由于那个c/s架构写起来痛苦不已,只实现一个简单的群聊就要大概一千五的代码量,尤为回调函数跟界面绑定的时候,写起来很X痛,今后就再没写过windows界面程序
下边是websocket的原理性知识总结是写给我本身看的,若是你没兴趣,能够跳过直接到代码:前端
tcp创建链接
tcp链接的创建须要经历”三次握手“的过程。过程以下client发送SYN包(值为j)以及SEQ包到server端,此时client进入SYN_SEND状态。此为第一次握手。server端收到SYN包后,发送一个ACK(值为seq+1)确认包和SYN(值为k)给client,此时server进入SYN_RECV状态。此为第二次握手。client收到SYN+ACK包后,向server发送一个ACK(值为k+1),该包发送完成后,client和server均进入ESTABLISH状态。此为第三次握手。html5
client和server两端状态变化以下:java
client:node
CLOSED->SYN_SEND->ESTABLISHweb
server:ajax
CLOSED->LISTEN->SYN_RECV->ESTABLISHwindows
tcp链接释放
Tcp释放链接的过程须要经历“四次挥手”的过程,为何创建链接只须要3次握手,而释放链接须要进行4次挥手呢?很简单,由于TCP链接是全双工(Full Duplex)的,所以形成了两个方向都须要进行关闭。怎么理解呢?client和server,须要关闭链接,此时client通知server我要关闭链接了,此时关闭的只会是client这一端的链接,而server端并未关闭,它依旧可以向client发送数据。固然,关闭链接也能够是server做为主动方的。接下来以client主动断开与server端的链接为场景来描述整个过程,咱们把它分为两个阶段,分别为client端关闭链接和server端关闭链接。api
第一阶段 首先client会发送一个FIN包给server(同时还有ack和seq包),这是要告诉server,我已经没有数据要发给你了,此时client处于FIN_WAIT_1状态。接收到FIN包的server处于CLOSE_WAIT的状态。 server发回一个ACK(值为client传过来的seq+1)和seq(值为client传过来的ack的值)给client。client收到server发过来的包后确认关闭链接,此时client处于FIN_WAIT_2。 第二阶段 server在接收到client的FIN后,得知client要断开tcp链接了,因而在发送完ack和seq给client后,本身发送一个FIN包给client(也带有ack和seq包),告诉client我也要断开链接了,此时server处于LAST_ACK状态。 client接收到server的FIN信息后,会回复server一个ack包,而且会进入TIME_WAIT状态,持续2个MSL(Max Segment Lifetime),这个时间windows下为240s。而server接收到client后便关闭链接。client在指定时间事后仍然没有接收到server的数据,确认server已经没有数据过来,也关闭了链接。 此时双方都进入CLOSED状态。
创建细节
tcp是传输层的协议,tcp三次握手后,应用层协议http也便创建了链接。而对于当今web的发展状况,http仍有许多瓶颈。
一条链接只能发送一个请求。 请求只能从客户端开始。客户端不能够接收除响应之外的指令。 请求/响应首部未经压缩发送,首部信息越多延迟越大。 发送冗长的首部。每次互相发送相同的首部形成较多的浪费。 可任意选择数据压缩格式。非强制压缩发送。 虽然已经出现了不少解决方案,如ajax、comet,可是他们最终使用的都是http协议,所以也没法从根本上解决这些瓶颈。
所以也就诞生了一个新的通讯协议,WebSocket协议,一种全双工通讯协议。
该通讯协议创建在http协议的基础之上,所以链接的发起方仍然是客户端,在http链接创建以后,再将协议升级为webSocket链接,在webSocket链接创建以后,客户度和服务器端均可以主动向对方发送报文信息了。
创建webSocket链接,须要先创建http链接,并在此基础上再进行一次”握手“。
client会发起一个”握手“的请求,请求首部含有upgrade:websocket(还有其余首部,具体看以下示例)。服务器端返回一个101状态码,确认转换协议。完成握手后即可以使用websocket协议进行通讯。
Client(request)
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: AQIDBAUGBwgJCgsMDQ4PEC== Origin: http://example.com Sec-WebSocket-protocol: chat, superchat Sec-WebSocket-Version: 13
server (response)
HTTP/1.1 101 switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Protocol: chat
websocket这个协议涉及前端显示,以及服务器处理,我这里使用基础的java+js来实现一个简单的群聊
若是不熟悉websocket的api,你能够看这里:https://developer.mozilla.org...
以及:https://www.ibm.com/developer...
重要的只有两个文件:chat.java以及chat.html
Chat.java:
package example; import java.io.IOException; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; @ServerEndpoint(value="/ws/chat/{nickName}") public class Chat { private static final Set<Chat> connections = new CopyOnWriteArraySet<Chat>(); private String nickName; private Session session; public Chat(){ } /* * 打开链接 */ @OnOpen public void onOpen(Session session,@PathParam(value="nickName") String nickName){ this.session=session; this.nickName=nickName; connections.add(this); System.out.println("新用户链接进入,名字是:"+this.nickName); String message=String.format("System>%s %s",this.nickName,"hasjoined."); Chat.broadCast(message); } /* * 关闭链接 */ @OnClose public void onClose(){ connections.remove(this); String message=String.format("System> %s, %s", this.nickName, " has disconnection."); Chat.broadCast(message); } /* * 接收信息 */ @OnMessage public void onMessage(String message,@PathParam(value="nickName")String nickName){ System.out.println("新消息from:"+nickName+" : "+message); Chat.broadCast(nickName+">"+message); } /* * 错误消息 */ @OnError public void onError(Throwable throwable){ System.out.println(throwable.getMessage()); } /* * 广播消息 */ private static void broadCast(String message){ for(Chat chat:connections){ try{ synchronized (chat) { chat.session.getBasicRemote().sendText(message); } }catch(IOException e){ connections.remove(chat); try{ chat.session.close(); }catch(IOException e1){ chat.broadCast(String.format("System> %s %s", chat.nickName, " has bean disconnection.")); } } } } }
chat.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>Testing websockets</title> </head> <body> <div> <input type="text" id="yourName"/> <button id="start">click to start</button> </div> <div style="width:100%;"> <div id="messages" style="border:5px solid red;width:50%;height:300px;margin-top:20px;"></div> </div> <div style="width:100%;margin-top:20px;"> <div id="sends" style="float:left;width:50%;height:50px;margin-right:20px;"> <input type="text" id="MessageUN" style="width:100%;height:48px;"/> </div> <button style="float:left;width:10%;height:50px;" id="sendMessage">发送</button> </div> <script type="text/javascript"> var button=document.getElementById("start"); button.onclick=function(){ var name=document.getElementById("yourName"); console.log(name.value); var websocketAdd="ws://localhost:8080/t8j8/ws/chat/"+name.value; var webSocket=new WebSocket(websocketAdd); function onMessage(event) { document.getElementById('messages').innerHTML += '<br />' + event.data; } function onOpen(event) { document.getElementById('messages').innerHTML = 'Connection established'; //一旦连接开始,尝试发出一条通信消息 start(); alert("消息通道开启,能够发送消息了"); //确认连接开始,就能够开始消息的发送了 //首先绑定一个点击事件 var sendMessage=document.getElementById("sendMessage"); sendMessage.onclick=function(){ var message= document.getElementById('MessageUN').value; //咱们以前会有一个惟一的标识符,就是在click to start以前的标识符 webSocket.send(message); //上边一部完成以后,会自动触发onmessage事件 } } function start() { webSocket.send(name.value+" : "+'hello'); } function onError(event) { alert(event.data); } webSocket.onerror = function(event) { onError(event) }; webSocket.onopen = function(event) { onOpen(event) }; webSocket.onmessage = function(event) { onMessage(event) }; } </script> </body> </html>
WebSocket 是什么原理?为何能够实现持久链接?(看尤雨溪大神的回答)
若是你熟悉nodejs你还能够:
使用Node.js+Socket.IO搭建WebSocket实时应用