说明:此demo不考虑粘包和半包情况
Netty依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha1</version>
</dependency>
<!--其他可能用到的依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
1. 服务器编写
NettyServer
@Slf4j
public class NettyServer {
public static void main(String[] args) {
int port = 8080;
// 创建链各个Reactor线程租,一个用于服务端接受客户端连接
// 一个用于SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// netty 用于启动NIO服务端的辅助启动类,不低降低服务端开发难度
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)//配置NioServerSocketChannel 的TCP 参数
.childHandler(new ChildChannelHeandler());// 绑定I/O事件处理类 ChildChannelHeandler
log.info("服务器启动" + "端口 {}", port);
// 绑定端口,调用同步阻塞等待成功
ChannelFuture f = b.bind(port).sync();
//等待服务端监听端口关闭
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
class ChildChannelHeandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new TimeServerHandler());
}
}
TimeServerHandler
/**
* @author WH
* @version 1.0
* @date 2020/5/24 21:48
* @Description 继承ChannelHandlerAdapter 对网络事件进行读写
*/
@Slf4j
public class TimeServerHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
System.out.println("111111111111");
//将msg转换成Netty的ByteBuf对象
ByteBuf buf = (ByteBuf) msg;
//buf.readableBytes()获取缓冲区可读的字节数
byte[] req = new byte[buf.readableBytes()];
// 将缓冲区的字节数组复制到新的byte数组中
buf.readBytes(req);
String body = new String(req, "UTF-8");
//请求信息
log.info("客户端请求参数boyd: {}", body);
String message = "服务端收到了你的消息" + body;
ByteBuf resp = Unpooled.copiedBuffer(message.getBytes());
ctx.write(resp);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception{
//将消息发送队列中的消息写入到SocketChannel中发送给客户端
ctx.flush();
}
// 客户端断开连接监听
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.info("客户端断开了连接");
ctx.close();
}
}
客户端
NettyClient
public class NettyClient {
public static void main(String[] args) {
//配置客户端NIO线程租
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception{
ch.pipeline().addLast(new TimeClinetHandler());
}
});
//发起异步连接操作
ChannelFuture f = b.connect("127.0.0.1", 8080).sync();
//等待客户端链路关闭
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//优雅退出,释放NIO线程租
group.shutdownGracefully();
}
}
}
TimeClinetHandler
@Slf4j
public class TimeClinetHandler extends ChannelHandlerAdapter {
private final ByteBuf firstMessage;
public TimeClinetHandler() {
byte[] req = "你好服务器".getBytes();
firstMessage = Unpooled.buffer(req.length);
firstMessage.writeBytes(req);
log.info("发送消息 req: {}", new java.lang.String(req));
}
//当服务器TCP链路建立成功后,调用 channelActive 方法
@Override
public void channelActive(ChannelHandlerContext ctx) {
log.info("开始发送消息");
ctx.writeAndFlush(firstMessage);
}
//当服务器返回应答消息时,调用 channelRead 方法
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
log.info("服务器返回消息为" + body);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.warn("发生异常" + cause.getMessage());
ctx.close();
}
}
运行测试
服务器端
客户端启动