• WebSocket Day02 : 握手连接


    前言

            握手连接是WebSocket建立通信的第一步,通过客户端和服务器之间的一系列握手操作,确保了双方都支持WebSocket协议,并达成一致的通信参数。握手连接的过程包括客户端发起握手请求、服务器响应握手请求以及双方完成握手连接。完成握手连接后,客户端和服务器之间建立了一个持久性的双向通信链路,可以进行实时的数据传输。

           本次案例,使用 servlet 实现一个用户登录进入聊天室聊天。

    一、前期准备

    1、新建项目,结构如下

    2、导入依赖
    1. <dependency>
    2. <groupId>javax.websocketgroupId>
    3. <artifactId>javax.websocket-apiartifactId>
    4. <version>1.1version>
    5. <scope>providedscope>
    6. dependency>
    7. <dependency>
    8. <groupId>ch.qos.logbackgroupId>
    9. <artifactId>logback-classicartifactId>
    10. <version>1.3.8version>
    11. dependency>
    12. <dependency>
    13. <groupId>org.projectlombokgroupId>
    14. <artifactId>lombokartifactId>
    15. <version>1.18.24version>
    16. dependency>
    17. <dependency>
    18. <groupId>javax.servletgroupId>
    19. <artifactId>javax.servlet-apiartifactId>
    20. <version>4.0.1version>
    21. <scope>providedscope>
    22. dependency>
    23. <dependency>
    24. <groupId>com.fasterxml.jackson.coregroupId>
    25. <artifactId>jackson-databindartifactId>
    26. <version>2.14.2version>
    27. dependency>

     让我逐个解释这些依赖项的作用:

    1. javax.websocket-api: 这是Java WebSocket API的依赖项,用于在Java应用程序中实现WebSocket通信。

    2. logback-classic: 这是Logback日志框架的经典实现,用于打印日志。

    3. lombok: 这是一个Java库,用于通过注解减少Java代码的样板代码量,提高代码的可读性和简洁性。

    4. javax.servlet-api: 这是Java Servlet API的依赖项,用于开发基于Java的Web应用程序。

    5. jackson-databind: 这是Jackson JSON处理库的依赖项,用于在Java应用程序中进行JSON数据的序列化和反序列化操作。

    二、使用 servlet 实现登录功能

    1、新建一个 loginServlet
    1. /**
    2. * @Date 2023-11-01
    3. * @Author qiu
    4. * 用户登录
    5. */
    6. @WebServlet("/login")
    7. public class LoginServlet extends HttpServlet {
    8. @Override
    9. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    10. String userName = req.getParameter("userName");
    11. // 将用户名保存到 HttpSession
    12. req.getSession().setAttribute("user",userName);
    13. // 重定向到聊天的首页
    14. resp.sendRedirect("chat.html");
    15. }
    16. }

    让我来逐行解释这段代码的功能:

    1. @WebServlet("/login"):这是一个Servlet注解,指定了该Servlet对应的URL路径为"/login"。当客户端发送带有"/login"路径的请求时,Servlet容器将调用该Servlet来处理请求。

    2. public class LoginServlet extends HttpServlet:这是一个命名为LoginServlet的Java类,它继承自HttpServlet类,表示它是一个Servlet。

    3. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException:这是Servlet的service方法,它负责处理客户端的请求并返回响应。HttpServletRequest对象提供了关于HTTP请求的信息,而HttpServletResponse对象用于设置HTTP响应。

    4. String userName = req.getParameter("userName");:从HTTP请求中获取名为"userName"的参数值,并将其存储在一个名为userName的字符串变量中。这里假设前端通过HTTP请求将用户名作为参数传递给后端。

    5. req.getSession().setAttribute("user",userName);:使用HttpServletRequest的getSession()方法获取当前会话的HttpSession对象,并使用setAttribute()方法将用户名存储在名为"user"的属性中。这样,在整个会话期间,可以通过getAttribute()方法来访问和使用该属性。

    6. resp.sendRedirect("chat.html");:使用HttpServletResponse的sendRedirect()方法将响应重定向到"chat.html"页面。这意味着登录成功后,用户将被重定向到聊天页面。

    以上就是这段代码的功能。它通过获取用户输入的用户名,并将其保存到会话中,实现了用户登录的功能。然后,通过重定向将用户导航到聊天页面。

    2、新建一个 html 页面实现登录
    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Titletitle>
    6. head>
    7. <body>
    8. <h1>用户登录h1>
    9. <form name="f1" method="post" action="login">
    10. Name:<input type="text" name="userName"/>
    11. <input type="submit" value="登录">
    12. form>
    13. body>
    14. html>

    让我来逐行解释这段代码的功能: 

    1. :这是一个表单(form标签),用于接收用户输入的登录信息,并将其提交到名为"login"的URL路径。

    2. Name::这是一个文本输入框(input标签),用于用户输入用户名。name属性指定了该输入框的名称为"userName",以便后端能够通过该名称获取用户输入的值。

    3. :这是一个提交按钮(input标签),用于提交表单数据。当用户点击该按钮时,表单中的数据将被发送到服务器进行处理。

    三、握手连接

    1、新建一个 消息对象 实体类
    1. @Data
    2. @AllArgsConstructor
    3. @NoArgsConstructor
    4. public class Message {
    5. /**
    6. * 发送人
    7. */
    8. private String fromUser;
    9. /**
    10. * 发送时间
    11. */
    12. private String sendTime;
    13. /**
    14. * 发送内容
    15. */
    16. private String content;
    17. }

    让我来逐行解释这段代码的功能:

    1. @Data:这是一个Lombok注解,自动生成getter、setter、equals、hashCode和toString方法等常见的代码。

    2. @AllArgsConstructor:这是一个Lombok注解,自动生成一个包含所有属性的构造函数。

    3. @NoArgsConstructor:这是一个Lombok注解,自动生成一个无参构造函数。

    4. public class Message:这是一个命名为Message的Java类。

    5. private String fromUser;:这是一个私有的字符串类型变量,表示消息的发送人。

    6. private String sendTime;:这是一个私有的字符串类型变量,表示消息的发送时间。

    7. private String content;:这是一个私有的字符串类型变量,表示消息的内容。

    以上就是这段代码的功能。它定义了一个Message类,用于表示一条消息的数据结构。该类包含了发送人、发送时间和发送内容三个属性,并使用Lombok注解简化了相应的代码编写工作。通过创建Message对象,可以方便地操作和传递消息的相关信息。

    2、握手连接处理类
    1. public class WebSocketHandshake extends Configurator {
    2. /**
    3. * 重写握手处理方法
    4. * @param sec
    5. * @param request 请求对象
    6. * @param response 响应对象
    7. */
    8. @Override
    9. public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
    10. // 获取 HttpSession 对象
    11. HttpSession httpSession = (HttpSession)request.getHttpSession();
    12. // 获取用户名
    13. String userName = (String) httpSession.getAttribute("user");
    14. // 将用户名保存到当前用户连接 websocket 的 session 中
    15. sec.getUserProperties().put("user",userName);
    16. }
    17. }

    让我来逐行解释这段代码的功能:

    1. public class WebSocketHandshake extends Configurator:这是一个命名为WebSocketHandshake的Java类,继承了Configurator类。

    2. @Override:这是一个注解,表示该方法重写了父类或接口的方法。

    3. public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response):这是一个公共的无返回值方法,用于修改WebSocket握手过程中的处理逻辑。它接受三个参数:

      • sec:ServerEndpointConfig对象,用于配置WebSocket端点。
      • request:HandshakeRequest对象,表示握手请求。
      • response:HandshakeResponse对象,表示握手响应。
    4. HttpSession httpSession = (HttpSession)request.getHttpSession();:获取握手请求中的HttpSession对象,用于获取用户相关的会话信息。

    5. String userName = (String) httpSession.getAttribute("user");:从HttpSession对象中获取名为"user"的属性,即用户名。

    6. sec.getUserProperties().put("user",userName);:将获取到的用户名保存到当前用户连接WebSocket的session中。通过sec.getUserProperties()可以获取到与当前用户连接相关的会话属性,这里将用户名保存在"user"属性中。

    以上就是这段代码的功能。它通过重写WebSocket握手处理方法,在握手过程中获取并保存了用户名,以便后续在WebSocket连接中使用。

    1)为什么需要这个类握手处理类

    这个类是用于在WebSocket握手过程中获取并保存用户名的。WebSocket是一种基于TCP协议的全双工通信协议,它通过在客户端和服务器之间建立持久连接,实现实时的双向通信。

    在实际应用中,往往需要对连接WebSocket的用户进行身份验证和权限控制。而在WebSocket握手过程中,可以通过HTTP协议传递一些额外的信息,比如用户的身份信息。为了在后续的WebSocket连接中能够对用户进行身份验证和权限控制,我们需要将用户相关的信息保存起来。

    这个WebSocketHandshake类的作用就是在WebSocket握手过程中,获取用户的身份信息(这里是用户名),并将其保存到当前用户连接的WebSocket会话中。通过在握手过程中获取用户名,并将其保存在WebSocket的session中,我们可以在后续的WebSocket连接中使用这个信息进行身份验证和权限控制。

    因此,这个类的存在是为了方便在WebSocket应用中获取和保存用户信息,以便后续进行进一步的处理和控制。

    3、服务端
    1. @Slf4j
    2. @ServerEndpoint(value = "/connect",configurator = WebSocketHandshake.class)
    3. public class ChatServer {
    4. // 用户列表,key 为用户 id 或者是 name,
    5. // value 则是每一个客户端的 Session
    6. private static Map users = new HashMap<>();
    7. @OnOpen
    8. public void onOpen(Session session){
    9. // 添加用户到用户列表
    10. String userName = (String) session.getUserProperties().get("user");
    11. // 添加到用户列表中
    12. users.put(userName,session);
    13. }
    14. @OnMessage
    15. public void onMessage(String message,Session session) throws Exception {
    16. // 获取发送人
    17. String formUser = (String) session.getUserProperties().get("user");
    18. // 创建发送时间
    19. String sendTime = new SimpleDateFormat("hh:mm").format(new Date());
    20. // 封装消息对象并序列化为 JSON
    21. Message msg = new Message(formUser,sendTime,message);
    22. String jsonMessage = new ObjectMapper().writeValueAsString(msg);
    23. log.info(jsonMessage);
    24. // 群发给所有人
    25. for (String userName : users.keySet()){
    26. Session s = users.get(userName);
    27. s.getBasicRemote().sendText(jsonMessage);
    28. }
    29. }
    30. @OnClose
    31. public void onClose(Session session){
    32. // 将用户移除在线列表
    33. String userName = (String) session.getUserProperties().get("user");
    34. users.remove(userName);
    35. }
    36. }

    这是一个基于Java实现的WebSocket聊天室后端代码。

    首先,在websocket包下,使用了@ServerEndpoint(value = "/connect")注解声明了一个WebSocket服务端,对应的WebSocket地址为/connect

    接下来,代码中定义了一个静态变量users,用来存储所有连接到该WebSocket服务端的用户Session。在用户连接WebSocket服务器时,通过@OnOpen注解声明的方法将用户Session添加到用户列表中。

    @OnMessage注解声明的方法中,当WebSocket服务端接收到客户端发送的消息时,先获取发送人和发送时间,然后封装成一个Message对象并序列化成JSON格式。然后遍历用户列表,将消息发送给每一个客户端。

    最后,在@OnClose注解声明的方法中,当一个用户关闭连接时,将其从用户列表中移除。

    需要注意的是,在上述代码中还使用了@ServerEndpoint注解的configurator参数,通过自定义WebSocketHandshake类实现了WebSocket握手过程的一些特殊处理。具体可查看WebSocketHandshake类的实现。

    总的来说,这是一个简单的WebSocket聊天室后端实现,通过Java提供的WebSocket API完成了对WebSocket连接的管理和消息的广播。

    4、新建一个客户端聊天页面
    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Titletitle>
    6. <script src="js/JQuery文件.txt.js">script>
    7. head>
    8. <body>
    9. <h1>聊天室h1>
    10. <div id="msg">
    11. <input type="text" id="message"/>
    12. <input type="button" value="发送"/><br>
    13. div>
    14. <script>
    15. // 创建 WebSocket 对象
    16. var ws = new WebSocket("ws://localhost:8080/connect");
    17. // 接受服务端的信息
    18. ws.onmessage = function (event) {
    19. // 将消息填充到 div 中
    20. let data = event.data;
    21. // 将 json 字符串 转换为 json 对象
    22. data = $.parseJSON(data);
    23. $('#msg').append(data.fromUser + " : " + data.sendTime + "
      "
      );
    24. $('#msg').append(data.content + "
      "
      );
    25. }
    26. $(function () {
    27. $(':button').on('click',function () {
    28. let msg = $('#message').val();
    29. // 发送消息
    30. ws.send(msg);
    31. // 发送完之后清空消息框
    32. $('#message').val('');
    33. })
    34. })
    35. script>
    36. body>
    37. html>

    这是一个简单的前端实现,用于创建一个基本的聊天室界面,并通过WebSocket协议与服务器进行通信。下面逐行详细讲解代码的功能和实现。

    1. :引入一个JavaScript文件,其中包含了JQuery库的代码。

    2. 聊天室

      :在页面中插入一个标题,显示为"聊天室"。

    3. :定义一个div元素,用于显示聊天消息。

    4. :创建一个文本输入框,用户可以在其中输入消息。


    5. :创建一个按钮,用于发送消息。

    6. :JavaScript代码的结束标签。

    总体来说,这段代码实现了一个简单的聊天室界面,用户可以在文本输入框中输入消息,点击发送按钮后,消息会通过WebSocket协议发送给服务器。同时,页面会接收服务器返回的消息并将其显示在页面上。但需要注意的是,这只是前端部分的实现,后端代码和服务器的支持是实现完整聊天室功能的必要条件。

    5、运行效果

    本次案例就是通过实现用户的登录,然后把用户名保存到作用域中,然后再从作用域中获取用户名实现的一个多人聊天案例。通过登录去获取到进入聊天室的人是谁。 

    四、gitee 案例

    地址:ch02 · qiuqiu/WebSocket-study - 码云 - 开源中国 (gitee.com)

  • 相关阅读:
    记一次SQL注入的收获
    基于遗传算法的红绿灯间隔时间优化(代码完整,数据齐全)
    java计算机毕业设计婚纱摄影网设计MyBatis+系统+LW文档+源码+调试部署
    【Linux集群教程】07 块存储之 iSCSI 服务
    Kotlin okhttp3 HttpClient
    【Proteus仿真】【STM32单片机】大棚远程监测控制
    Mathorcup数学建模竞赛第四届-【妈妈杯】C题:家庭暑假旅游套餐的设计(附MATLAB和SAS代码)
    关于西门子plc的CPU
    【网络编程】网络编程中的基本概念及Java实现UDP、TCP客户端服务器程序(万字博文)
    使用 Mendix 中的 OIDC 模块集成 Azure AD SSO
  • 原文地址:https://blog.csdn.net/zhiqiuqiu2/article/details/134189277