注:使用netty之前,需要先引入netty包,不同的框架,引入netty包的方式不同
1.定义NettyServer类
用于初始化netty连接
public class NettyServer {
public void start() throws Exception {
System.out.println("启动记载netty");
EventLoopGroup boosGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap(); // 引导类ServerBootstrap,这个类将引导服务端的启动工作
serverBootstrap
.group(boosGroup, workerGroup) // .group(bossGroup,workerGroup)给引导类配置两大线程组
.channel(NioServerSocketChannel.class) // 指定服务端的IO模型为NIO NioServerSocketChannel是对NIO类型连接的抽象
.option(ChannelOption.SO_BACKLOG, 1024) // 表示系统用于临时存放已完成三次握手的请求的队列的最大长度
.childOption(ChannelOption.SO_KEEPALIVE, true) // 表示是否开启TCP底层心跳机制,true表示开启。
.childOption(ChannelOption.TCP_NODELAY, true) // 表示是否开启Nagle算法,true表示关闭,false表示开启
.handler(new ChannelInitializer<NioServerSocketChannel>() { // handler()方法用于指定在服务端启动过程中的一些逻辑
@Override
protected void initChannel(NioServerSocketChannel ch) {
System.out.println("服务端启动过程中...");
}
})
.childHandler(new ChannelInitializer<NioSocketChannel>() { // childHandler()方法,给这个引导类创建一个ChannelInitializer,主要是定义后续每个连接的数据读写
protected void initChannel(NioSocketChannel ch) { // 泛型参数NioSocketChannel,这个类就是Netty对NIO类型连接的抽象
ch.pipeline().addLast(new IdleStateHandler(0, 0, 15));//检测读写空闲事件,需要就开启,不需要就关闭
ch.pipeline().addLast(new EchoServerHandler());
}
});
System.out.println("启动加载netty2");
// 给这个ChannelFuture添加一个监听器GenericFutureListener
serverBootstrap.bind(9997).addListener(future -> {
if (future.isSuccess()) {
System.out.println(new Date() + ": 端口[" + 9997 + "]绑定成功!");
} else {
System.err.println("端口[" + 9997 + "]绑定失败!");
}
});
}
}
2.编写EchoServerHandler
用于netty接收到数据后处理数据
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
/**
* 读取客户端发送过来的数据
* @param ctx 上下文对象
* @param msg 客户端发送的数据
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg){
ByteBuf byteBuf= (ByteBuf) msg;
String req=byteBuf.toString(CharsetUtil.UTF_8);
System.out.println("接收到客户端请求:"+req);
System.out.println("客户端地址: " + ctx.channel().remoteAddress());
/**String res= "服务器响应["+ LocalDateTime.now().format(DateTimeFormatter.ISO_DATE)+" "
+LocalDateTime.now().format(DateTimeFormatter.ISO_TIME)+"]:"+req;
System.out.println("服务端响应:"+res);**/
}
/**
* 数据读取完毕
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx){
ctx.writeAndFlush(Unpooled.copiedBuffer("服务器响应[收到消息,谢谢]",CharsetUtil.UTF_8));
}
/**
* 处理异常, 一般是需要关闭通道
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
cause.printStackTrace();
ctx.close();
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.WRITER_IDLE) {
ctx.channel().writeAndFlush("Heartbeat" + System.getProperty("line.separator"));
}else if(event.state() == IdleState.ALL_IDLE){//如果长时间空闲,则关闭链接
System.out.println("链接长时间没有数据,关闭连接");
ctx.channel().close();
}
}
}
}
3.使用netty
public class Main {
public static void main(String[] args) {
NettyServer server = new NettyServer();
try{
server.start();
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}