springboot自带websocket,通过几个简单的注解就可以实现websocket的功能;
启动类跟普通的springboot一样:
/**
* 2023年3月2日下午4:16:57
*/
package testspringboot.test7websocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
/**
* @author XWF
*
*/
@SpringBootApplication
@PropertySource(value = "test7.properties")
public class Test7Main {
/**
* @param args
*/
public static void main(String[] args) {
SpringApplication.run(Test7Main.class, args);
}
}
还需要一个websocket的配置类:
/**
* 2023年3月2日下午4:26:15
*/
package testspringboot.test7websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @author XWF
*
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
配置文件里只配置了一个端口:
主要的逻辑都放到@ServerEndpoint标签注释的类里,类似controller的功能;
可以使用4种注解处理业务:
另外可以通过Session对象设置属性或者发送数据,session.getUserProperties()存取属性,session.getBasicRemote()或者session.getAsyncRemote()可以进行同步异步发送数据;
处理类:
/**
* 2023年3月2日下午4:27:01
*/
package testspringboot.test7websocket;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
/**
* @author XWF
*
*/
@Component
@ServerEndpoint(value = "/websockettest/{name}")
public class WebSocketHandler {
@OnOpen
public void onOpen(@PathParam("name") String name, Session session) throws IOException {
System.out.println("onOpen:" + name);
session.getUserProperties().put("name", name);
}
@OnClose
public void onClose(@PathParam("name") String name, Session session) throws IOException {
System.out.println("onClose:" + name);
System.out.println(session.getUserProperties().get("name") + "关闭了");
}
@OnError
public void onError(Session session, Throwable error) {
System.out.println("onERROR");
error.printStackTrace();
}
@OnMessage
public void textMessage(Session session, String msg) {
System.out.println("收到:" + msg);
try {
session.getBasicRemote().sendText("hello");
} catch (IOException e) {
e.printStackTrace();
}
}
@OnMessage
public void binaryMessage(Session session, ByteBuffer msg) {
System.out.println("Binary message: " + msg.toString());
}
@OnMessage
public void pongMessage(Session session, PongMessage msg) {
System.out.println("Pong message: " + msg.getApplicationData().toString());
}
}
测试websocket用的Apipost:
连接测试:
发送数据测试:
断开连接:
另外,也可以设置自己的编解码处理自己的消息,实现javax.websocket.Encoder.Text或者javax.websocket.Encoder.Binary接口实现编码器,实现javax.websocket.Decoder.Text或者javax.websocket.Decoder.Binary接口实现解码器,解码器最多两个,一个解码Text一个Binary;
在@ServerEndpoint注解里配置上自己的encoders和decoders就可以实现自定义编解码了;
自定义消息MsgA:
/**
* 2023年3月3日下午3:12:47
*/
package testspringboot.test7websocket;
/**
* @author XWF
*
*/
public class MsgA {
public int id;
public String name;
@Override
public String toString() {
return "MsgA [id=" + id + ", name=" + name + "]";
}
}
编码器:
/**
* 2023年3月3日下午3:14:02
*/
package testspringboot.test7websocket;
import javax.websocket.EncodeException;
import javax.websocket.Encoder.Text;
import javax.websocket.EndpointConfig;
/**
* @author XWF
*
*/
public class MsgATextEncoder implements Text{
@Override
public void init(EndpointConfig endpointConfig) {
System.out.println("msga encoder init");
}
@Override
public void destroy() {
System.out.println("msga encoder destroy");
}
// 进行编码操作,将对象编码成string
@Override
public String encode(MsgA object) throws EncodeException {
return object.toString();
}
}
解码器:
/**
* 2023年3月3日下午3:16:52
*/
package testspringboot.test7websocket;
import javax.websocket.DecodeException;
import javax.websocket.Decoder.Text;
import javax.websocket.EndpointConfig;
/**
* @author XWF
*
*/
public class MsgATextDecoder implements Text {
@Override
public void init(EndpointConfig endpointConfig) {
System.out.println("msga decoder init");
}
@Override
public void destroy() {
System.out.println("msga decoder destroy");
}
// 进行解码操作,将string解码成需要的对象
@Override
public MsgA decode(String s) throws DecodeException {
MsgA msga = new MsgA();
msga.id = Integer.parseInt(s.split(",")[0]);
msga.name = s.split(",")[1];
return msga;
}
// 验证消息是否可以解码,返回true可以解码,否则返回false
@Override
public boolean willDecode(String s) {
// 接收格式:id,name
if (s.split(",").length == 2) {
try {
Integer.parseInt(s.split(",")[0]);
} catch (NumberFormatException e) {
return false;
}
return true;
} else {
return false;
}
}
}
处理类:
/**
* 2023年3月3日下午3:07:45
*/
package testspringboot.test7websocket;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
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.ServerEndpoint;
import org.springframework.stereotype.Component;
/**
* @author XWF
*
*/
@Component
@ServerEndpoint(value = "/websocketmsgtest", encoders = {MsgATextEncoder.class}, decoders = {MsgATextDecoder.class})
public class WebSocketMsgHandler {
@OnOpen
public void onOpen() {
}
@OnClose
public void onClose() {
}
@OnError
public void onError(Throwable error) {
}
@OnMessage
public void textMessageA(Session session, MsgA msga) {
System.out.println("收到:" + msga);
MsgA sendMsg = new MsgA();
sendMsg.id = 9999;
sendMsg.name = "HELLO WORLD";
try {
session.getBasicRemote().sendObject(sendMsg);
} catch (IOException e) {
e.printStackTrace();
} catch (EncodeException e) {
e.printStackTrace();
}
sendMsg.id = 8888;
sendMsg.name = "hello world";
Future future = session.getAsyncRemote().sendObject(sendMsg);
try {
future.get(3, TimeUnit.SECONDS);
System.out.println("发送完毕");
} catch (InterruptedException | ExecutionException | TimeoutException e) {
System.out.println("超时");
e.printStackTrace();
}
}
}
测试:
不按照解码格式要求请求会异常并断开连接:
正常测试: