此文档适合初次搭建websocket的情况,包含以下内容
- websocket启动需要的配置
- ws地址
- 注入业务service
- 上传内容过大问题,如:base64的图片
引入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的项目配置添加完成
创建配置类 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;
}
}
创建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
其中websocketServer是WebSocketServer 类上的注解@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,否则会报错。
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) {
}
}
}