Netty 簡易實(shí)戰(zhàn),傻瓜都能看懂!
點(diǎn)擊關(guān)注公眾號,Java干貨及時送達(dá)
作者:rickiyang
出處:www.cnblogs.com/rickiyang/p/11074237.html
這一節(jié)我們來講解Netty,使用Netty之前我們先了解一下Netty能做什么,無為而學(xué),豈不是白費(fèi)力氣!
1.使用Netty能夠做什么
開發(fā)異步、非阻塞的TCP網(wǎng)絡(luò)應(yīng)用程序; 開發(fā)異步、非阻塞的UDP網(wǎng)絡(luò)應(yīng)用程序; 開發(fā)異步文件傳輸應(yīng)用程序; 開發(fā)異步HTTP服務(wù)端和客戶端應(yīng)用程序; 提供對多種編解碼框架的集成,包括谷歌的Protobuf、Jboss marshalling、Java序列化、壓縮編解碼、XML解碼、字符串編解碼等,這些編解碼框架可以被用戶直接使用; 提供形式多樣的編解碼基礎(chǔ)類庫,可以非常方便的實(shí)現(xiàn)私有協(xié)議棧編解碼框架的二次定制和開發(fā); 基于職責(zé)鏈模式的Pipeline-Handler機(jī)制,用戶可以非常方便的對網(wǎng)絡(luò)事件進(jìn)行攔截和定制; 所有的IO操作都是異步的,用戶可以通過Future-Listener機(jī)制主動Get結(jié)果或者由IO線程操作完成之后主動Notify結(jié)果,用戶的業(yè)務(wù)線程不需要同步等待; IP黑白名單控制; 打印消息碼流; 流量控制和整形; 性能統(tǒng)計(jì); 基于鏈路空閑事件檢測的心跳檢測
2. Netty常用類講解
在這里我們就一些我們常用到的類做大致的講解,然后再寫入門程序的時候大致知道每一行都講了什么。
EventLoop,EventLoopGroup
EventLoop目的是為Channel處理IO操作,一個EventLoop可以為多個Channel服務(wù),EventLoopGroup會包含多個EventLoop。
BootStrap,ServerBootstrap
一個Netty應(yīng)用通常由一個Bootstrap開始,它主要作用是配置整個Netty程序,串聯(lián)起各個組件。分享:Spring Boot 學(xué)習(xí)筆記。
ChannelInitializer
當(dāng)一個鏈接建立時,我們需要知道怎么來接收或者發(fā)送數(shù)據(jù),當(dāng)然,我們有各種各樣的Handler實(shí)現(xiàn)來處理它,那么ChannelInitializer便是用來配置這些Handler,它會提供一個ChannelPipeline,并把Handler加入到ChannelPipeline。
Handler
為了支持各種協(xié)議和處理數(shù)據(jù)的方式,便誕生了Handler組件。Handler主要用來處理各種事件,這里的事件很廣泛,比如可以是連接、數(shù)據(jù)接收、異常、數(shù)據(jù)轉(zhuǎn)換等。
ChannelInboundHandler
一個最常用的Handler。這個Handler的作用就是處理接收到數(shù)據(jù)時的事件,也就是說,我們的業(yè)務(wù)邏輯一般就是寫在這個Handler里面的,ChannelInboundHandler就是用來處理我們的核心業(yè)務(wù)邏輯。
Future
在Netty中所有的IO操作都是異步的,因此,你不能立刻得知消息是否被正確處理,但是我們可以過一會等它執(zhí)行完成或者直接注冊一個監(jiān)聽,具體的實(shí)現(xiàn)就是通過Future和ChannelFutures,他們可以注冊一個監(jiān)聽,當(dāng)操作執(zhí)行成功或失敗時監(jiān)聽會自動觸發(fā)。總之,所有的操作都會返回一個ChannelFuture。
3. 第一個Helloworld
上面我們已經(jīng)對常用類進(jìn)行說明,下面我們就使用這些類來構(gòu)建我們的第一個入門程序,本示例我使用的是maven來構(gòu)建工程,如果你使用的是普通的項(xiàng)目則跳過第一步。另外,微信搜索Java技術(shù)棧,在后臺回復(fù):Maven,獲取系列 Maven 教程。
首先引入maven jar包:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.5.Final</version>
</dependency>
public class HelloWorldClient {
private int port;
private String address;
public HelloWorldClient(int port,String address) {
this.port = port;
this.address = address;
}
public void start(){
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ClientChannelInitializer());
try {
Channel channel = bootstrap.connect(address,port).sync().channel();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
for(;;){
String msg = reader.readLine();
if(msg == null){
continue;
}
channel.writeAndFlush(msg + "\r\n");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
HelloWorldClient client = new HelloWorldClient(7788,"127.0.0.1");
client.start();
}
}
ChannelInitializer用來配置處理數(shù)據(jù)的handler:
public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
/*
* 這個地方的 必須和服務(wù)端對應(yīng)上。否則無法正常解碼和編碼
*
* 解碼和編碼 我將會在下一節(jié)為大家詳細(xì)的講解。暫時不做詳細(xì)的描述
*
* /
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 我們自己的handler
pipeline.addLast("handler", new HelloWorldClientHandler());
}
}
寫一個我們自己的handler,用自己的方式來處理數(shù)據(jù):
public class HelloWorldClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server say : "+msg.toString());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client is active");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client is close");
}
}
public class HelloWordServer {
private int port;
public HelloWordServer(int port) {
this.port = port;
}
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap server = new ServerBootstrap().group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInitializer());
try {
ChannelFuture future = server.bind(port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
HelloWordServer server = new HelloWordServer(7788);
server.start();
}
}
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
// 字符串解碼 和 編碼
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 自己的邏輯Handler
pipeline.addLast("handler", new HelloWordServerHandler());
}
}
public class HelloWordServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(ctx.channel().remoteAddress()+"===>server: "+msg.toString());
ctx.write("received your msg");
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
ctx.close();
}
}
上面服務(wù)器端和客戶端的代碼都已經(jīng)寫完,下面我們先啟動服務(wù)端,然后啟動客戶端,程序中我是在客戶端讓手動輸入,輸入結(jié)束之后回車,服務(wù)器端即可接受數(shù)據(jù)。
客戶端:

服務(wù)端:







關(guān)注Java技術(shù)棧看更多干貨


