学习连接
WebSocket协议:5分钟从入门到精通 https://www.cnblogs.com/chyingp/p/websocket-deep-in.html
WebSocket协议入门介绍 https://www.cnblogs.com/nuccch/p/10947256.html
java的websocket客户端和服务端库 https://github.com/TooTallNate/Java-WebSocket
对于Tomcat中的websocket的了解 https://blog.csdn.net/zjgyjd/article/details/99686939
基于Tomcat的webSocket的使用方式和源码分析https://blog.csdn.net/coder_what/article/details/109032940
协议过程简介
(全部摘自上面的链接,学到就是自己的!!!)
1、客户端:申请协议升级
首先,客户端发起协议升级请求。可以看到,采用的是标准的HTTP报文格式,且只支持GET方法。
GET / HTTP/1.1
Host: localhost:8080
Origin: http://127.0.0.1:3000
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==
重点请求首部意义如下:
Connection: Upgrade:表示要升级协议
Upgrade: websocket:表示要升级到websocket协议。
Sec-WebSocket-Version: 13:表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。
Sec-WebSocket-Key:与后面服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。
注意,上面请求省略了部分非重点请求首部。由于是标准的HTTP请求,类似Host、Origin、Cookie等请求首部会照常发送。在握手阶段,可以通过相关请求首部进行 安全限制、权限校验等。
2、服务端:响应协议升级
服务端返回内容如下,状态代码101表示协议切换。到此完成协议升级,后续的数据交互都按照新的协议来。
HTTP/1.1 101 Switching Protocols
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=
备注:每个header都以\r\n结尾,并且最后一行加上一个额外的空行\r\n。此外,服务端回应的HTTP状态码只能在握手阶段使用。过了握手阶段后,就只能采用特定的错误码。
3、数据帧交互
客户端、服务端数据的交换,离不开数据帧格式的定义。因此,在实际讲解数据交换之前,我们先来看下WebSocket的数据帧格式。
WebSocket客户端、服务端通信的最小单位是帧(frame),由1个或多个帧组成一条完整的消息(message)。
发送端:将消息切割成多个帧,并发送给服务端; 接收端:接收消息帧,并将关联的帧重新组装成完整的消息;
4、连接保持+心跳
WebSocket为了保持客户端、服务端的实时双向通信,需要确保客户端、服务端之间的TCP通道保持连接没有断开。然而,对于长时间没有数据往来的连接,如果依旧长时间保持着,可能会浪费包括的连接资源。
但不排除有些场景,客户端、服务端虽然长时间没有数据往来,但仍需要保持连接。这个时候,可以采用心跳来实现。
发送方->接收方:ping 接收方->发送方:pong ping、pong的操作,对应的是WebSocket的两个控制帧,opcode分别是0x9、0xA。
举例,WebSocket服务端向客户端发送ping,只需要如下代码(采用ws模块)
ws.ping('', false, true);
最简单的demo
现在开发基本上都使用spring,而spring又把很多技术都包装起来,提供了非常方便的Api让使用技术变得更加的容易,但是停下来想想,很多我们本应了解的东西也被封装了进去,以至于看不到具体实现或者很自然的忽略了本应该了解的东西,先抛开spring对websocket的封装,了解下最基础的websocket的使用,再去看spring的封装,这样比较好。
可以到这个项目的github处,里面有不少的使用示例,这里只用最简单的示例
引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zzhua</groupId>
<artifactId>demo-base-websocket</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
测试
@ServerEndpoint(value="/websocket/{userId}")
public class MyWebSocketEndpoint {
private static final AtomicInteger counter = new AtomicInteger(0);
private static final Set<MyWebSocketEndpoint> connections = new CopyOnWriteArraySet<MyWebSocketEndpoint>();
private Session session = null;
private Integer number = 0;
public MyWebSocketEndpoint() {
number = counter.incrementAndGet();
}
@OnOpen
public void start(Session session, @PathParam("userId") String userId) {
System.out.println("on open");
this.session = session;
connections.add(this);
try {
session.getBasicRemote().sendText(new StringBuffer().append("Hello: ").append(number).toString());
} catch (IOException e) {
e.printStackTrace();
}
}
@OnClose
public void close(@PathParam("userId") String userId) {
System.out.println("session close");
try {
this.session.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
connections.remove(this);
}
}
@OnMessage
public void message(String message,@PathParam("userId") String userId) {
System.out.println("message: "+ message);
for(MyWebSocketEndpoint client : connections) {
synchronized (client) {
try {
client.session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@OnError
public void error(Throwable t) {
System.out.println("client: error"+ number+t.getMessage());
}
public static Set<MyWebSocketEndpoint> getConnections() {
return connections;
}
public Session getSession() {
return session;
}
}
tomcat下使用websocket
引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zzhua</groupId>
<artifactId>demo-tomcat-websocket</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<jsp.version>2.1</jsp.version>
<servlet.version>3.1.0</servlet.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>0520carrent</finalName>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<uriEncoding>UTF-8</uriEncoding>
<path>/</path>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
</web-app>
MyServlet
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
for (MyWebSocketEndpoint myWebSocketEndpoint : MyWebSocketEndpoint.getConnections()) {
myWebSocketEndpoint.getSession().getBasicRemote().sendText("halo");
}
resp.getWriter().write("hello world");
}
}
MyWebSocketEndpoint
package com.zzhua.ws;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
@ServerEndpoint(value="/websocket")
public class MyWebSocketEndpoint {
private static final AtomicInteger counter = new AtomicInteger(0);
private static final Set<MyWebSocketEndpoint> connections = new CopyOnWriteArraySet<MyWebSocketEndpoint>();
private Session session = null;
private Integer number = 0;
public MyWebSocketEndpoint() {
number = counter.incrementAndGet();
}
@OnOpen
public void start(Session session) {
System.out.println("on open");
this.session = session;
connections.add(this);
try {
session.getBasicRemote().sendText(new StringBuffer().append("Hello: ").append(number).toString());
} catch (IOException e) {
e.printStackTrace();
}
}
@OnClose
public void close() {
System.out.println("session close");
try {
this.session.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
connections.remove(this);
}
}
@OnMessage
public void message(String message) {
System.out.println("message: "+ message);
for(MyWebSocketEndpoint client : connections) {
synchronized (client) {
try {
client.session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@OnError
public void error(Throwable t) {
System.out.println("client: error"+ number+t.getMessage());
}
public static Set<MyWebSocketEndpoint> getConnections() {
return connections;
}
public Session getSession() {
return session;
}
}
html的websocket客户端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
请输入: <input id="content" type="text"/>
<button onclick="sendMsg()">发送</button>
<button onclick="closeWebsocket()">关闭</button>
<ul id="msgList">
</ul>
</body>
<script>
var socket = new WebSocket('ws://localhost:8080/websocket/user001');
socket.onopen = function (event) {
console.log("建立连接成功")
}
function sendMsg() {
var content = document.querySelector('#content');
socket.send(content.value)
}
socket.onmessage = function (ev) {
console.log("收到服务器消息: " + JSON.stringify(ev))
var ul = document.querySelector('#msgList');
var li = document.createElement('li');
li.innerText = ev.data
ul.appendChild(li)
}
function closeWebsocket() {
socket.close()
}
socket.onclose = function (event) {
console.log("连接关闭")
}
</script>
</html>
|