使用netty实现消息收发
引入websocket包编写客户端
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.25.Final</version>
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.3.8</version>
</dependency>
服务端启动类
package com.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyServer {
public static void main(String[] args) throws Exception{
EventLoopGroup master= new NioEventLoopGroup();
EventLoopGroup slaver= new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap =new ServerBootstrap();
serverBootstrap.group(master,slaver)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerInitializer());
ChannelFuture channelFuture= serverBootstrap.bind(8088).sync();
channelFuture.channel().closeFuture().sync();
}finally {
// 关闭线程
master.shutdownGracefully();
slaver.shutdownGracefully();
}
}
}
初始化服务端
package com.netty;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
public class ServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline =socketChannel.pipeline();
pipeline.addLast("HttpServerCodec",new HttpServerCodec());
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpObjectAggregator(1024*64));
pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
pipeline.addLast(new CustomHandler());
}
}
服务端消息处理助手
package com.netty;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.time.LocalDateTime;
public class CustomHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private static ChannelGroup clients=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
String content =textWebSocketFrame.text();
System.out.println("接收到的数据:"+ content);
// 推送消息到所有客户端
for(Channel channel:clients){
channel.writeAndFlush(new TextWebSocketFrame("服务器在"+LocalDateTime.now()+"收到消息"+"'"+content+"'"));
}
}
// 客户端连接之后获取客户端channel并放入group中管理
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
clients.add( ctx.channel());
}
// 移除对应客户端的channel
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
// 长短id
System.out.println(ctx.channel().id().asLongText());
System.out.println(ctx.channel().id().asShortText());
clients.remove(ctx.channel());
}
}
客户端用于测试
package com.netty;
import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Scanner;
public class WebsocketClient {
public static WebSocketClient client;
public static void main(String[] args) {
try {
client = new WebSocketClient(new URI("ws://localhost:8088/ws"),new Draft_6455()) {
@Override
public void onOpen(ServerHandshake serverHandshake) {
System.out.println(("握手成功"));
}
@Override
public void onMessage(String msg) {
System.out.println(("客户端收到消息:"+msg));
}
@Override
public void onClose(int i, String s, boolean b) {
System.out.println("链接已关闭");
}
@Override
public void onError(Exception e){
e.printStackTrace();
System.out.println(("发生错误已关闭"));
}
};
} catch (URISyntaxException e) {
e.printStackTrace();
}
client.connect();
while(!client.getReadyState().equals(WebSocket.READYSTATE.OPEN)){
System.out.println(("正在连接..."));
}
while (true){
Scanner sc = new Scanner(System.in);
String x = sc.nextLine();
//连接成功,发送信息
client.send(x);
}
}
}
先启动服务端在启动客户端
客户端连接上了,发一条消息试试。
服务端收到了消息
服务端回复了消息。
多启动几个客户端,可以发现服务端在收到消息后会向所有客户端推送相同的消息。
springboot整合netty
修改之前的服务端启动类
package com.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyServer {
private static class SingletionNettyServer {
static final NettyServer instance=new NettyServer();
}
public static NettyServer getInstance(){
return SingletionNettyServer.instance;
}
private EventLoopGroup master;
private EventLoopGroup slaver;
private ServerBootstrap serverBootstrap;
private ChannelFuture channelFuture;
public NettyServer(){
master= new NioEventLoopGroup();
slaver= new NioEventLoopGroup();
serverBootstrap =new ServerBootstrap();
serverBootstrap.group(master,slaver)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerInitializer());
}
public void start(){
this.channelFuture= serverBootstrap.bind(8088);
System.err.println("netty 启动完毕");
}
}
新建一个组件让springboot帮我们启动netty
package com.netty;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Component
public class NettyBooter implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
if(contextRefreshedEvent.getApplicationContext().getParent()==null){
try {
NettyServer.getInstance().start();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
完成这两步之后就可以启动springboot了。
启动客户端发一条消息,效果和之前也是一样的。