一、什么是WebSocket?
WebSocket是一个应用层协议,他依赖于http协议完成一次握手,建立服务端和和客户端之间的联系,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
二、WebSocket握手请求
Websocket 使用 ws 或 wss 的统一资源标志符,类似于 HTTPS,其中 wss 表示在 TLS 之上的 Websocket
1.1 客户端请求
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://localhost:8080
Sec-WebSocket-Key: sN9csFrP/n5NdDdcy2VJFS==
Sec-WebSocket-Version: 13
1.2 服务端响应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAJyBsdsf4hq5s=
Sec-WebSocket-Location: ws://localhost:8080
Connection 必须设置 Upgrade,表示客户端希望连接升级。
Upgrade 字段必须设置 Websocket,表示希望升级到 Websocket 协议。
Sec-WebSocket-Key 是随机的字符串,服务器端会用这些数据来构造出一个 SHA-1 的信息摘要。把 “Sec-WebSocket-Key” 加上一个特殊字符串 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算 SHA-1 摘要,之后进行 BASE-64 编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端。如此操作,可以尽量避免普通 HTTP 请求被误认为 Websocket 协议。
Sec-WebSocket-Version 表示支持的 Websocket 版本。RFC6455 要求使用的版本是 13,之前草案的版本均应当弃用。
Origin 字段是可选的,通常用来表示在浏览器中发起此 Websocket 连接所在的页面,类似于 Referer。但是,与 Referer 不同的是,Origin 只包含了协议和主机名称。
三、SpringBoot中使用WebSocket教程(参考springboot-websocket官网)
3.1 创建maven项目,引入依赖
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
</dependencies>
3.2 配置webscoket服务端
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");//配置目标地址前缀,broker代理,可以看作一个频道,订阅地址前缀
config.setApplicationDestinationPrefixes("/app");//配置应用的地址前缀
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket")//服务端站点,客户端链接服务端的端点
.setAllowedOrigins("*")//设置允许的的请求源
.withSockJS()//设置服务端websocket的类型
;
}
}
3.3 websocket接口的开发
websocket是基于http协议的升级版,和restful接口开发类似
@Controller
public class GreetingController {
//自定义消息类HelloMessage,Greeting
@MessageMapping("/hello")//api的地址
@SendTo("/topic/greetings")//发送到指定的频道
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
System.out.println(message);
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}
3.4 websocket的客户端
客户端可以是支持socketjs的各种编程语言
3.4.1 java客户端
//创建websocket处理器
public class MyWebSocketHandler extends StompSessionHandlerAdapter {
public MyWebSocketHandler() {
super();
}
@Override
public Type getPayloadType(StompHeaders headers) {
return Greeting.class;//设置订阅频道收到消息的类型
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {//此方法处理收到订阅频道的消息
Greeting greeting = (Greeting) payload;
System.out.println("来自服务器得到消息 : " + greeting.getContent());
}
@Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {//链接服务端后处理
super.afterConnected(session, connectedHeaders);
}
@Override
public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {//处理器异常的处理
exception.printStackTrace();
}
@Override
public void handleTransportError(StompSession session, Throwable exception) {//通信过程中出现错误的处理,注意上上一个方法是有区别的
super.handleTransportError(session, exception);
}
}
//创建java版的websocket客户端
public class MsgWebSocketClient {
public static void main(String[] args) throws ExecutionException, InterruptedException, JsonProcessingException {
Transport webSocketTransport = new WebSocketTransport(new StandardWebSocketClient());//websocket client类型
List<Transport> transports = Collections.singletonList(webSocketTransport);
SockJsClient sockJsClient = new SockJsClient(transports);//创建socketjs类型的webclient
sockJsClient.setMessageCodec(new Jackson2SockJsMessageCodec());//设置消息编码和解码类型,这里传递的json格式数据
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);//创建stomp websocket 客户端,对标准的websocket 又进一步封装
stompClient.setMessageConverter(new MappingJackson2MessageConverter());//设置通信消息使用jackson来转换,还有多种消息转换器提供配置
String url = "ws://127.0.0.1:8080/gs-guide-websocket";//websocket的服务端地址
TaskScheduler taskScheduler = new ConcurrentTaskScheduler();//设置任务调度方式为并发的方式,不会阻塞
stompClient.setTaskScheduler(taskScheduler);
StompSessionHandler stompFrameHandler = new MyWebSocketHandler();//自定义的消息处理器
ListenableFuture<StompSession> f = stompClient.connect(url, stompFrameHandler, "localhost", 8080);//链接服务器,
//f.cancel() 取消链接,如果任务已经完成、被取消、不能链接会返回false,取消失败 ,任务未开始或者任务正在执行会取消任务,返回true;
StompSession stompSession = f.get();//客户端和服务端通过StompSession完成通信
stompSession.setAutoReceipt(true);//设置自动接收消息
HelloMessage helloMessage = new HelloMessage();//编辑一个消息
helloMessage.setName("cc");
System.out.println("Subscribing to greeting topic using session " + stompSession);
stompSession.send("/app/hello",helloMessage);//调用api发送消息
stompSession.subscribe("/topic/greetings", stompFrameHandler);//订阅频道接收消息
}
}
3.4.2 js客户端
function connect() {
var socket = new SockJS('/gs-guide-websocket');//链接服务端
stompClient = Stomp.over(socket);//创建socket客户端
stompClient.connect({}, function (frame) {//frame是链接socket的框架socketJs
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {//订阅频道,将服务端的消息显示到网页
showGreeting(JSON.parse(greeting.body).content);
});
});
}
function sendName() {//同过websocket的api向服务端发送消息
stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}
3.5 程序结果
3.5.1html中js客户端发送消息与接收消息:
3.5.2 java 客户端结果
一个频道可被多个客户端订阅,使用websocket实现聊天,替换常规前端轮询,以及实时股票等方面又很好的应用
四、资料
1、socketjs:https://github.com/sockjs/sockjs-client
2、spring-websoket:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web.html#websocket-fallback-sockjs-client
3、教程源码:https://github.com/NapWells/java_framework_learn/tree/master/websocketDemo
如有问题,欢迎指正,qq:425154211