当前位置: 首页 > news >正文

JavaWeb之WebSocket

目录

    • 一、 websocket 概念
    • 二、WebSocket原理
    • 三、WebSocket特点
    • 四、WebSocket应用场景
    • 五、Websocket基本使用
      • 1、创建Websocket对象
      • 2、Websocket事件
      • 3、Websocket方法
      • 4、前端服务程序
    • 六、聊天室案例
      • 1、Tomcat版本:8.0.44
      • 2、Maven 依赖:
      • 3、前端代码
      • 4、后端代码

一、 websocket 概念

WebSocket是HTML5提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行双向数据传输。WebSocket 的出现就解决了半双工通信的弊端。它最大的特点是:服务器可以向客户端主动推动消息,客户端也可以主动向服务器推送消息。

获取到最新的信息,只有再次发起客户端请求,服务器端才会返回结果。但是服务器端不能做到推送消息给客户端,当然我们可以使用轮询,查看服务器有没有新的消息

二、WebSocket原理

客户端向 WebSocket 服务器通知(notify)一个带有所有接收者ID(recipients IDs)的事件(event),服务器接收后立即通知所有活跃的(active)客户端,只有ID在接收者ID序列中的客户端才会处理这个事件。
在这里插入图片描述

三、WebSocket特点

  • 支持双向通信,实时性更强,相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少
  • 可以发送文本,也可以发送二进制数据
  • 建立在TCP协议之上,服务端的实现比较容易
  • 数据格式比较轻量,性能开销小,通信高效
  • 没有同源限制,客户端可以与任意服务器通信
  • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
  • 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器

四、WebSocket应用场景

  • 弹幕
  • 媒体聊天
  • 协同编辑
  • 基于位置的应用
  • 体育实况更新
  • 股票基金报价实时更新

五、Websocket基本使用

在HTML5中,浏览器已经实现了websocket的API,直接使用即可:WebSocket-MDN

1、创建Websocket对象

// 参数1: url:连接的websocket属性
// 参数2: protocol,可选的,指定连接的协议
// var socket = new WebSocket('ws://echo.websocket.org')
var Socket = new WebSocket(url, [protocol] );

2、Websocket事件

事件事件处理程序描述
openSocket.onopen连接建立时触发
messageSocket.onmessage客户端接收服务端数据时触发
errorSocket.onerror通信发生错误时触发
closeSocket.onclose连接关闭时触发

3、Websocket方法

方法描述
Socket.send()使用连接发送数据
Socket.close()关闭连接

WebSocket的状态

可以通过读取readyState的属性值来获取WebSocket对象的状态,readyState属性存在以下几种属性值。

  • CONNECTING(数字值为0),表示正在连接;
  • OPEN(数字值为1),表示已建立连接;
  • CLOSING(数字值为2),表示正在关闭连接;
  • CLOSED(数字值为3),表示已关闭链接。

4、前端服务程序

// 导入第三方模块
const ws = require('nodejs-websocket')
var websocket = new WebSocket("ws://172.16.116.22:8088/WebSocket/chatServer/"+nickname);
// websocket占用的端口号
const PORT = 3000

const server = ws.createServer(connect => {
  console.log('新的连接')

  // 接收到客户端的文本内容时触发
  connect.on('text', str => {
    console.log('接收:' + str)
    // 把接收到的字符串转换成大写,并且给客户端响应
    connect.sendText(str.toUpperCase() + '!!!!')
  })

  // 监听关闭事件
  connect.on('close', () => {
    console.log('连接关闭了')
  })

  // 监听错误事件, 比如浏览器关闭了连接,或者发送的数据格式不对等
  connect.on('error', err => {
    console.log('连接异常')
  })
})

// 启动websocket服务
server.listen(PORT, function() {
  console.log(`websocket server listening on ${PORT}`)
})

