Spring boot 实现 WebSocket服务端

Spring boot 实现 WebSocket服务端

这里写目录标题

    • Spring boot 实现 WebSocket服务端
        • 声明
        • 准备工作
      • 1. 配置
      • 2. WebSocket服务类
      • 3. 编写客户端,测试websocket服务
      • 总结

声明

此文档适合初次搭建websocket的情况,包含以下内容

  • websocket启动需要的配置
  • ws地址
  • 注入业务service
  • 上传内容过大问题,如:base64的图片

websocket服务端
websocket服务端
websocket客户端
websocket客户端

准备工作

引入Maven依赖

        
        <dependency>
             <groupId>log4jgroupId>
            <artifactId>log4jartifactId>
            <version>1.2.12version>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-websocketartifactId>
        dependency>

        
        <dependency>
            <groupId>org.java-websocketgroupId>
            <artifactId>Java-WebSocketartifactId>
            <version>1.3.8version>
        dependency>

添加application.yml配置文件

server:
  port: 8800
  servlet:
    context-path: /myserver
spring:
  application:
    name: websocketTest

websocket的项目配置添加完成

1. 配置

创建配置类 WebSocketConfiguration.class 实现了 ServletContextInitializer 接口

类上添加注解 @Configuration, 表明这是个配置类

package com.cloud.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import org.springframework.web.util.WebAppRootListener;

import javax.servlet.ServletContext;

/**
 * 

* websocket支持 *

* * @author Administrator * @date 2021-04-20 */
@Configuration public class WebSocketConfiguration implements ServletContextInitializer { @Bean public ServerEndpointExporter serverEndpointExporter() { ServerEndpointExporter serverEndpointExporter = new ServerEndpointExporter(); return serverEndpointExporter; } /** * 启动加载 * * @param servletContext */ @Override public void onStartup(ServletContext servletContext) { servletContext.addListener(WebAppRootListener.class); // 接收base64的字符串,等于50M servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize", "52428800"); servletContext.setInitParameter("org.apache.tomcat.websocket.binaryBufferSize", "52428800"); } /** * 注入业务service * * @param sxBlxxjlbService */ @Autowired public void setWebSocketServer(MyService myService) { WebSocketServer.myService = myService; } }

2. WebSocket服务类

创建websocket服务实现类

package com.websocket.websocket;

import com.websocket.service.MyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 

* websocket服务实现 *

* * @author Administrator * @date 2021-04-20 */
@Component @ServerEndpoint(value = "/websocketServer/{userId}") public class WebSocketServer { private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class); /** * 业务service */ public static MyService myService; /** * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 */ private static ConcurrentMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>(); /** * 与某个客户端的连接会话,需要通过它来给客户端发送数据 */ private Session session; /** * 接收userId */ private String userId; @OnOpen public void onOpen(Session session, @PathParam("userId") String userId) { this.session = session; this.userId = userId; if (webSocketMap.containsKey(userId)) { // 重新连接,踢掉上次的连接信息 WebSocketServer webSocketServer = webSocketMap.get(userId); webSocketServer.onClose(); } webSocketMap.put(userId, this); LOGGER.info("用户登录:" + userId); } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { if (webSocketMap.containsKey(userId)) { try { // 踢掉已经连接的人 this.session.close(); webSocketMap.remove(userId); } catch (IOException e) { e.printStackTrace(); } } LOGGER.info("用户退出:" + userId); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息 */ @OnMessage public void onMessage(String message) { LOGGER.debug("用户消息:" + userId + ",报文:" + message); // 根据当前的userId类型判断转发到pc端还是app try { sendInfo(this.userId, "你好,我是" + myService.getMyIdentity()); } catch (IOException e) { e.printStackTrace(); } } /** * @param error 错误 */ @OnError public void onError(Throwable error) { try { String errorMsg = "用户错误:" + userId + ",原因:" + error.getMessage(); sendMessage(errorMsg); LOGGER.error(errorMsg); } catch (IOException e) { e.printStackTrace(); } } /** * 实现服务器主动推送-string */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * 实现服务器主动推送-object */ public void sendMessage(Object message) throws EncodeException, IOException { this.session.getBasicRemote().sendObject(message); } /** * 发送自定义消息 */ public static String sendInfo(String userId, String message) throws IOException { if (webSocketMap.containsKey(userId)) { webSocketMap.get(userId).sendMessage(message); return "发送成功"; } String errorMsg = "用户" + userId + ",不在线!"; LOGGER.info(errorMsg); return errorMsg; } /** * 发送自定义消息 */ public static void sendInfo(String userId, Object message) throws IOException, EncodeException { if (webSocketMap.containsKey(userId)) { webSocketMap.get(userId).sendMessage(message); return; } LOGGER.info("用户" + userId + ",不在线!"); } }

至此WebSocket的代码已经写完!

