Websocket学习
学习连接
协议过程简介
1、客户端:申请协议升级
⾸先,客户端发起协议升级请求。可以看到,采⽤的是标准的HTTP报⽂格式,且只⽀持GET⽅法。
GET / HTTP/1.1
Host: localhost:8080
Origin: 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
引⼊依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="/POM/4.0.0"
xsi="/2001/XMLSchema-instance"
schemaLocation="/POM/4.0.0 /xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId&hua</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>
测试
/*
1. @ServerEndpoint⽤于标记被修饰的类是⼀个 websocket服务器的⼀个端点,且被标注的类必须有⼀个public的⽆参构造⽅法
2. @ServerEndpoint可以指定端点发布的路径, 和其它重要的属性,如encoders编码器、decoders解码器
3. 端点发布的路径可以带路径变量,并且该路径变量名可以在@OnOpen和@OnClose标注的⽅法的⽅法参数中使⽤
如:发布路径是@ServerEndpoint(value="/websocket/{userId}"),则在@OnOpen修饰的⽅法中可以使⽤@PathParam("userId"),
4. @ServerEndpoint允许指定⾃定义配置器 ServerEndpointConfig.Configurator
*/
@ServerEndpoint(value="/websocket/{userId}")// 必须以斜杠开头,可以写多个路径变量
public class MyWebSocketEndpoint {
private static final AtomicInteger counter =new AtomicInteger(0);// 客户端计数器
private static final Set<MyWebSocketEndpoint> connections =new CopyOnWriteArraySet<MyWebSocketEndpoint>();// 客户端websocket连接集合private Session session = null;// WebSocket会话对象
private Integer number =0;// 客户端编号
public MyWebSocketEndpoint(){
number = counter.incrementAndGet();
}
/**
* 客户端建⽴websocket连接
* @param session
*/
@OnOpen
@OnOpen
public void start(Session session,@PathParam("userId") String userId){
System.out.println("on open");
this.session = session;
connections.add(this);
try{
e.printStackTrace();
}
}
/**
* 客户端断开websocket连接
*/
@OnClose
public void close(@PathParam("userId") String userId){
System.out.println("session close");
try{
this.session.close();
}catch(IOException e){
e.printStackTrace();
}finally{
}
}
/**
* 接收客户端发送的消息
* @param message
*/
@OnMessage
public void message(String message,@PathParam("userId") String userId){
System.out.println("message: "+ message);
for(MyWebSocketEndpoint client : connections){
synchronized(client){
try{
BasicRemote().sendText(message);
}catch(IOException e){
e.printStackTrace();
}
}
}
}
@OnError
public void error(Throwable t){
System.out.println("client: error"+ Message());
}
public static Set<MyWebSocketEndpoint>getConnections(){
return connections;
}
public Session getSession(){
return session;
}
}
tomcat下使⽤websocket
引⼊依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="/POM/4.0.0"
xsi="/2001/XMLSchema-instance"
schemaLocation="/POM/4.0.0 /xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId&hua</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>
<!-- javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
</dependency>
<!-- mvnrepository/artifact/javax.websocket/javax.websocket-api --> <dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>0520carrent</finalName>
<plugins>
<!-- 配置tomcat的运⾏插件 -->
<plugin>
<groupId>at.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 配置端⼝ -->websocket和socket
<port>8080</port>
<!-- 配置urlencoding -->
<uriEncoding>UTF-8</uriEncoding>
<path>/</path>
</configuration>
</plugin>
<!-- 配置jdk的编译版本 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<!-- 指定source和target的版本 -->
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="/xml/ns/javaee"
xsi="/2001/XMLSchema-instance"
schemaLocation="/xml/ns/javaee
/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
</web-app>
MyServlet

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。