一步步教你如何使用Java实现WebSocket
作者:今昔在 发布时间:2023-06-07 04:18:20
一、WebSocket简介
WebSocket协议通过在客户端和服务端之间提供全双工通信来进行Web和服务器的交互功能。
在WebSocket应用程序中,服务器发布WebSocket端点,客户端使用url连接到服务器。建立连接后,服务器和客户端就可以互相发送消息。客户端通常连接到一台服务器,服务器接受多个客户端的连接。
1.1 WebSocket协议
WebSocket协议有两个部分:握手和传输。客户端通过向服务端URL发送握手请求来建立连接。握手与现有的基于HTTP的基础结构相兼容。Web服务器将其解释为升级版的HTTP连接请求。
一个客户端建立连接的握手请求:
GET /path/to/websocket/endpoint HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
Origin: http://localhost
Sec-WebSocket-Version: 13
一个服务端响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
从上面的请求和响应中可以看出来,一个WebSocket连接的建立,需要客户端和服务端维护一个Key来作为该连接的连接凭证。
客户端向服务端发送WebSocketKey,服务器根据WebSocketKey生成WebSocketAccept返回给客户端,客户端对WebSocketKey的值再进行相同的操作,如果与服务器返回的Accept的值相匹配,就表示握手成功。握手之后客户端与服务端就互相发送消息。
1.2 WebSocket支持的消息类型
WebSocket支持文本消息(UTF-8编码)和二进制消息。WebSocket的控制消息由Close、Ping、Pong组成。ping和pong也有可能包含应用程序信息。
webSocket端点具有如下的URI形式表示:
ws://host:port/path?query
wss://host:port/path?query
ws代表未加密的连接,默认端口是80
wss代表加密的连接,默认端口是443
path:表示服务器内端点的位置
query:路径参数信息
二、创建一个WebSocket程序
创建和部署WebSocket端点的过程如下:
1、创建一个端点类
2、实现端点的生命周期方法
3、将业务逻辑添加到端点
4、将端点部署到Web应用程序中
**注意:**与Servlet相反,WebSocket端点类会被实例化多次,容器针对与其部署的URI的每个连接都创建一个实例。每个实例都与一个连接相关联。因为在任何时间都只有一个线程执行端点实例的代码,所以有助于保持每个连接的用户状态简化开发。
2.1 生命周期方法
EndPoint类中定义了三个生命周期方法:onOpen、onClose、onError
使用注解方式创建服务类
@ServerEndpoint("/echo")
public class EchoEndpoint {
@OnMessage
public void onMessage(Session session, String msg) {
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) { ... }
}
}
2.2 业务逻辑方法
@ServerEndpoint("/receive")
public class ReceiveEndpoint {
@OnMessage
public void textMessage(Session session, String msg) {
System.out.println("Text message: " + msg);
}
@OnMessage
public void binaryMessage(Session session, ByteBuffer msg) {
System.out.println("Binary message: " + msg.toString());
}
@OnMessage
public void pongMessage(Session session, PongMessage msg) {
System.out.println("Pong message: " +
msg.getApplicationData().toString());
}
}
注意:一个服务类最多可以有三个OnMessage注解,每个消息类型分别使用一种方法:Text、Binary、Pong
2.3 维护客户状态
有时在程序中我们可能需要在连接中维护一些用户参数,WebSocket也提供了这样的服务
Session.getUserProperties获取用户参数信息
如果要存储所有连接的客户端共有的信息,可以使用静态变量,但是需要用户保证对数据的线程安全访问。
@ServerEndpoint("/delayedecho")
public class DelayedEchoEndpoint {
@OnOpen
public void open(Session session) {
session.getUserProperties().put("previousMsg", " ");
}
@OnMessage
public void message(Session session, String msg) {
String prev = (String) session.getUserProperties()
.get("previousMsg");
session.getUserProperties().put("previousMsg", msg);
try {
session.getBasicRemote().sendText(prev);
} catch (IOException e) { ... }
}
}
2.4 数据格式的编码与解码
由于客户端和服务端交互可能涉及数据格式的转换,所以提供了Decoder和Encoder的方式解决。
同时由于WebSocket的@Message注解只能有一个用来传输Text信息或Binary信息,所以要进行最常用的Json->entity转换解析就需要该方法
Encoder
Encoder.Text 用于文本消息Encoder.Binary 用于二进制消息
使用方法:
1、创建编解码类
public class MessageATextEncoder implements Encoder.Text<MessageA> {
@Override
public void init(EndpointConfig ec) { }
@Override
public void destroy() { }
@Override
public String encode(MessageA msgA) throws EncodeException {
// Access msgA's properties and convert to JSON text...
return msgAJsonString;
}
}
2、在端点类注解中添加该Encoder
@ServerEndpoint(
value = "/myendpoint",
encoders = { MessageATextEncoder.class, MessageBTextEncoder.class }
)
public class EncEndpoint { ... }
3、这时候就可以发送MessageA和MessageB两种类型的Text数据
MessageA msgA = new MessageA(...);
MessageB msgB = new MessageB(...);
session.getBasicRemote.sendObject(msgA);
session.getBasicRemote.sendObject(msgB);
注意:webSocket会自动寻找使用哪种编码器,所以发送数据统一使用sendObject即可
Decoder
实现Decoder以将WebSocket消息转换为Java对象
Decoder.Text 用于文本消息
Decoder.Binary 用于二进制消息
使用方法
与Encoder类似
注意:与Encoder不同,Decoder最多可以指定一个Binary和一个Text类型的Decoder,如果有两种以上的Java类型作为文本消息进行发送和接收需要进行定义处理。可以使多个消息继承一个公共的消息父类
1、编写Decoder类,对收到消息类型的不同进行不同的解码
public class MessageTextDecoder implements Decoder.Text<Message> {
@Override
public void init(EndpointConfig ec) { }
@Override
public void destroy() { }
@Override
public Message decode(String string) throws DecodeException {
// Read message...
if ( /* message is an A message */ )
return new MessageA(...);
else if ( /* message is a B message */ )
return new MessageB(...);
}
@Override
public boolean willDecode(String string) {
// Determine if the message can be converted into either a
// MessageA object or a MessageB object...
return canDecode;
}
}
2、在端点类中添加decoders={MessageDecoder.class}
@ServerEndpoint(
value = "/myendpoint",
encoders = { MessageATextEncoder.class, MessageBTextEncoder.class },
decoders = { MessageTextDecoder.class }
)
public class EncDecEndpoint { ... }
3、在@OnMessage方法中使用
@OnMessage
public void message(Session session, Message msg) {
if (msg instanceof MessageA) {
// We received a MessageA object...
} else if (msg instanceof MessageB) {
// We received a MessageB object...
}
}
来源:https://blog.csdn.net/weixin_45336946/article/details/129594828
猜你喜欢
- 第一次接触到随机数还是在c语言里面 使用的是 rand(); 但是重新执行一次的时候会发现,诶,居然和上一次执行的结果是一样的,因为没有初始
- Java在1.5开始引入了注解,目前流行的框架都在用注解,可想而知注解的强大之处。以下通过自定义注解来深入了解java注解。一、创建自定义注
- 一、内部类介绍1.定义:一个类内部又嵌套了一个类,被嵌套的类就是内部类(inner class),嵌套其他类的称为外部类(outer cla
- 创建类的对象 = 类的实例化 = 实例化类类和对象的使用(面向对象思想落地的实现):1.创建类,设计类的成员2.创建类的对象3.通过“对象.
- 本文实例为大家分享了java使用字符画一个海绵宝宝的具体代码,供大家参考,具体内容如下用字符画一个海绵宝宝用" &ldqu
- 图形验证码属于老生常谈了,具体细节这里就不说了。生成图形验证码的办法非常多,今天讲解一种通过Kaptcha组件快速生成图形验证码的方法。Ka
- 本文实例讲述了java实现变更文件查询的方法。分享给大家供大家参考。具体如下:自己经常发布包时需要查找那些文件时上次发包后更新的数据文件,所
- mybatis-plus的代码生成器会在实体类中生成数据库所有字段,我们去用mapper接口查询时,会返回数据库所有的字段。但有些字段不是我
- 1 什么是class对象类是程序的一部分,每个类都有一个class对象。换言之,每当编写并且编译了一个新类,就会产生一个class对象(更恰
- 前言现在开发大部分都是服务化或者微服务,数据交换都是跨服务的,这里记录java调取其他接口的方法,下面话不多说了,来一起看看详细的介绍吧。j
- JPanel是面板组件,非顶层容器,一个界面只有可以有一个JFrame窗体组件,但可以有多个Jpanel面板,而JPanel上也可以使用Fl
- 引言MyBatis-Plus | 最优雅最简洁地完成数据库操作是对MyBatis-Plus的功能进行简单介绍,虽然是介绍,也让我们领略到他的
- 面向过程和面向对象的区别面向过程:当事件比较简单的时候,利用面向过程,注重的是事件的具体步骤和过程,注重的是过程中的具体行为,以函数为最小单
- 一.相关知识:Java多线程程序设计到的知识:(一)对同一个数量进行操作(二)对同一个对象进行操作(三)回调方法使用(四)线程同步,死锁问题
- 1、SpringBoot配置文件1.1 优先级关于SpringBoot配置文件可以是properties或者是yaml格式的文件,但是在Sp
- 前言现在是移动端产品疯狂的年代,随之,移动端支付也是热门小技能,最近本公司在做一个移动端,要接入微信支付和支付宝支付, * 惯,功能做完之后做
- 今天接触了一下ToStringBuilder类,这里做一个备忘。ToStringBuilder在包 common-lang 中,主要用于一个
- 引入pom<?xml version="1.0" encoding="UTF-8"?>&
- 一、tomcat内存设置问题 收藏 在使用Java程序从数据库中查询大量的数据或是应用服务器(如tomcat、jboss,weblogic)
- 一.背景项目中有个需求大体意思是,上传一个word模板,根据word模板合成word文件,再将word文件转为pdf。二.方案选择1.Spi