跟webservice来相比,Web Socket可以做到保持长连接,或者说强连接,一直握手存在两端可以互相发送消息互相收到消息,而webservice是一次性的,你要我响应就必须要请求我一次(黄盖:“请鞭挞我吧!”)
注:浏览器需要使用高版本的chrome或者Firefox,Tomcat使用8
先来了解一下基本概念
一、WebSocket是HTML5出的,是一种协议,也就是说原版的HTTP协议没有变化的,又或者说这两者压根就是不一样的东西,HTTP本身就不支持强连接
二、Websocket是什么样的协议,具体有什么优点
首先,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。
举个栗子吧,简单来说
- 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 <project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
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 <java.version>1.7</java.version>
14 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
16
17 <spring.version>4.0.0.RELEASE</spring.version>
18
19 <junit.version>4.11</junit.version>
20
21
22 <logback.version>1.0.13</logback.version>
23 <slf4j.version>1.7.7</slf4j.version>
24
25
26
27
28
29 org.springframework
30 spring-core
31
s
p
r
i
n
g
.
v
e
r
s
i
o
n
<
/
v
e
r
s
i
o
n
>
32
<
/
d
e
p
e
n
d
e
n
c
y
>
3334
<
d
e
p
e
n
d
e
n
c
y
>
35
<
g
r
o
u
p
I
d
>
o
r
g
.
s
p
r
i
n
g
f
r
a
m
e
w
o
r
k
<
/
g
r
o
u
p
I
d
>
36
<
a
r
t
i
f
a
c
t
I
d
>
s
p
r
i
n
g
−
w
e
b
<
/
a
r
t
i
f
a
c
t
I
d
>
37
<
v
e
r
s
i
o
n
>
{spring.version}</version> 32 </dependency> 33 34 <dependency> 35 <groupId>org.springframework</groupId> 36 <artifactId>spring-web</artifactId> 37 <version>
spring.version</version>32</dependency>3334<dependency>35<groupId>org.springframework</groupId>36<artifactId>spring−web</artifactId>37<version>{spring.version}
38
39
40
41 org.springframework
42 spring-webmvc
43
s
p
r
i
n
g
.
v
e
r
s
i
o
n
<
/
v
e
r
s
i
o
n
>
44
<
/
d
e
p
e
n
d
e
n
c
y
>
4546
<
!
−
−
j
s
t
l
−
−
>
47
<
d
e
p
e
n
d
e
n
c
y
>
48
<
g
r
o
u
p
I
d
>
j
s
t
l
<
/
g
r
o
u
p
I
d
>
49
<
a
r
t
i
f
a
c
t
I
d
>
j
s
t
l
<
/
a
r
t
i
f
a
c
t
I
d
>
50
<
v
e
r
s
i
o
n
>
1.2
<
/
v
e
r
s
i
o
n
>
51
<
/
d
e
p
e
n
d
e
n
c
y
>
5253
<
!
−
−
s
p
r
i
n
g
测
试
框
架
−
−
>
54
<
d
e
p
e
n
d
e
n
c
y
>
55
<
g
r
o
u
p
I
d
>
o
r
g
.
s
p
r
i
n
g
f
r
a
m
e
w
o
r
k
<
/
g
r
o
u
p
I
d
>
56
<
a
r
t
i
f
a
c
t
I
d
>
s
p
r
i
n
g
−
t
e
s
t
<
/
a
r
t
i
f
a
c
t
I
d
>
57
<
v
e
r
s
i
o
n
>
{spring.version}</version> 44 </dependency> 45 46 <!-- jstl --> 47 <dependency> 48 <groupId>jstl</groupId> 49 <artifactId>jstl</artifactId> 50 <version>1.2</version> 51 </dependency> 52 53 <!--spring测试框架 --> 54 <dependency> 55 <groupId>org.springframework</groupId> 56 <artifactId>spring-test</artifactId> 57 <version>
spring.version</version>44</dependency>4546<!−−jstl−−>47<dependency>48<groupId>jstl</groupId>49<artifactId>jstl</artifactId>50<version>1.2</version>51</dependency>5253<!−−spring测试框架−−>54<dependency>55<groupId>org.springframework</groupId>56<artifactId>spring−test</artifactId>57<version>{spring.version}
58 test
59
60
61
62
63 org.springframework
64 spring-jdbc
65
s
p
r
i
n
g
.
v
e
r
s
i
o
n
<
/
v
e
r
s
i
o
n
>
66
<
/
d
e
p
e
n
d
e
n
c
y
>
6768
<
d
e
p
e
n
d
e
n
c
y
>
69
<
g
r
o
u
p
I
d
>
j
u
n
i
t
<
/
g
r
o
u
p
I
d
>
70
<
a
r
t
i
f
a
c
t
I
d
>
j
u
n
i
t
<
/
a
r
t
i
f
a
c
t
I
d
>
71
<
v
e
r
s
i
o
n
>
4.8.2
<
/
v
e
r
s
i
o
n
>
72
<
s
c
o
p
e
>
t
e
s
t
<
/
s
c
o
p
e
>
73
<
/
d
e
p
e
n
d
e
n
c
y
>
7475
<
!
−
−
s
p
r
i
n
g
w
e
b
s
o
c
k
e
t
库
−
−
>
76
<
d
e
p
e
n
d
e
n
c
y
>
77
<
g
r
o
u
p
I
d
>
o
r
g
.
s
p
r
i
n
g
f
r
a
m
e
w
o
r
k
<
/
g
r
o
u
p
I
d
>
78
<
a
r
t
i
f
a
c
t
I
d
>
s
p
r
i
n
g
−
w
e
b
s
o
c
k
e
t
<
/
a
r
t
i
f
a
c
t
I
d
>
79
<
v
e
r
s
i
o
n
>
{spring.version}</version> 66 </dependency> 67 68 <dependency> 69 <groupId>junit</groupId> 70 <artifactId>junit</artifactId> 71 <version>4.8.2</version> 72 <scope>test</scope> 73 </dependency> 74 75 <!--spring websocket库 --> 76 <dependency> 77 <groupId>org.springframework</groupId> 78 <artifactId>spring-websocket</artifactId> 79 <version>
spring.version</version>66</dependency>6768<dependency>69<groupId>junit</groupId>70<artifactId>junit</artifactId>71<version>4.8.2</version>72<scope>test</scope>73</dependency>7475<!−−springwebsocket库−−>76<dependency>77<groupId>org.springframework</groupId>78<artifactId>spring−websocket</artifactId>79<version>{spring.version}
80
81
82 org.springframework
83 spring-messaging
84 KaTeX parse error: Expected 'EOF', got '&' at position 755: …ing with SLF4J &̲ LogBack --> 10…{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 package com.lee.websocket;
2
3 import java.util.Map;
4
5 import javax.servlet.http.HttpSession;
6
7 import org.springframework.http.server.ServerHttpRequest;
8 import org.springframework.http.server.ServerHttpResponse;
9 import org.springframework.http.server.ServletServerHttpRequest;
10 import org.springframework.web.socket.WebSocketHandler;
11
12 public class HandshakeInterceptor implements org.springframework.web.socket.server.HandshakeInterceptor {
13
14 //进入hander之前的拦截
15 @Override
16 public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
17 if (request instanceof ServletServerHttpRequest) {
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 @Override
35 public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
36
37 }
38
39 }
复制代码
HomeController.java
复制代码
1 package com.lee.websocket;
2
3 import java.text.DateFormat;
4 import java.util.Date;
5 import java.util.Locale;
6
7 import org.slf4j.Logger;
8 import org.slf4j.LoggerFactory;
9 import org.springframework.stereotype.Controller;
10 import org.springframework.ui.Model;
11 import org.springframework.web.bind.annotation.RequestMapping;
12 import org.springframework.web.bind.annotation.RequestMethod;
13
14 /**
15 * Handles requests for the application home page.
16 /
17 @Controller
18 public class HomeController {
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 public String home(Locale locale, Model model) {
27 logger.info(“Welcome home! The client locale is {}.”, locale);
28
29 Date date = new Date();
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 public String chat(Locale locale, Model model) {
41 return “chat”;
42 }
43
44 }
复制代码
WebSocketConfig.java
复制代码
1 package com.lee.websocket;
2
3 import org.springframework.context.annotation.Configuration;
4 import org.springframework.web.socket.config.annotation.EnableWebSocket;
5 import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
6 import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
7
8 @Configuration
9 @EnableWebSocket//开启websocket
10 public class WebSocketConfig implements WebSocketConfigurer {
11 @Override
12 public void registerWebSocketHandlers(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 package com.lee.websocket;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8 import org.springframework.web.socket.CloseStatus;
9 import org.springframework.web.socket.TextMessage;
10 import org.springframework.web.socket.WebSocketHandler;
11 import org.springframework.web.socket.WebSocketMessage;
12 import org.springframework.web.socket.WebSocketSession;
13
14 public class WebSocketHander implements WebSocketHandler {
15 private static final Logger logger = LoggerFactory.getLogger(WebSocketHander.class);
16
17 private static final ArrayList users = new ArrayList<>();
18
19 //初次链接成功执行
20 @Override
21 public void afterConnectionEstablished(WebSocketSession session) throws Exception {
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 @Override
32 public void handleMessage(WebSocketSession session, WebSocketMessage<?> webSocketMessage) throws Exception {
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 @Override
52 public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
53 if(webSocketSession.isOpen()){
54 webSocketSession.close();
55 }
56 logger.debug(“链接出错,关闭链接…”);
57 users.remove(webSocketSession);
58 }
59
60 @Override
61 public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
62 logger.debug(“链接关闭…” + closeStatus.toString());
63 users.remove(webSocketSession);
64 }
65
66 @Override
67 public boolean supportsPartialMessages() {
68 return false;
69 }
70
71 /**
72 * 给所有在线用户发送消息
73 *
74 * @param message
75 /
76 public void sendMessageToUsers(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 * @param userName
92 * @param message
93 */
94 public void sendMessageToUser(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 package com.lee.websocket.entity;
2
3 public class Person {
4
5 private int age;
6 private String name;
7 private String sex;
8
9 public int getAge() {
10 return age;
11 }
12 public void setAge(int age) {
13 this.age = age;
14 }
15 public String getName() {
16 return name;
17 }
18 public void setName(String name) {
19 this.name = name;
20 }
21 public String getSex() {
22 return sex;
23 }
24 public void setSex(String sex) {
25 this.sex = sex;
26 }
27
28 }
复制代码
chat.jsp
复制代码
1 <%@ page contentType=“text/html; charset=utf-8” language=“java” %>
2
3
4
5
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
复制代码