在现在很多业务场景(比如聊天室),又或者是手机端的一些online游戏,都需要做到实时通信,那怎么来进行双向通信呢,总不见得用曾经很破旧的ajax每隔10秒或者每隔20秒来请求吧,我的天呐(
),这尼玛太坑了
跟webservice来相比,Web Socket可以做到保持长连接,或者说强连接,一直握手存在两端可以互相发送消息互相收到消息,而webservice是一次性的,你要我响应就必须要请求我一次(黄盖:“请鞭挞我吧!”)
注:浏览器需要使用高版本的chrome或者Firefox,Tomcat使用8
先来了解一下基本概念
一、WebSocket是HTML5出的,是一种协议,也就是说原版的HTTP协议没有变化的,又或者说这两者压根就是不一样的东西,HTTP本身就不支持强连接
二、Websocket是什么样的协议,具体有什么优点
首先,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。
举个栗子吧,简单来说
1) HTTP的生命周期通过Request来界定,也就是一个Request 对应一个Response,或者多个Request 对应多个Response,
也就是说request对应的response数量是恒定不变的。而且这个response也是被动的,不能主动发起,必须有request才会有response
那么Websocket究竟是啥玩意呢
首先Websocket是基于HTTP协议的,或者说引用了HTTP的协议来完成一小部分的握手
简单来说,客服的发起请求到服务端,服务端找到对应的小弟(服务助理),找到好,这个小弟就会一直和老大保持联系,为老大服务
三、Websocket的作用
曾经接触WebSocket之前,我接触过ajax轮询以及long poll ,先来说说这2个概念,因为至今还有一些小项目是这么做的
ajax轮询:
原理非常简单,JS控制让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息,有的话就响应给客户端
以此循环获取后端的数据,同时浏览器又不需要刷新
简单的例子:OA首页显示流程,每个几秒刷新看看有没有需要处理的新流程出现
long poll:
long poll 其实原理跟 ajax轮询 差不多,都是采用循环的方式,不过采取的手段不太友好,是阻塞模型,客户端发起请求后,如果没响应,就一直不返回Response,直到有响应才返回,返回完之后,客户端再次建立连接,如此循环往复不亦乐乎。。。
从上面这两种方式看出他们都是在不断地建立HTTP连接,然后等待服务器处理,这样显得十分被动
那么缺点也随之而来:
这两种形式非常消耗资源,性能也不不好
好!接下来说说Websocket
Websocket的出现,使得资源不需要像之前那种方式那么浪费
它非常主动,服务端就可以主动推送信息给客户端
所以,只需建立一次HTTP请求,就可以做到源源不断的信息传送了。(就像你在手机上玩ol游戏,一开始建立连接后,你就一直保持在线,除非你断线再连)
下面贴出我的代码片段以及github地址
功能点:
spring websocket chating room
使用spring websocket实现聊天室基本功能
1.群发消息给所有人
2.悄悄话给某个人
效果:
主要代码:
pom.xml引入必要的库
1 <?xml version="1.0" encoding="UTF-8"?>
2
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4 4.0.0
5 com.lee
6 websocket
7 maven-spring-websocket-01
8 war
9 1.0.0-BUILD-SNAPSHOT
10
11
12
13 1.7
14 UTF-8
15 UTF-8
16
17 4.0.0.RELEASE
18
19 4.11
20
21
22 1.0.13
23 1.7.7
24
25
26
27
28
29 org.springframework
30 spring-core
31 ${spring.version}
32
33
34
35 org.springframework
36 spring-web
37 ${spring.version}
38
39
40
41 org.springframework
42 spring-webmvc
43 ${spring.version}
44
45
46
47
48 jstl
49 jstl
50 1.2
51
52
53
54
55 org.springframework
56 spring-test
57 ${spring.version}
58 test
59
60
61
62
63 org.springframework
64 spring-jdbc
65 ${spring.version}
66
67
68
69 junit
70 junit
71 4.8.2
72 test
73
74
75
76
77 org.springframework
78 spring-websocket
79 ${spring.version}
80
81
82 org.springframework
83 spring-messaging
84 ${spring.version}
85
86
87
88
89 com.fasterxml.jackson.core
90 jackson-databind
91 2.3.0
92
93
94
95 commons-fileupload
96 commons-fileupload
97 1.2.2
98
99
100 commons-io
101 commons-io
102 2.2
103
104
105
106
107 org.slf4j
108 slf4j-api
109 ${slf4j.version}
110 compile
111
112
113 ch.qos.logback
114 logback-classic
115 ${logback.version}
116 runtime
117
118
119
120
121 com.alibaba
122 druid
123 1.0.4
124
125
126
127
128 mysql
129 mysql-connector-java
130 5.1.29
131
132
133
134
135
136
137
138 org.apache.maven.plugins
139 maven-compiler-plugin
140
141 1.7
142 1.7
143
144
145
146
147
148
主要结构
HandshakeInterceptor.java
1 packagecom.lee.websocket;2
3 importjava.util.Map;4
5 importjavax.servlet.http.HttpSession;6
7 importorg.springframework.http.server.ServerHttpRequest;8 importorg.springframework.http.server.ServerHttpResponse;9 importorg.springframework.http.server.ServletServerHttpRequest;10 importorg.springframework.web.socket.WebSocketHandler;11
12 public class HandshakeInterceptor implementsorg.springframework.web.socket.server.HandshakeInterceptor {13
14 //进入hander之前的拦截
15 @Override16 public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map map) throwsException {17 if (request instanceofServletServerHttpRequest) {18 ServletServerHttpRequest servletRequest =(ServletServerHttpRequest) request;19
20 String clientName = (String)servletRequest.getServletRequest().getParameter("name");21 System.out.println(clientName);22
23 HttpSession session = servletRequest.getServletRequest().getSession(true);24 //String userName = "lee";
25 if (session != null) {26 //使用userName区分WebSocketHandler,以便定向发送消息27 //String clientName = (String) session.getAttribute("WEBSOCKET_USERNAME");
28 map.put("WEBSOCKET_USERNAME", clientName);29 }30 }31 return true;32 }33
34 @Override35 public voidafterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {36
37 }38
39 }
HomeController.java
1 packagecom.lee.websocket;2
3 importjava.text.DateFormat;4 importjava.util.Date;5 importjava.util.Locale;6
7 importorg.slf4j.Logger;8 importorg.slf4j.LoggerFactory;9 importorg.springframework.stereotype.Controller;10 importorg.springframework.ui.Model;11 importorg.springframework.web.bind.annotation.RequestMapping;12 importorg.springframework.web.bind.annotation.RequestMethod;13
14 /**
15 * Handles requests for the application home page.16 */
17 @Controller18 public classHomeController {19
20 private static final Logger logger = LoggerFactory.getLogger(HomeController.class);21
22 /**
23 * Simply selects the home view to render by returning its name.24 */
25 @RequestMapping(value = "/", method =RequestMethod.GET)26 publicString home(Locale locale, Model model) {27 logger.info("Welcome home! The client locale is {}.", locale);28
29 Date date = newDate();30 DateFormat dateFormat =DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);31
32 String formattedDate =dateFormat.format(date);33
34 model.addAttribute("serverTime", formattedDate );35
36 return "home";37 }38
39 @RequestMapping(value = "/chat", method =RequestMethod.GET)40 publicString chat(Locale locale, Model model) {41 return "chat";42 }43
44 }
WebSocketConfig.java
1 packagecom.lee.websocket;2
3 importorg.springframework.context.annotation.Configuration;4 importorg.springframework.web.socket.config.annotation.EnableWebSocket;5 importorg.springframework.web.socket.config.annotation.WebSocketConfigurer;6 importorg.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;7
8 @Configuration9 @EnableWebSocket//开启websocket
10 public class WebSocketConfig implementsWebSocketConfigurer {11 @Override12 public voidregisterWebSocketHandlers(WebSocketHandlerRegistry registry) {13 registry.addHandler(new WebSocketHander(),"/echo").addInterceptors(new HandshakeInterceptor()); //支持websocket 的访问链接
14 registry.addHandler(new WebSocketHander(),"/sockjs/echo").addInterceptors(new HandshakeInterceptor()).withSockJS(); //不支持websocket的访问链接
15 }16 }
WebSocketHander.java
1 packagecom.lee.websocket;2
3 importjava.io.IOException;4 importjava.util.ArrayList;5
6 importorg.slf4j.Logger;7 importorg.slf4j.LoggerFactory;8 importorg.springframework.web.socket.CloseStatus;9 importorg.springframework.web.socket.TextMessage;10 importorg.springframework.web.socket.WebSocketHandler;11 importorg.springframework.web.socket.WebSocketMessage;12 importorg.springframework.web.socket.WebSocketSession;13
14 public class WebSocketHander implementsWebSocketHandler {15 private static final Logger logger = LoggerFactory.getLogger(WebSocketHander.class);16
17 private static final ArrayList users = new ArrayList<>();18
19 //初次链接成功执行
20 @Override21 public void afterConnectionEstablished(WebSocketSession session) throwsException {22 logger.debug("链接成功......");23 users.add(session);24 String userName = (String) session.getHandshakeAttributes().get("WEBSOCKET_USERNAME");25 if(userName!= null){26 session.sendMessage(new TextMessage("欢迎来到Nathan的聊天室,我们开始聊天吧!~"));27 }28 }29
30 //接受消息处理消息
31 @Override32 public void handleMessage(WebSocketSession session, WebSocketMessage> webSocketMessage) throwsException {33 String clientName = (String) session.getHandshakeAttributes().get("WEBSOCKET_USERNAME");34
35 clientName = "" + clientName + "";36
37 String msg =webSocketMessage.getPayload().toString();38 String charter = "";39
40 String msgs[] = msg.split("\\|");41 if (msgs.length > 1) {42 msg = msgs[1];43 charter = msgs[0];44 sendMessageToUser(charter, new TextMessage(clientName + " 悄悄地对你说 :" +msg));45 } else{46 sendMessageToUsers(new TextMessage(clientName + " 说:" +msg));47 }48
49 }50
51 @Override52 public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throwsException {53 if(webSocketSession.isOpen()){54 webSocketSession.close();55 }56 logger.debug("链接出错,关闭链接......");57 users.remove(webSocketSession);58 }59
60 @Override61 public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throwsException {62 logger.debug("链接关闭......" +closeStatus.toString());63 users.remove(webSocketSession);64 }65
66 @Override67 public booleansupportsPartialMessages() {68 return false;69 }70
71 /**
72 * 给所有在线用户发送消息73 *74 *@parammessage75 */
76 public voidsendMessageToUsers(TextMessage message) {77 for(WebSocketSession user : users) {78 try{79 if(user.isOpen()) {80 user.sendMessage(message);81 }82 } catch(IOException e) {83 e.printStackTrace();84 }85 }86 }87
88 /**
89 * 给某个用户发送消息90 *91 *@paramuserName92 *@parammessage93 */
94 public voidsendMessageToUser(String userName, TextMessage message) {95 for(WebSocketSession user : users) {96 if (user.getHandshakeAttributes().get("WEBSOCKET_USERNAME").equals(userName)) {97 try{98 if(user.isOpen()) {99 user.sendMessage(message);100 }101 } catch(IOException e) {102 e.printStackTrace();103 }104 break;105 }106 }107 }108 }
Person.java
1 packagecom.lee.websocket.entity;2
3 public classPerson {4
5 private intage;6 privateString name;7 privateString sex;8
9 public intgetAge() {10 returnage;11 }12 public void setAge(intage) {13 this.age =age;14 }15 publicString getName() {16 returnname;17 }18 public voidsetName(String name) {19 this.name =name;20 }21 publicString getSex() {22 returnsex;23 }24 public voidsetSex(String sex) {25 this.sex =sex;26 }27
28 }
chat.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
webSocket测试16
17 varchater;18
19 $(function(){20
21 varwebsocket;22 functionconnectServer() {23 var clientName = $("#client_name").val();24 if ("WebSocket" inwindow) {25 websocket = new WebSocket("ws://127.0.0.1:8080/websocket/echo?name=" +clientName);26 } else if ("MozWebSocket" inwindow) {27 alert("MozWebSocket");28 websocket = new MozWebSocket("ws://echo");29 } else{30 alert("SockJS");31 websocket = new SockJS("http://127.0.0.1:8080/websocket/sockjs/echo");32 }33 }34
35 //websocket.onopen = function (evnt) {
36 //$("#tou").html("链接服务器成功!")
37 //};
38 //websocket.onmessage = function (evnt) {
39 //$("#msg").html($("#msg").html() + "
" + evnt.data);
40 //};
41 //websocket.onerror = function (evnt) {
42 //};
43 //websocket.onclose = function (evnt) {
44 //$("#tou").html("与服务器断开了链接!")
45 //}
46
47 $("#conncet_server").bind("click", function() {48 connectServer();49
50 websocket.onopen = function(evnt) {51 $("#tou").html("链接服务器成功!")52 };53 websocket.onmessage = function(evnt) {54 $("#msg").html($("#msg").html() + "
" +evnt.data);55 };56 websocket.onerror = function(evnt) {57 };58 websocket.onclose = function(evnt) {59 $("#tou").html("与服务器断开了链接!")60 }61 });62
63 $("#send").bind("click", function() {64 send();65 });66
67 functionsend(){68 if (websocket != null) {69 var message = document.getElementById("message").value;70
71 if ($.trim(chater) != "") {72 message = chater + "|" +message;73 }74
75 websocket.send(message);76 } else{77 alert("未与服务器链接.");78 }79 }80 });81
82 functionchangeChater(e) {83 chater =$(e).html();84 alert("您将和" + chater + "进行聊天...");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