咱们的学习思路比较奇特,看到啥学啥。
先将为什么要用Websocker.因为Http只能由客户端发送给服务端。而没办法相互通信,而Websocker解决了这个问题。
那Spring boot有自带的WebSocker集成,那为啥不用呢?因为Netty比较牛逼(建议自行百度)。本人理解就是,它可以变成所有的协议服务,用自定义的操作很方便又简易的开发方式,Netty成功地找到了一种在不妥协的情况下实现易于开发,性能,稳定性和灵活性的方法。
这里只做服务器的代码,客户端的测试,可以去找一下测试工具!!!
你可以偷偷用,不要告诉别人喔~
好,然后到了我们的实现步骤,
1.添加依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.21.Final</version>
</dependency>
2.构造Netty服务初始器
/**
* @author
*
* netty服务初始化器
* 主要是配置业务的前置信息
* 比如像和客户端通信,指定相应的通信协议,编码解码信息,自定义相关的handler
**/
public class NettyServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//添加编解码
socketChannel.pipeline().addLast(new HttpServerCodec());
//添加对于读写大数据流的支持
socketChannel.pipeline().addLast(new ChunkedWriteHandler());
//对httpMessage进行聚合
socketChannel.pipeline().addLast(new HttpObjectAggregator(1024*64));
// WebSocketServerProtocolHandler:将 http 协议升级为 ws 协议,保持长连接
socketChannel.pipeline().addLast(new WebSocketServerProtocolHandler("/ws"));
// 增加IdleStateHandler心跳检测处理器,userEventTriggered()方法作为超时事件,如果五秒内ChannelRead()方法未被调用
socketChannel.pipeline().addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
//增加一个自己的Handler操作
socketChannel.pipeline().addLast(new NettyServerHandler());
}
}
可以在代码看到,里面添加了很多类进去,注意倒数三个,一个是将http变成Websock,一个是心跳检测器,是哪个参数分别对应,客户端的写操作超时时间,读操作超时时间,全部操作超时时间,第三个是我们自定义的业务Handler。埋个伏笔(关于编解码的问题,只知道概念,不知道会有什么影响…)
3.新建Handler类
/**
* @author
*
* WebSocketFrame: 表示一个文本帧
* netty服务端处理器
**/
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
/**
* 客户端连接会触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("Channel active......");
}
/**
* 客户端发消息会触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.info("服务器收到消息: {}", msg.toString());
if (msg instanceof TextWebSocketFrame) {
TextWebSocketFrame textMsg = (TextWebSocketFrame) msg;
logger.info("服务器收到文本消息: {}", textMsg.text());
}
//ctx.channel().write(new TextWebSocketFrame("abc"));
ctx.flush();
}
/**
* 发生异常触发
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
//客户端销毁的时候触发,
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
//当handlerRemoved 被触发时候,channelGroup会自动移除对应的channel
//clients.remove(ctx.channel());
System.out.println("客户端断开,当前被移除的channel的短ID是:" + ctx.channel().id().asShortText());
}
private int lossConnectCount = 0;
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception {
if (obj instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) obj;
//这就是客户端不写过来
if (event.state() == IdleState.READER_IDLE) {
logger.info("客户端写超时");
lossConnectCount++;
if(lossConnectCount > 2){
logger.info("关闭这个不活跃的通道");
ctx.channel().close();
}
//这是客户端不读
} else if (event.state() == IdleState.WRITER_IDLE) {
logger.info("客户端读超时");
//如果都没操作就都无
} else if (event.state() == IdleState.ALL_IDLE) {
logger.info("客户端所有操作超时");
}
}
}
}
重点都在注释里面了。。我就不讲解了
4.设置服务并且开启
新建NettyServer类
/**
* @author
* <p>
* 服务启动监听器
**/
public class NettyServer {
private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
public void start(InetSocketAddress socketAddress) {
//new 一个主线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//new 一个工作线程组
EventLoopGroup workGroup = new NioEventLoopGroup(200);
ServerBootstrap bootstrap = new ServerBootstrap()
.group(bossGroup, workGroup)
//设置通道
.channel(NioServerSocketChannel.class)
//设置子处理器
.childHandler(new NettyServerChannelInitializer())
.localAddress(socketAddress)
//设置队列大小
.option(ChannelOption.SO_BACKLOG, 1024)
// 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
.childOption(ChannelOption.SO_KEEPALIVE, true);
//绑定端口,开始接收进来的连接
try {
ChannelFuture future = bootstrap.bind(socketAddress).sync();
logger.info("服务器启动开始监听端口: {}", socketAddress.getPort());
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//关闭主线程组
bossGroup.shutdownGracefully();
//关闭工作线程组
workGroup.shutdownGracefully();
}
}
}
在启动类里面加入这个方法
public static void main(String[] args) {
SpringApplication.run(DevApplication.class, args);
//1.判断库是否存在
//2.建库.
//3.判断表是否存在,不存在建立表,存在则更新数据库表结构
//4.表建立完之后,进行外键的连接。
//启动服务端
NettyServer nettyServer = new NettyServer();
nettyServer.start(new InetSocketAddress("172.31.97.13", 6088));
}
我们来看看结果
Nice,Bro