WebSocket |学习笔记

本文涉及的产品
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 快速学习 WebSocket

开发者学堂课程【高校精品课-上海交通大学-企业级应用体系架构:WebSocket 】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/75/detail/15830


WebSocket

 

内容介绍

一、WebSocket

、Introduction to WebSocket

、Creating WebSocket Applications

、Creating and Deploying a WebSocket Endpoint

 

跑 zookeeper 用的是 Kafka中quickstart 写的启动服务的方法进行启动跑 zookeeper 和跑 kafka会有两个窗口一个跑 kafka一个跑 zookeeper写发送者和接受者的代码逻辑很简单发送者连接到 localhost:9092,kafka服务器上所以里面会有服务器的位置下面可以看到kafkaproducer生产表stringstring 文件里面用字符串描述直接发出100条消息发到kafka的topic test中,Integer. toString(i), value: "Message‘’+ Integer, toString i循环变量结束接收者也要连接到服务器上

while (true) {

Consume rRecords<string, String> records =consumer. poll(Duration. ofMillis(100));不断的拿消息

for (ConsumerRecord<String, String> record : records )

offset 是什么输入输出分别是什么消费者先跑起来死循环在注册等待发送者发消息后退出接收者在接收消息,offset 101开始是因为已经执行过一遍消息不会马上删除而是存活一段时间在这段时间里面没有死掉再发消息就是从101开始到200,收到的消息内容是从099,这就是 kafka 的例子利用 kafka 实现当消息通信想用重量级的里面包含太多不需要用的功能就考虑用 kafka 实现代码中 service 层收发消息即可相比之下从编程代码可以看到做一定的封装代码比原生的 jms 代码写起来要少一些

 

一、WebSocket

有哪些通信的方式在系统里面如何选择

WebSocket is an application protocol that provides full-duplex

communications between two peers over the TCP protocol.想实现全双功的通信

1、In the traditional request-response model used in HTTP 客户端发请求服务, the client requests resources and the server provides responses.适当给响应唤醒时候有响应很快速的响应服务器端能不能主动把消息推出去service处理客户端请求时接收到一个消息把它放到一个 q 里面后端进行处理处理完之后监听另外一个 q处理结果处理完把结果放到另外一个 topic 里面topic 会被监听到发生在服务器端如何把最终消息推给客户端在http协议里面必须要先有请求才有响应现在客户端没有发请求需要双功式不但客户端发请求服务端也能主动推消息双向通信像 jms 中的点对点一样现在的客户端和服务器端的模型有点模糊双方之间必须都能通起来。WebSocket 是通过 http 协议走到它的应用层面在 tcp 的协议上和 http 有差异应用层不是最新的一次协议所以它是新写的所以 ws://,http://,有主动推送消息的方式可以看到系统变得灵活服务系统主动推

2、The exchange is always initiated by the client; the server cannot send any data without the client requesting it first.

3、The WebSocket protocol provides a full-duplex communication channel between the client and the server.

4、Combined with other client technologies, such as JavaScript and HTML5, WebSocket enables web applications to deliver a richer user experience.

 

二、Introduction to WebSocket

1、In a WebSocket application, the server publishes a

WebSocket endpoint and the client uses the endpoint's URI to

connect to the server.WebSocket 中服务器需要发布 endpoint,相当于在 http 中看到 service 的作用一样要能接收客户端浏览器发过来的连接请求要想收到主动推的消息必须要进行注册一旦注册就知道在监听消息未来就可以推消息利用 uri 连接服务器服务器发布一个 endpoint实际上有 uri客户端对 uri 要监听消息服务器端收到请求以后要把消息推过去

The WebSocket protocol is symmetrical after the connection has been established:

(1)The client and the server can send messages to each other at any time while

the connection is open, and they can close the connection at any time.

(2)Clients usually connect only to one server, and servers accept connections from multiple clients.

2、The WebSocket protocol has two parts:

handshake and data transfer.

握手和传数据握手指的是大家如何建立连接一旦建立连接后,WebSocket是对称的作用建立连接之后客户端和服务器端都能用连接进行发送或者接收消息大家在看待这个连接并不想在http中只能是客户端发请求给服务器端响应服务器端发请求给客户端进行接收所以是分工的

3、The client initiates the handshake by sending a request to a

WebSocket endpoint using its URI.建立连接

The handshake is compatible with existing HTTP-based infrastructure:

web servers interpret it as an HTTP connection upgrade request.

An example handshake from a client looks like this:

GET /path/ to/websocket/ endpoint HTTP/1.1客户端通过 get 方法对 endpoint uri 发送

Host: localhost 机器主机的域名唯一的标识

Upgrade: websocket 明发送的属性

Connection: Upgrade 升级版链接

Sec -WebSocket- Key: xqBt3ImNzJbYqRINxEFlkg==发过去服务器端收到包收到后服务器端会发一个响应给客户端

Origin: http://localhost

Sec -WebSocket-Version: 13

Sec -WebSocket - Accept:K7DJLdLooIwIG/ MOpvWFB3y3FE8=

4、The server applies a known operation to

the value of the Sec -WebSocket -Key header to generate the value of the Sec-WebSocket- Accept header.

服务器端拿到发过来的key后双方都解的操作比如约定的操作浏览器默认的操作

5、The client applies the same operation to 服务器端发回给客户端客户端用同样的操作解 Sec-WebSocket- Key,用 key 解如果解开就是 key 算出来双方建立一次连接相当于鉴别身份安全性比较低,只是确认一下能否握手一旦解开,对 key 产生相同的操作,看 accept 是否一致如果一致相匹配互相之间建立的一个连接。

the value of the Sec-WebSocket- Key header, and the connection is  established successfully if the result matches the value received from the server.

6、The client and the server can send messages to each other after a successful handshake.

7、WebSocket endpoints are represented by URIs that have the

following form:

Ws://host: port/path?query比如握手利用协议发不是 http 也不是 https其次跟它们很像但是实际上不是 http 协议看浏览器和应用服务器是否支持 websocket虽然支持的越来越多不支持的占少数区别和http和https 一样加密

WSS:/ /host: port/ path?query

The WS scheme represents an unencrypted WebSocket connection, and the WSS scheme represents an encrypted connection.

The port component is optional;

the default port number is 80 for unencrypted connections and 443 for encrypted connections.默认端口

The path component indicates the location of an endpoint within a server.

The query component is optional.

建立好连接之后,刷数据所有的东西都要经过连接

 

三、Creating WebSocket Applications

1、The Java API for WebSocket consists of the following packages:

The javax . websocket . server package contains annotations, classes, and interfaces to create and configure server endpoints.相应的包进行导入

The javax . websocket package contains annotations, classes, interfaces,and exceptions that are common to client and server endpoints.

2、WebSocket endpoints are instances of the javax .websocket . Endpoint class.服务器端

The Java API for WebSocket enables you to create two kinds of endpoints:

programmatic endpoints and annotated endpoints.两种方法

To create a programmatic endpoint, you extend the Endpoint class and override its lifecycle methods.

To create an annotated endpoint, you decorate a Java class and some of its methods with the annotations provided by the packages above.

After you have created an endpoint, you deploy it to an specific URI in the application so remote clients can connect to it.

 

四、Creating and Deploying a WebSocket Endpoint

1、The process for creating and deploying a WebSocket endpoint:

Create an endpoint class.

Implement the lifecycle methods of the endpoint.

Add your business logic to the endpoint.

Deploy the endpoint inside a web application.

The process is slightlý different for programmatic endpoints

and annotated endpoints编程方式写

2、Programmatic Endpoints

EchoEndpoint子类里面有若干个方法写

public class EchoEndpoint extends Endpoint {

@Override

public void onOpen(final Session session,EndpointConfig config)有客户端浏览器发送一个请求过来,连接握手要干什么

支持 websocket 外部浏览器包装好的东西包含用户的信息在什么位置等待uri 会配合什么要处理的就是如何对待 session

{

session . addMessageHandler(双功的相当于发个消息消息处理器不能是同步处理是异步处理当客户端有事情过来的时候就要去处理

new MessageHandler . Whole<String>() {

@Override

public void onMessage(String msg) {

try [

session. getBasicRemote( ). sendText(msg);

}catch(IOException e){...}

}

});

}

针对用户加一个消息处理器一旦有消息过来就要得到远程客户端

可以理解 为session. getBasicRemote 就是远程的客户端session 就是把它包装起来的样子对远程客户发消息逻辑是业务逻辑,如何写都可以含义只要有人连接,就在上面注册一个监听器,监听器未来在session上一旦消息过来原封不动的把消息转发出去用户的消息再发送出去代码量比较多,而且不是很明确编程的模式不是特别在意。

To deploy this programmatic endpoint, use the following code

in your Java EE application:

复解的方式做,还需要部署一下可以看到类的静态方法类的一个实例,把它部署到 echo 位置上。

ServerEndpointConfig . Builder. create( EchoEndpoint . class,

"/echo"). build();

When you deploy your application, the endpoint is available

atws:/ /<host>: <port>/ <application>/ echo;

for example, ws://localhost: 8080/ echoapp/ echo.未来是对应的 url协议主机名端口应用配的路径未来客户端访问对 ws 进行操作

3、Annotated Endpoints

EchoEndpoint

@ServerEndpoint(" /echo" )标注

public class EchoEndpoint {扩展类或者实现接口

@OnMessage

public void onMessage(Session session, String msg) {发过来之后就会有session信息

try {

session. getBasicRemote(). sendText(msg);谁发消息都原封不动的传回去

} catch (IOException e){  ...}

}

}

代码看起来简单的多并且非常的直观

Annotation

Event

Example

OnOpen

Connection opened.连接被打开当有人发消息时用握手的方式建立连接

@OnOpen public void open(Session session,EndpointConfig conf){}名字可以随便写

OnMessage

Message received.接收消息之后用什么方法处理

@OnMessage

public void message (Session session, String msg) {}

OnError

Connection error.一旦调用出错调用哪个处理方法

@OnError

public void error(Session session, Throwable error){}

OnClose

Connection closed.把连接关闭掉时应该干什么

@OnClose

public void close(Session session,

CloseReason reason)「」

4、Sending Messages to All Peers Connected to an Endpoint

Send messages

@ServerEndpoint("/echoall")

public class EchoAllEndpoint {从名字可以看出把消息发送给所有的用户未来不管是谁发消息都要遍历当前的实例维护所有的 session,就是获取当前所有保持连接的 session比如设计股票操作的应用很多人都连着针对里面每一个人for 循环如果是开着的就把消息发出去之后不管谁发消息都把消息发回给保持连接的封装就是向所有人保持回复

@OnMessage

public void onMessage(Session session, String msg)

{

try {

for (Session sess : session. getopenSessions()) {引入一个概念通过方法的调用可以跟 endpoint 连接的所有的客户端比如在线聊天室等方法应用程序需要应用方法让聊天室里的人都知道消息用的是 for 循环做处理

if (sess. is0pen())如果逻辑写的比较复杂保持开放的人很多那就会遇到执行时间非常长的问题所以逻辑不易复杂简单的进行处理即可如果过长大家频繁的发消息逻辑执行非常长会处理不过来

sess. getBasicRemote() . sendText(msg);

}

} catch ( IOException e) { ... }

}

5、Receive messages 接收消息

@ServerEndpoint(" /receive")

public class ReceiveEndpoint (

@OnMessage发过来的message不一定是字符串文本消息还有可能是字节数据甚至还有可能是对象实际上可以处理各种各样不同类型的消息所以 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());

}

}

6、An example

(1)ETFEndPoint.java

@ServerEndpoint(" /dukeetf")

public class ETFEndpoint {

private static final Logger logger =

Logger . getLogger("ETFEndpoint");

static Queue<Session> queue = new

ConcurrentLinkedQueue<>();定义未来是并发链接的 queue,当有大量用户创建连接时可以同时塞进queuequeue 里面存放的是很多个 session当有大量 session 建立连接都希望把自己注册上来时可以并发的写入queue

public static void send(double price, int volume) {定义方法没有任何注解是自己写的double类型的价格int 类型数量会组一个字符串把字符串当成消息

String msg = String. format("%.2f, %d", price, volume);

try {

for (Session session : queue) {对于 queue 中所有的 session

session. getBasicRemote( ). sendText(msg);都要获取它们的客户端发送文本消息出去

logger .log(Level. INFO, "Sent:

(0", msg);

}

} catch (IOException e) {

logger . log(Level. INFO, e. toString());

}

只要 send 给数据就把数据推给所有的客户端

(2)ETFEndPoint.java

@OnOpen

public void openConnection(Session session) {

queue . add(session);

logger .log(Level. INFO, "Connection opened.");

}

@OnClose

public void closedConnection(Session session) {

queue . remove(session) ;

logger. log(Level. INFO, "Connection closed.");

}

@OnError

public void error(Session session, Throwable t) {

queue. remove( session);

logger. log(Level. INFO, t. toString());

logger .log(Level. INFO, "Connection error.");

}

}

当有人连接时把 session 添加到队列中关闭时再从队列中把 session 移除掉session error把它移除掉没有特别的逻辑用日志说明一下

(3)ETFListener.java

@WebListener 定义监听器

public class

ETFListener implements ServletContextListener {

private Timer timer = null ;

public void contextInitialized( ServletContextEvent event ) {

timer = new Timer(true);

event . getServletContext( ).log("The Timer is started");

timer . schedule( new ReportBean( event .

getServletContext()) ,

0, 1000);

event. getServletContext().log("The task is added");

}

}

定义一个定时器定时器每一秒钟动作一次创建一个 ReportBean 实例

(4)ReportBean.java

public class ReportBean extends TimerTask {定时器任务跟定时关联起来每一秒都执行一个

private ServletContext context = null ;

private Random random = new Random( ) ;

private double price = 100.0;

private int volume = 300000;

public ReportBean( ServletContext context )

{ this. context = context; }

public void run() {只要时间到了都会生成价格生成 volume调用方法发出去

context.log("Task started");

price += 1. 0*(random . nextInt(100)-50)/100.0;

volume += random. nextInt(5000) - 2500;

ETFEndpoint. send(price, volume) ;

context.log( "Task ended");

}

}

逻辑每隔一秒用随机事务生成价格数量用websocket推出去

(5)Index.html客户端是一个脚本支持websocket浏览器里面内置一个wsocket对在创建的时候告诉对着谁在本地跑应用

<html>

<head>

<title>Duke's WebSocket ETF</title>

<script type="text/javascript">

var wsocket ;

function connect() {

wsocket = new WebSocket

("ws: //localhost : 8080/WebSocketSamples/ dukeetf");主机名业务名endpoint建立连接发出请求之后相当于 onopen 方法被调用客户端添加到 queue

wsocket. onmessage = onMessage;一旦有消息回来调用 onmessage 方法

}

function onMessage(evt) {

var arraypv = evt.data.split(",");

document . getElementById(" price") . innerHTML = arraypv[0];

document . getElementById( "volume") . innerHTML = arraypv[1];

}

window . addEventListener("load", connect, false);

</script>

</head>

发过来的是价格和 volume价格显示两位小数中间加逗号数量显示成十进制数。逗号隔开放到数组里前面价格,后面是数量拿价格掉替换页面里面的 arraypv数量替换页面的 volumewindow 把监听器进行注册在整个页面加载时跑 connect每次有消息过来就进行替换每次有消息都会进行替换

Index.html

<body>

<h1>Duke's WebSocket ETF</h1>

<table>

<tr>

<td width= "100">Ticker</td>

<td align="center">Price</td>

<td

id="price '

style="font-s ize :24pt ; font-weight :bold;">--.--</td>内容进行替换

</tr>

<tr>

<td style="font-size:18pt ; font-weight:bold; "

width="100">DKEJ</td>

<td align="center" >Volume</td>

<td id="volume" align="right">--</td>内容进行替换

</tr>

</table>

</ body>

< /html>

跑服务器端可以看到一秒接收一次两个浏览器同时在变说明两个浏览器同时注册到 websocket服务器上websocket 推消息同时收到现在浏览器上什么都没做,浏览器里面的页面也没有发消息出去数字的变化完全是服务器端推出来的这是 websocket 的作用。

 image.png

rmi 做同步调用jms 做异步调用都发生在后台java 代码之间如果前台和后台交互使用 ajax现在有websocket系统里都会有吗

rmi 是在后台 server 端有两个进程它俩之间的通信必须要用 rmi因为它俩之间没有办法直接进行交互比如后台做集群直接让集群上两个进程通信zookeeper 和 kafka 之间通信也需要找 rmi虽然 rmi 是后台的多个之间进程的通信必须要这样访问jms本质上 也是不同进程之间的通信只不过中间有 rmi 服务器的中介解决的是rmi的直接通信的饱和度太高再进行调用之后一旦出错任务就失败了没有重新尝试的机会再就是要继续堵塞等待所以希望以异步的方式进行通信所以有 jms这些发生在服务器里面服务器和客户端之间price 功能就是不断接收后台发过来数据可以用 ajax 做ajax 进行周期性的轮询到后面去问有没有新数据有就给极大的缺点就是造成后台的压力很大,也许数据很长时间变一次,去抓一下每隔一秒去抓一下压力很大。也有可能一秒钟要刷好几次,一秒刷一次又太慢。

所以前后台匹配不好,这种情况下不太适合ajax 主要希望页面要做局部的刷新local fefrech 做全局刷新做局部翻新,react 跟用户进行交互,对用户的操作有所响应用户体验好还会用 ajax 请求服务端和客户端之间传json页面,系统的性能会提高这是从 ajax 角度考虑,但是它仍然是请求响应的模式。

Websocket客户端就是浏览器和服务器不是请求相应,是服务器想主动给客户端股票交易市场软件希望所有的客户不断的收到消息财经类网站都是样的,不断的收到服务器端推过来消息,在浏览器里都要去实现这样的功能所以用 websocket可以看到在一个系统里四种通信方式可能都存在但是前两种发生在后端后两种是发生在前端和后端之间。

所以在一个系统,各种各样的通讯方式都有像数据库在一个系统里面不应该只用一种数据库,应该根据不同的应用不同的场景,选择不同的数据库利用起来同步通信,异步通信同步通信和异步通信发生后台如果前端和后端发生通信异步通信是什么样的如果做一个前后端的双通又是什么样的所以这是不同的通信模式,在系统里应用的时候四种都不一样,websocket 本身很简单,但是要把相关凑到一起理解之间应该怎么样根据不同的场景做选择不同的场景不要认为要用一种通信模式,系统不要开发过于简单,都用一种方式

(5)Using Encoders and Decoders

The Java API for WebSocket provides support for converting between WebSocket messages and custom Java types using encoders and decoders.

An encoder takes a Java object and produces a representation that can be transmitted as a WebSocket message;

for example, encoders typically produce JSON, XML, or binary

representations.

A decoder performs the reverse function: it reads a WebSocket message and creates a Java object.

This mechanism simplifies WebSocket applications, because it decouples the business logic from the serialization and deserialization of objects.

聊天室,写名字就会有相应的加入收到的消息推给所有的客户端跑的时候开两个不同的浏览器

image.png

Encoderdecoder 在发送消息或者接收消息的时候,消息的格式是自定义的,可以如何进行处理

Implement one of the following interfaces:

Encoder.Text<T> for text messages

Encoder. Binary<T> for binary messages

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;

}

}

decoders

Implement one of the following interfaces:

Decoder. Text<T> for text messages

Decoder. Binary<T> for binary messages

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 {只要有消息过来给一个字符串组装成message

//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;

}}

加入之后谁说一句话另外一方能收到页面的第一块是名字输入第二块是所有的聊天记录用户列表底下是可选的展示一下双方通信对象

(6)An example - Chatroom

index.html

<html xmlns="http: / /www. w3. org/1999/xhtml">

<head>

<title>WebsocketBot</title>

<script type="text/javascript">

var wsocket;

// Websocket connection

var userName ;

// User's name

var textarea;

// Chat area

var wsconsole;

// Websocket console area

var userlist;

// User list area

function connect() {

textarea = document . getElementById( "textarea" );

wsconsole = document . getElementById( "wsconsole");

userlist = document . getElementById("userlist");

wsocket = new WebSocket (

"Ws://localhost : 8080/WebSocketChatRoom/ websocketbot") ;进行连接

wsocket . onmessage = onMessage ;回调函数一旦回调成功

document . getElementById( "name"). focus();页面刷新出来光标停在输入框上

image.png

document . getElementById( " consolediv"). style.visibility =' hidden' ;不显示

}

index.html

function onMessage(evt) {

var line = "";

var msg = JSON. parse(evt.data);

if (msg.type === "chat") {是聊天消息

line = msg. name +’’:’’;

if (msg. target.length > 0)

line ="@" + msg.target + "" ;

line = ‘’[--’’+msg.message +" " ;

textarea. value +=’’’’+ line;

} else if (msg.type === "info") {

line = "[--" + msg.info + "--]\n";加入的话术离开的话术

textarea. Value+ =+ ‘’’’line;

} else if (msg.type == "users") {用户列表消息

line = "Users:\n";

for (var i=0; i < msg.userlist.length; i++)

line +="-" + msg.userlist[i] + "\n";

userlist.value = line;

}

textarea. scrollTop = 999999 ;

wsconsole.value +=" - >"+evt.data + "\n";

wsconsole. scrollTop = 999999;

}

三种消息各不相同如果是聊天消息把新的消息组装之后 line 到当前的内容中按照内容不断增加如果是info直接就直接在会话区显示一下消息是什么如果是消息列表就刷新消息列表

index.html

function sendJoin() {

var input = document . getElementById("input");

var name = document . getElementById("name");

Var join = document. getElementById("join");

var jsonstr;

if (name . value.length > 0) {

var joinMsg ={};

joinMsg. type = "join";

joinMsg. name = name. value ;

jsonstr = JSON. stringify(joinMsg);

wsocket . send(jsonstr);

name.disabled = true;

join.disabled = true;

input.disabled = false;

userName = name. value;

wsconsole. value +="<-" + jsonstr + "\n";

wsconsole. scrollTop = 999999;

}

}

在浏览器里面如果输入谁就把它组装成 json 对象客户端发了消息给服务器端

index.html

function sendMessage(evt) {

var input = document . getElementById("input");

var jsonstr;

var msgstr;

if (evt.keyCode === 13 && input. value.length > 0) {

var chatMsg = {};

chatMsg.type = "chat";

chatMsg. name = userName;

msgstr = input. value;

chatMsg.target = getTarget (msgstr.replace(/,/g, ""));

chatMsg . message = cleanTarget (msgstr);

chatMsg . message =

chatMsg . message . replace(/(lrlnllnlr)/gm,"");

jsonstr = JSON. stringify( chatMsg);

wsocket . send(jsonstr);

input.value = "";

wsconsole. value +="<- " + jsonstr + "\n";

wsconsole. scrollTop = 999999;

}

}

发送聊天消息出去判断是不是按过回车或者是输入的密码是大于零,不是空消息组消息消息的类型是聊天消息组装起来弄成 json 对象

index.html

function checkJoin(evt) {

var name = document . getElementById("name");

var input = document . getElementById( " input" ) ;

if (evt.keyCode === 13 && name . value.length > 0) {

sendJoin();

input. focus();

}

}

function getTarget(str) {

var arr = str.split(" ");

var target = "";

for (var i=0; i<arr.length; i++) {

if (arr[i]. charAt(0) === '@') {

target = arr[i]. substring(1,arr[i] . length);

target = target. replace(/(\r\n|n\r)/gm,"");

}

}

return target ;

}

一个人加入之后按下回车健,并且还写一个名字大于0,如果是就加入消息把输入框给过去

index.html

function cleanTarget(str) {

var arr = str.split(" ");

var cleanstr ="";

for (var i=0; i<arr.length; i++) {

if (arr[i].charAt(0) !== '@')

cleanstr += arr[i] +'

}

return cleanstr. substring(0, cleanstr.length-1);

}

function showHideConsole() {

var chkbox = document . getElementById(" showhideconsole");

var consolediv = document . getElementById("consolediv");

if (chkbox. checked )

consolediv. style. visibility = 'visible';

else

consolediv. style. visibility = 'hidden';

}

window . addEventListener( "load", connect, false);

</script>

</head>

index.html

<body>

<h1>WebsocketBot</h1>

Your name: <input id="name" type="text" size="20" maxlength="20"

onkeyup=" checkJoin(event);"/>

<input type="súbmit" id="join" value="Join!"

onclick="sendJoin();"/><br/>

<textarea id="input" cols="70" rows="1" disabled="true"

onkeyup= " sendMessage( event); "></textarea><br/>

<textarea id="textarea" cols="70" rows="20"

readonly= "true"></textarea>显示所有聊天消息

<textarea id="userlist" cols="20" rows="20"用户的列表

readonly= "true">< /textarea>

<br/><br/><br/>

<input id=" showhideconsole" type= "checkbox"

onclick= " showHideConsole();"/>

Show WebSocket console<br/>

<div id=" consolediv"><textarea id= "wsconsole" cols="80" rows="8"

readonly= "true" style= "font-size: 8pt;"></textarea>/div>

</body>

</html>

页面构成里面输入名字的地方按下键弹起时检查用户按下设置的回车键,并且名字是否大于零如果大于零发送有人加入的消息数据也可以不按回车点按钮也可以一旦有输入消息,同样会判断是否有按键弹起发消息检验是否大于零

BotEndPoint.java

@ServerEndpoint(

value = " /websocketbot" ,

decoders ={ MessageDecoder.class }

encoders ={ JoinMessageEncoder . class, ChatMessageEncoder . class,

InfoMessageEncoder. class, UsersMessageEncoder. class }

)

public class BotEndpoint {功能不复杂

private static final Logger logger = Logger. getLogger("BotEndpoint");

@OnOpen

public void openConnection(Session session) {

logger . log(Level. INFO, "Connection opened.");

}

有组装和解析消息的打开连接但是没做什么

BotEndPoint.java

@OnMessage

public void message(final Session session, Message msg) {

if (msg instanceof JoinMessage) {

JoinMessage jmsg = (JoinMessage), msg;

sess ion. getUserProperties() . put("name", jmsg. getName() );

session . getUserProperties() . put("active", true);

logger . log(Level . INFO, "Received: [0]", jmsg. toString());

sendAll(session, new InfoMessage(jmsg . getName() + " has

joined the chat"));

sendAll(session, new ChatMessage( "Duke", jmsg. getName( ),

"Hi there!!"));

sendAll(session, new UsersMessage( this . getUserList(session)));

} else if (msg instanceof ChatMessage) {

final ChatMessage cmsg = (ChatMessage) msg;

logger . log(Level . INFO, "Received: [0]", cmsg. toString());

sendAll(session, cmsg);

}

}

判断一个消息是什么判断谁加入解析内容是谁加入还是离开把消息发送给所有的人如果谁加入就sendal如果是聊天消息也是 sendall

BotEndPoint.java

public synchronized void sendAll(Session session, Object msg) {

try {

for(Session s : session. getOpenSessions()) {

if (s. isOpen()) {

s. getBasicRemote() . sendObject (msg);

logger . log(Level.INFO, "Sent: [0]", msg. toString());

}

}

] catch (IOExceptionl EncodeException e) {

logger . log(Level . INFO, e. toString());

public List<String> getUserList(Session session) {

List<String> users = new ArrayList<>( );

for (Session S : session. getOpenSessions()) {

if (s. is0pen( )&&(boolean) s. getUserProperties().get("active"))

users. add(s. getUserProperties(). get("name") . toString());

}

return users;

}

}

保持当前所有 session 的连接需求只要开放就把消息推给就可以看到所有的消息是如何得到的

Message.java

public class Message {}

ChatMessage.java

public class ChatMessage extends Message {

private String name;

private String target;

private String message;

public ChatMessage(String name, String target, String message) {

this. name = name ;

this.target = target;

this .message = message ;

}

...

public String getMessage() { return message;}

public void setMessage(String message) f this .message = message; }

}

Message 有不同的属性usersmessage 属性不一样而已。

ChatMessageEncoder.java

public class ChatMessageEncoder, implements Encoder. Text<ChatMessage> {

@Override

public void init( EndpointConfig ec) {}

@Override

public void destroy() {}

@Override

public String encode ( ChatMessage chatMessage ) throws EncodeException

StringWriter swriter = new StringWriter();

try (JsonGenerator jsonGen = Json. createGenerator(swriter)) {

jsonGen. writeStartObject( )

. write("type", "chat'

, write("name", chatMessage . getName())

. write("target" , chatMessage . getTarget( ) )

. write( "message",

chatMessage . getMessage())

. writeEnd( );

}

return swriter . toString();

}

}

针对聊天的 messageencode 里面主要是 encode 的方法要把这个消息的内容组成一个 json 的对象,最后写出去解析 chat 里面的内容

MessageDecoder.java

public class MessageDecoder implements Decoder. Text <Message> {

private Map<String,String> messageMap;

@Override

public void init(EndpointConfig ec) {}

@Override

public void destroy() {}

/* Create a new Message object if the message can be decoded */

@Override

public Message decode(String string) throws DecodeException {

Message msg = null;

if (wíllDecode(string)) {

switch (messageMap. get("type")) {

case "join":

msg = new JoinMessage (messageMap. get("name"));

break;

case "chat":

msg = new ChatMessage (messageMap. get("name"),

messageMap . get( "target" ),

messageMap . get( "message"));

} else {

throw new DecodeException(string, " [Message] Can't decode.");

return msg;

}

给了字符串如何把字符串内容组成想要的消息一个是把消息组成 json一个是把接收到的 json组成想要的消息自定义怎么样的消息合适这就是 websocket 主要的逻辑

相关文章
WebSocket学习笔记 -- 环境搭建2
WebSocket学习笔记 -- 环境搭建2
|
JSON JavaScript 数据格式
WebSocket学习笔记 -- 环境搭建1
WebSocket学习笔记 -- 环境搭建1
126 0
|
移动开发 网络协议 HTML5
慧安-WebSocket协议学习笔记
慧安-WebSocket协议学习笔记
94 0
|
XML 前端开发 JavaScript
SpringBoot 整合 WebSocket|学习笔记
快速学习 SpringBoot 整合 WebSocket
123 0
SpringBoot 整合 WebSocket|学习笔记
|
存储 Java
netty框架的学习笔记 + 一个netty实现websocket通信案例
一、前言 1.什么是netty? 2.netty的使用场景。 3.学习目录 二.java io通信 三.netty入门 四.websocket入门 五.netty实现websocket通信案例。 1.
297 0
|
移动开发 网络协议 前端开发
spring学习笔记(八)webSocket
spring学习笔记(八)webSocket
216 0
|
5月前
|
前端开发 网络协议 JavaScript
在Spring Boot中实现基于WebSocket的实时通信
在Spring Boot中实现基于WebSocket的实时通信
|
2月前
|
开发框架 前端开发 网络协议
Spring Boot结合Netty和WebSocket,实现后台向前端实时推送信息
【10月更文挑战第18天】 在现代互联网应用中,实时通信变得越来越重要。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为客户端和服务器之间的实时数据传输提供了一种高效的解决方案。Netty作为一个高性能、事件驱动的NIO框架,它基于Java NIO实现了异步和事件驱动的网络应用程序。Spring Boot是一个基于Spring框架的微服务开发框架,它提供了许多开箱即用的功能和简化配置的机制。本文将详细介绍如何使用Spring Boot集成Netty和WebSocket,实现后台向前端推送信息的功能。
582 1
|
2月前
|
前端开发 Java C++
RSocket vs WebSocket:Spring Boot 3.3 中的两大实时通信利器
本文介绍了在 Spring Boot 3.3 中使用 RSocket 和 WebSocket 实现实时通信的方法。RSocket 是一种高效的网络通信协议,支持多种通信模式,适用于微服务和流式数据传输。WebSocket 则是一种标准协议,支持全双工通信,适合实时数据更新场景。文章通过一个完整的示例,展示了如何配置项目、实现前后端交互和消息传递,并提供了详细的代码示例。通过这些技术,可以大幅提升系统的响应速度和处理效率。