1.webScoket客户端
@Slf4j
public class MyWebSocketClient extends WebSocketClient {
private Map<String, String> map;
public MyWebSocketClient(URI uri,Map<String, String> map) {
super(uri);
this.map=map;
}
@PostConstruct
private void init() {
try {
this.connectBlocking();
//开启心跳重连线程
new Thread(() -> {
while (true) {
try {
//当连接状态不为open时每5秒重连一次
if (getReadyState() != ReadyState.OPEN) {
reconnectBlocking();
Thread.sleep(5 * 1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
log.info("与云服务器成功建立WS连接");
}
@Override
public void onMessage(String s) {
log.info("接收到来自服务端的消息{}",s);
ReplyMsg replyMsg = JSONUtil.toBean(s, ReplyMsg.class);
map.put("sid",replyMsg.getSid());
}
@Override
public void onClose(int code, String reason, boolean remote) {
log.warn("readyState:" + getReadyState() + "," + code + "-reason:" + reason + ",与云服务器断开了连接");
}
@Override
public void onError(Exception e) {
log.error("webSocket发生异常: {}:{}", e.getClass().getName(), e.getMessage());
close();
}
}
onMessage是接收来自服务端的消息,如果需要向服务端发送消息则可以使用new一个webScoket客户端与服务端立ws链接
MyWebSocketClient client = new MyWebSocketClient(new URI(this.frontUrl), this.map);
client.connect();
while (!client.isOpen()) {
Thread.sleep(100);
}
log.info("连接成功!!!!!!!!!!!!!!");
//1.发送opt=0的初始数据
DotMsg dotMsg = opt0();
String msg = JSONUtil.toJsonStr(dotMsg);
log.info("发送消息为{}",msg);
client.send(msg);
2.服务端使用netty是基于NIO实现
导入pom依赖
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.79.Final</version> </dependency>
@Component
public class WsServer {
@Value("${netty.port:8088}")
private int port;
@Value("${netty.bossThread:1}")
private int bossThread;
@Value("${netty.workThread:1}")
private int workThread;
@Autowired
private DotMsgHandler msgHandler;
public void start() {
NioEventLoopGroup bossGroup = null;
NioEventLoopGroup workGroup = null;
try {
log.info("Starting Websocket Server...");
// 创建两个线程组,bossGroup为接收请求的线程组,一般1-2个就行
bossGroup = new NioEventLoopGroup(this.bossThread);
workGroup = new NioEventLoopGroup(this.workThread);
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workGroup)
// 指定使用的channel
.channel(NioServerSocketChannel.class)
// 设置连入服务端的 Client 的 SocketChannel 的处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
// 添加一个http的编解码器,HttpRequestDecoder和HttpResponseEncoder的组合,针对http协议进行编解码
pipeline.addLast(new HttpServerCodec());
// 添加一个用于支持大数据流的支持,分块对客户端发送数据,防止发送大文件时导致内存溢出,channel.write(new ChunkedFile("test.mkv"))
pipeline.addLast(new ChunkedWriteHandler());
// 添加一个聚合器,这个聚合器主要是将HttpMessage和HttpContents聚合到一个完成得FullHttpRequest/FullHttpResponse中,
pipeline.addLast(new HttpObjectAggregator(1024 * 64));
// 聚合websocket的数据帧,因为客户端可能分段向服务端发送数据
pipeline.addLast(new WebSocketFrameAggregator(10 * 1024 * 1024));
// 需要指定接收请求的路由,服务端向外暴露的web socket端点,当客户端传比较大的对象时,maxFrameSize参数的值需调大
pipeline.addLast(new WebSocketServerProtocolHandler("/dot", null, true, 10485760));
// 超时机制
pipeline.addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS));
// 自定义处理器,处理web socket文本消息
pipeline.addLast(new TextHandler(msgHandler));
// 自定义处理器,处理web socket二进制消息
//pipeline.addLast(new BinaryHandler(msgHandler));
}
});
//使用netty应用需要指定服务为异步操作
ChannelFuture channelFuture = bootstrap.bind(this.port).sync();
log.info("Websocket Server Started on port: {}", this.port);
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("websocket InterruptedException!", e);
} finally {
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
TextHandler是自定义消息处理器需要继SimpleChannelInboundHandler<TextWebSocketFrame>这个类才能作为处理器,重写其中的方法
/**
* 客户端与服务器建立连接的时候触发
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("建立连接,channel:{}", ctx.channel().id().toString());
dotMsgHandler.connected(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info("断开连接,channel:{}", ctx.channel().id().toString());
dotMsgHandler.disconnected(ctx);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) throws Exception {
//log.info("channel {} 收到text数据:{}", ctx.channel().id().toString(), textWebSocketFrame.text());
//客户端会回复信息
ctx.channel().writeAndFlush(new TextWebSocketFrame(textWebSocketFrame.text()));
}