  • websocket的地址 ws://+IP地址+项目端口号+servlet.context-path+websocket名称

如:ws://127.0.0.1:8800/myserver/websocketServer/123456789

其中websocketServerWebSocketServer 类上的注解@ServerEndpoint(value = “/websocketServer/{userId}”)

servlet.context-path 如果没有配置就不用添加

  • 注入业务service实现

首先在websocket配置类中,使用依赖注入

/**
    * 注入业务service
    *
    * @param sxBlxxjlbService
    */
   @Autowired
   public void setWebSocketServer(MyService myService) {
       WebSocketServer.myService = myService;
   } 

然后在websocket服务实现类中,添加静态的业务service实现

	/**
    * 业务service
    */
   public static MyService myService;

这样就实现了在websocket服务中注入业务service,否则会报错。

3. 编写客户端,测试websocket服务

websocket测试代码

采用WebSocketClient作为客户端测试

package com.websocket.websocket;

import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;
import java.net.URISyntaxException;

/**
 * 

* 我的测试 *

* * @author Administrator */
public class TestMain { /** * 我的测试 * * @param args */ public static void main(String[] args) { try { String url = "ws://127.0.0.1:8800/myserver/websocketServer/123456789"; WebSocketClientTest webSocketTest = new WebSocketClientTest(new URI(url)); webSocketTest.connect(); while (!webSocketTest.getReadyState().equals(WebSocket.READYSTATE.OPEN)) { System.out.println("请稍后,连接中..."); Thread.sleep(1000); } webSocketTest.send("你好,服务器,我是客户端"); } catch (URISyntaxException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } static class WebSocketClientTest extends WebSocketClient { /** * Constructs a WebSocketClient instance and sets it to the connect to the * specified URI. The channel does not attampt to connect automatically. The connection * will be established once you call connect. * * @param serverUri the server URI to connect to */ public WebSocketClientTest(URI serverUri) { super(serverUri); } /** * Called after an opening handshake has been performed and the given websocket is ready to be written on. * * @param handshakedata The handshake of the websocket instance */ @Override public void onOpen(ServerHandshake handshakedata) { } /** * Callback for string messages received from the remote host * * @param message The UTF-8 decoded message that was received. * @see #onMessage(ByteBuffer) **/ @Override public void onMessage(String message) { System.out.println(message); } /** * Called after the websocket connection has been closed. * * @param code The codes can be looked up here: {@link CloseFrame} * @param reason Additional information string * @param remote **/ @Override public void onClose(int code, String reason, boolean remote) { } /** * Called when errors occurs. If an error causes the websocket connection to fail {@link #onClose(int, String, boolean)} will be called additionally.
* This method will be called primarily because of IO or protocol errors.
* If the given exception is an RuntimeException that probably means that you encountered a bug.
* * @param ex The exception causing this error **/
@Override public void onError(Exception ex) { } } }

总结

  1. 使用springwebsocket需要服务配置
  2. 对于业务service实现需要在配置类中引入,不能直接在WebSocket服务中注入,会报错

你可能感兴趣的:(JAVA,Spring,java,spring,websocket,spring,boot)