注:更多netty相關文章請訪問博主專欄: netty專欄
概述
本系列文章介紹netty的學習。使用的版本是4.1.45
jdk採用Java11
本系列文章由淺入深,先學習使用,再研究其實現原理。
本節編寫一個最簡單的netty服務器。
maven依賴
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.45.Final</version>
</dependency>
服務端
package com.example;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class MyNettyServer {
public static void main(String[] args) {
//配置服務端NIO線程組
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)//配置主從線程組
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)//配置一些TCP的參數
.childHandler(new MyChildHandler());//添加自定義的channel
//綁定8080端口
ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
//服務端監聽端口關閉
ChannelFuture future = channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
//netty優雅停機
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
class MyChildHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) {
//將自定義的channelhandler添加到channelpipeline的末尾
socketChannel.pipeline().addLast(new TimeServerHandler());
}
}
/**
* TimeServerHandler這個纔是服務端真正處理請求的服務方法
*/
class TimeServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//將請求入參轉爲ByteBuf對象
ByteBuf byteBuf = (ByteBuf) msg;
byte[] bytes = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(bytes);
//由於我們這裏傳的參數是string,所以直接強制轉換
String body = new String(bytes, "UTF-8");
System.out.println("收到客戶端請求:" + body);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String currentTimeStr = "QUERY TIME ORDER".equalsIgnoreCase(body) ?
format.format(new Date()) + "" : "BAD ORDER";
ByteBuf resp = Unpooled.copiedBuffer(currentTimeStr.getBytes());
ctx.write(resp);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
}
}
這裏配置了NIO服務端,採用的是主從線程模型,關於主從模型後續再介紹。
服務端端口是8080.
服務端真正處理客戶端請求的方法是com.example.TimeServerHandler#channelRead
,該方法的作用是接收請求,返回服務器時間
。
客戶端
package com.example;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class MyNettyClient {
public static void main(String[] args) {
//客戶端NIO線程組
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new TimeClientHandler());
}
});
//異步鏈接服務器
ChannelFuture future = bootstrap.connect("127.0.0.1", 8080).sync();
//等等客戶端鏈接關閉
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
//優雅停機
group.shutdownGracefully();
}
}
}
//客戶端業務邏輯處理類
class TimeClientHandler extends ChannelInboundHandlerAdapter {
/**
* 客戶端與服務器TCP鏈路鏈接成功後調用該方法
* @param ctx
*/
@Override
public void channelActive(ChannelHandlerContext ctx) {
byte[] req = "QUERY TIME ORDER".getBytes();
ByteBuf firstMsg = Unpooled.buffer(req.length);
firstMsg.writeBytes(req);
ctx.writeAndFlush(firstMsg);//寫入緩衝並且發送到socketchannel
}
/**
* 讀取到服務端相應後執行該方法
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
byte[] bytes = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("服務端返回:"+body);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("Unexpected exception from downstream : " + cause.getMessage());
ctx.close();
}
}
運行結果
先啓動服務器端,再啓動客戶端。可以看到客戶端運行結果:
OK,第一個netty程序就完成了,上面代碼中有一些註釋幫助理解運行過程。後續會再寫更深入的文章。
注:更多netty相關文章請訪問博主專欄: netty專欄