WebSocket实现在线聊天

WebSocket实现在线聊天

在这里插入图片描述

前两天在公司接到一个需求,使用WebSocket实现微信扫码登陆,当时了解了一下WebSocket,都说WebSocket可以实现在线聊天,所以我自己也写了一个。(发个帖子证明我还活着😂)

简单介绍下什么是WebSocket

我们的都知道,前端向后端发起请求一般都是使用的Http协议,但是呢Http协议有一个不好的地方那就是,只能由客户端主动发起请求,服务器收到客户端的请求后才会返回结果数据。这种单向请求的特点,就造就了一个问题,如果服务器端数据发生了变化,客户端就很难获知。所以一般都是使用轮询的方式,不断的向后端发起请求查询是否有数据变化。但是轮询的效率非常低,严重的浪费资源。所以在这种情况下,就出现了WebSocket协议的请求。WebSocket协议的最大特点就是,只要客户端和服务器端建立了连接,服务器端可以主动向客户端推送消息,客户端也可以向服务器端发送消息。本文章就是要基于这个特点实现在线聊天。

环境

服务器端
  • Java1.8
  • Maven
  • SpringBoot
客户端:
  • Vue(直接用原生Html配合JavaScript也可以,我使用Vue是为了使用双向绑定)

服务器的搭建

添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--WebSocket-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
注入Bean对象

Bean对象的注入用法就不用介绍了吧,直接在启动类中添加以下代码就好了。

此处说明一下为什么要注入此对象,SpringBoot的一大特点就是可以使用注解来简化配置,所以在注入这个对象之后,就可以使用WebSocket的注解。

/**
* 用于扫描带有ServerEndpoint注解成为WebSocket
* @return ServerEndpointExporter
*/
@Bean
public ServerEndpointExporter serverEndpointExporter () {
    return new ServerEndpointExporter();
}
配置Socket

通过这一个类实现Socket请求。

package cn.yanghuisen.websocketdemo.socket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Y
 */
@Slf4j
@Component
@ServerEndpoint("/socket/chat/{userName}")
public class WebSocket {
    /**
     * 所有的客户端
     */
    public static Map<String,Session> sessions = new HashMap<>();

    /**
     * 建立连接时的回调
     * @param session session(客户端)
     * @param userName 用户名
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("userName") String userName) {
      log.info("{},进入聊天室",userName);
      if (sessions.containsKey(userName)){
          session.getAsyncRemote().sendText("用户名已经存在");
          return;
      }
      sessions.put(userName,session);
      senMessage(String.format("%s:加入群聊",userName));
    }

    /**
     * 断开连接时的回调
     * @param userName 用户名
     */
    @OnClose
    public void onClose(Session session,@PathParam("userName") String userName) throws IOException {
        session.close();
        log.info("{},断开连接",userName);
        sessions.remove(userName);
        senMessage(String.format("%s:离开群聊",userName));
    }

    /**
     * 发生错误时的回调
     * @param session session(客户端)
     * @param throwable 错误
     */
    @OnError
    public void onError(Session session, Throwable throwable,@PathParam("userName") String userName) throws IOException {
        log.info("{},出现错误", userName);
        session.close();
        sessions.remove(userName);
    }

    /**
     * 收到消息时的回调
     * @param message 消息
     * @param userName 用户名
     */
    @OnMessage
    public void onMessage(String message,@PathParam("userName") String userName) {
        log.info("{}:{}",userName,message);
        senMessage(String.format("%s:%s",userName,message));
    }

    /**
     * 发送给指定客户端
     * @param message 消息
     * @param session session(客户端)
     */
    public void senMessage(String message,Session session) {
        // 发送消息
        session.getAsyncRemote().sendText(message);
    }

    /**
     * 发送给所有的客户端
     * @param message 消息
     */
    public void senMessage(String message) {
        sessions.forEach((k,v) -> {
            if (v.isOpen()) {
                // 发送消息
                v.getAsyncRemote().sendText(message);
            }
        });
    }
}

  • @OnOpen:此注解使用在方法上,当服务器端和客户端建立起连接时会回调配置了该注解的方法。
  • @OnClose:此注解使用在方法上,当服务器端和客户端断开连接时会回调配置了该注解的方法。
  • @OnError:此注解使用在方法上,当服务器端发生了异常时会回调配置了该注解的方法。
  • @OnMessage:此注解使用在方法上,当服务器端收到客户端发送的消息时会回调配置了该注解的方法。
  • @ServerEndpoint:类似于@RequestMapping注解,用于声明该请求是一个WebSocket协议的请求。

客户端的搭建

<template>
  <div id="app">
    <div class="left">
      <input type="text" v-model="userName"> <button @click="add">加入</button>
      <br><br><br>
      <textarea
        cols="30"
        style="height: 50px"
        v-model="message"
      ></textarea>
      <br>
      <button class="send" @click="sendMessage">发送</button>
      
      <button class="reset" @click="message = null">重置</button>

      <button class="close" @click="close">断开</button>
    </div>
    <div class="right">
      <p>{{isOpenMessage}}</p>
      <p v-for="(message,index) in messageList" :key="index"> {{ message }}</p>
    </div>
  </div>
</template>

<script>

export default {
  name: 'App',
  data () {
    return {
      userName: null,
      url: 'ws://localhost:8888/socket/chat/',
      ws: null,
      isOpenMessage: '暂未连接到服务器',
      messageList: [],
      message: null
    }
  },
  created () {
    this.isSocket()
  },
  methods: {
    // 加入聊天室
    add() {
      // 创建WebSocket连接
      this.ws = new WebSocket(this.url+this.userName);

      // 建立连接成功回调
      this.ws.onopen = () =>{
        this.isOpenMessage = '服务器连接成功'
      }

      // 客户端收到服务器端消息时回调
      this.ws.onmessage = (message) => {
        this.messageList.push(message.data)
        console.log(message.data);
      }
    },
    // 判断是否支持WebSocket
    isSocket() {
      if (!('WebSocket' in window)) {
        alert('该浏览器不支持WebSocket')
        return false
      }
      return true
    },
    sendMessage() {
      this.ws.send(this.message)
    },
    close() {
      this.ws.close()
    }
  }
}
</script>

<style>
.send, .reset, .close {
  margin: 10px;
}
.left, .right {
  width: 300px;
  float: left
}
</style>

  • new WebSocket(this.url+this.userName):建立客户端和服务器端的连接,建立成功后,前端会回调onopen的方法。
  • onopen:连接建立成功后回调的方法。
  • onmessage:客户端收到服务器端推送的消息时回调的方法。
  • this.ws.send(this.message):服务器端发送消息。
  • this.ws.close():断开服务器端和客户端的连接。

客户端中有些没有写到比如onerroronclose等,感兴趣的可以自己加上试试。

End

整个下来还是非常简单的,毕竟 我是一个啥也不会的程序员/(ㄒoㄒ)/~~,学的太多掉头发。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值