WebSocket的前端API

  • WebSocket需要接收一个url参数,然后调用WebSocket对象的构造器来建立与服务器之间的通信链接。
    websocket = new WebSocket(“ws://localhost:8088/chatServer/”+nickname);

    • URL字符串必须以 “ws” 或 “wss”(加密通信)开头。
    • 利用上面的代码,我们的通信连接建立之后,就可以进行客户端与服务器端的双向通信了。可以使用WebSocket对象的send方法对服务器发送数据,但是只能发送文本数据(我们可以使用JSON对象把任何js对象转换成文本数据后再进行发送)。
  • websocket.send(“data”);

    • websocket.send(“data”);
  • websocket.onmessage = function(event) {

    websocket.onmessage = function(event) {
       var data = event.data;
    }
    
  • 监听socket的打开事件

    • 通过获取onopen事件来监听socket的打开事件。如下代码:
      websocket.onopen = function(event) {
      // 开始通信时的处理
      }
      
  • 通过获取onclose事件来监听socket的关闭事件。如下代码:

    websocket.onclose = function(event) {
    // 通信结束时的处理
    }
    
  • 关闭socket
    通过close方法来关闭socket, 如下代码:

    websocket.close();

六、聊天室案例

1、Tomcat版本:8.0.44

2、Maven 依赖:

<dependency>
      <groupId>javax.websocket</groupId>
      <artifactId>javax.websocket-api</artifactId>
      <version>1.1</version>
      <scope>provided</scope>
    </dependency>
<!--    <dependency>-->
<!--      <groupId>javax</groupId>-->
<!--      <artifactId>javaee-api</artifactId>-->
<!--      <version>8.0</version>-->
<!--    </dependency>-->
    <!--    导入servlet-->
<!--    处理JSON-->
<!--    <dependency>-->
<!--      <groupId>com.alibaba</groupId>-->
<!--      <artifactId>fastjson</artifactId>-->
<!--      <version> 1.2.58</version>-->
<!--    </dependency>-->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.5</version>
    </dependency>

3、前端代码

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>聊天室</title>
  <link rel="stylesheet" href="">
  <style type="text/css" media="screen">
    *{margin: 0px;padding: 0px;}
    .all{
      width: 900px;
      height: 700px;
      margin: 0 auto;
    }
    .all-top{
      width: 100%;
      height: 30px;
      background-color: yellow;
    }
    .all-center{
      width: 100%;
      height: 570px;
      /*添加滚动条*/
      overflow-y: scroll;
      background-color: pink;
    }
    .all-bottom{
      width: 100%;
      height: 100px;
      background-color: green;
    }
    .number{
      width: 100%;
      height: 40px;
      background-color: green;
      text-align: center;
      color: #fff;
      font-size: 20px;
    }
  </style>
</head>
<body>
<div class="all">
  <div class="all-top">
    昵称:<input type="text" id="nickname">
    <button type="button" onclick="connect()">链接</button>
  </div>
  <div class="number" id="number">
    当前在线人数:0</div>
  <div class="all-center" id="message">
    IP:172.16.116.22 <br>
    端口是:8088<br>
    工程是:WebSocket<br>
    接口是:chatServer<br>
  </div>
  <div class="all-bottom">
    发送信息:<input type="text" id="msg">
    <button type="button" onclick="sendMsg()">发送</button>
  </div>
</div>
</body>
<script  type="text/javascript">
  /*ws:// 代表 websocket*/
  /*127.0.0.1:8088 后端服务地址*/
  /*WebSocket 工程名称*/
  /*chatServer 对应@ServerEndpoint注解的value属性*/
  var websocket = null;
  //链接后端服务器
  function connect(){
    if(websocket != null){
      return;
    }
    var nickname = document.getElementById("nickname").value;
    console.log(nickname)
    websocket = new WebSocket("ws://localhost:8088/chatServer/"+nickname);
    //链接成功时触发
    websocket.onopen = function(){
      setMsgToPage("你链接上聊天服务器了!");
    }
    //收到服务器端信息触发
    websocket.onmessage = function(event){
      var data = JSON.parse(event.data)
      if(data.code == 1){//接收到的是聊天信息
        var umsg = data.name + "说:" + data.msg;
        setMsgToPage(umsg);
      }else if(data.code == 2){//上线
        document.getElementById("number").innerHTML = "当前在线人数:"+data.number+"人";
        setMsgToPage("舰长:"+data.name+"进入直播间");
      }else if(data.code == 3){//下线
        document.getElementById("number").innerHTML = "当前在线人数:"+data.number+"人";
        setMsgToPage("舰长:"+data.name+"离开直播间");
      }
    }
    //发生异常时触发
    websocket.onerror = function(){
    }
    //链接关闭触发
    websocket.onclose = function(){
    }
  }
  //用来给后端发送信息
  function sendMsg(){
    //获取用户输入的信息
    var msg = document.getElementById("msg").value;
    //给后端发送
    websocket.send(msg);
  }

  //把信息赋值到页面
  function setMsgToPage(msg){
    document.getElementById("message").innerHTML += msg + "</br>";
  }



</script>
</html>

4、后端代码

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

//WebSocket服务
//value是WebSocket接口地址
@ServerEndpoint(value="/chatServer/{userName}")
public class ChatServer {
    //session使用来保存用户状态信息的
    //CopyOnWriteArraySet == set 但是CopyOnWriteArraySet是线程安全的
    private static CopyOnWriteArraySet<Session> socketSet = new CopyOnWriteArraySet<Session>();

    //1.0用户连接时触发
    @OnOpen
    public void onOpen(@PathParam("userName")String name,Session session){
        System.out.println("用户开始链接");
        //Session 用户的信息 用户的状态
        socketSet.add(session);
        for(Session s:socketSet){
            //给所有在集合内的用户广播信息
            try {
                if(s.isOpen()){
                    s.getBasicRemote().sendText("{\"code\":2,\"number\":\""+socketSet.size()+"\",\"name\":\""+name+"\"}");
                }else{
                    s.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //2.0接收到用户信息时触发
    @OnMessage
    public void onMessage(@PathParam("userName")String name,String data,Session session){
        for(Session s:socketSet){
            //给所有在集合内的用户广播信息
            try {
                if(s.isOpen()){

                    s.getBasicRemote().sendText("{\"code\":1,\"name\":\""+name+"\",\"msg\":\""+data+"\"}");

                }else{
                    s.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //3.0用户下线时触发
    @OnClose
    public void onClose(@PathParam("userName")String name,Session session){
        //用户下线时删除这个
        socketSet.remove(session);
        for(Session s:socketSet){
            //给所有在集合内的用户广播信息
            try {
                if(s.isOpen()){
                    s.getBasicRemote().sendText("{\"code\":3,\"number\":\""+socketSet.size()+"\",\"name\":\""+name+"\"}");
                }else{
                    s.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //4.0连接异常时触发
}

相关文章:

  • Servlet、Servlet的5个接口方法、生命周期、以及模拟实现 HttpServlet 来写接口的基本原理
  • 加速还是安全?CDN与群联云防护的本质差异与适用场景
  • 闻所闻尽:穿透声音的寂静,照见生命的本真
  • 数据库主观题题库及答案
  • 力扣45.跳跃游戏
  • 【leetcode hot 100 39】组合总和
  • 查看GPU型号、大小;CPU型号、个数、核数、内存
  • Vue3 基础语法指南:响应式系统与 Ref 应用
  • JavaIO流的使用和修饰器模式(直击心灵版)
  • 1201. 【高精度练习】蜜蜂路线
  • vpc网络的原理
  • 【Android】基础架构(详细介绍)
  • HAL库定时器配置
  • 每日一题力扣3248.矩阵中的蛇c++
  • 第三章:单调队列
  • Java中的JSONObject对象
  • DeepSeek-R1论文深度解析:纯强化学习如何引爆LLM推理革命?
  • LeetCode[454]四数相加Ⅱ
  • 近期学习资料,尚未整理
  • Wpf Avalonia-实现中英文切换工程
  • 十二届上海市委第六轮巡视全面进驻,巡视组联系方式公布
  • 两部门预拨4000万元支持山西、广西、陕西做好抗旱救灾工作
  • 国务院食安办:加强五一假期食品生产、销售、餐饮服务环节监管
  • “五一”逃离城市计划:带上帐篷去大自然里充电
  • 宋徽宗《芙蓉锦鸡图》亮相,故宫首展历代动物绘画
  • 脱发后怎么把头发养回来?脱发自救指南来了