Netty簡(jiǎn)單的入門(mén)代碼示例
前言
純Java幾種網(wǎng)絡(luò)IO的開(kāi)發(fā)步驟及示例,毫無(wú)疑問(wèn)最好的就是NIO,這也是目前最主流的方式,但是這玩意編寫(xiě)復(fù)雜,拓展性也不強(qiáng),在通信上方方面面都需要重寫(xiě),這不是一般人能搞得定了,所以呀我們得會(huì)用框架呀,Netty就是這方面框架中的佼佼者!
基礎(chǔ)示例入門(mén)
服務(wù)端
NettyServer.class
@Slf4j
public class NettyServer{
//1.創(chuàng)建線程組 bossGroup:連接線程 workGroup:工作線程
private final NioEventLoopGroup bossGroup = new NioEventLoopGroup();
private final NioEventLoopGroup workerGroup = new NioEventLoopGroup();
public void serverStart() throws InterruptedException {
try{
// 服務(wù)端啟動(dòng)類(lèi)
ServerBootstrap bootstrap = new ServerBootstrap();
// 傳入兩個(gè)線程組
bootstrap.group(bossGroup, workerGroup)
// 指定Channel 和NIO一樣是采用Channel通道的方式通信 所以需要指定服務(wù)端通道
.channel(NioServerSocketChannel.class)
//使用指定的端口設(shè)置套接字地址
.localAddress(new InetSocketAddress(11111))
//服務(wù)端可連接隊(duì)列數(shù),對(duì)應(yīng)TCP/IP協(xié)議listen函數(shù)中backlog參數(shù)
.option(ChannelOption.SO_BACKLOG, 1024)
//設(shè)置數(shù)據(jù)處理器
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
// 在管道中 添加數(shù)據(jù)處理類(lèi)
channel.pipeline().addLast(new NettyServerTestHandler());
}
});
// 同步等待成功
ChannelFuture future = bootstrap.bind().sync();
if (future.isSuccess()) {
log.info("啟動(dòng) Netty Server 成功");
}
//等待服務(wù)端監(jiān)聽(tīng)端口關(guān)閉 鏈路關(guān)閉后main函數(shù)才會(huì)結(jié)束
future.channel().closeFuture().sync();
}finally {
// 優(yōu)雅的關(guān)閉 釋放資源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new NettyServer().serverStart();
}
}看上去好多,感覺(jué)比NIO還麻煩?其實(shí)很方便的:
- 實(shí)例化一個(gè)服務(wù)端 ServerBootstrap ,這個(gè)NIO也一樣,Netty幫我們封裝了
- 設(shè)置兩個(gè)線程組,一個(gè)處理客戶(hù)端連接事件,一個(gè)處理連接后數(shù)據(jù)處理事件
- 指定服務(wù)端通道類(lèi)型,并綁定地址
- 設(shè)置傳輸?shù)囊恍┡渲脜?shù)(這里有很多可以設(shè)置,所以拓展性強(qiáng))
- 設(shè)置數(shù)據(jù)處理器,這里實(shí)際是添加了一個(gè)數(shù)據(jù)處理管道,管道內(nèi)可以有很多數(shù)據(jù)處理類(lèi),所以可以一層一層處理,可插拔式設(shè)計(jì)(類(lèi)似攔截器鏈)
channel.pipeline() : 這就是管道 new NettyServerTestHandler() : 這個(gè)就是管道里的一個(gè)數(shù)據(jù)處理類(lèi) // 數(shù)據(jù)處理類(lèi)可以有多個(gè)
- 最后啟動(dòng)
ServerBootstrap 實(shí)例化后,都是采用建造者模式設(shè)置的,對(duì)于我們來(lái)說(shuō)是非常的方便的,這里配置好后我們的重心就可以放在數(shù)據(jù)處理類(lèi)上了
NettyServerTestHandler.class
NettyServerTestHandler 就是數(shù)據(jù)處理類(lèi),所有的方法都幫我們封裝好了,我們不需要考慮其中調(diào)用的問(wèn)題,方法是處理什么事件的,我們寫(xiě)對(duì)應(yīng)的邏輯就好了,方法上的ChannelHandlerContext 可以理解為管道中所有數(shù)據(jù)處理類(lèi)的紐帶,比如攔截器鏈不也有么
@Slf4j
public class NettyServerTestHandler extends ChannelInboundHandlerAdapter {
// 讀取信息調(diào)用
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 和NIO一樣有緩沖區(qū) ByteBuf就是對(duì)ByteBuffer做了一層封裝
ByteBuf msg1 = (ByteBuf) msg;
log.info("客戶(hù)端信息:" + msg1.toString(CharsetUtil.UTF_8));
}
// 連接事件 連接成功調(diào)用
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
SocketAddress socketAddress = ctx.channel().remoteAddress();
log.info(socketAddress + " 已連接");
// 發(fā)送數(shù)據(jù)
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Client", CharsetUtil.UTF_8));
}
// 斷開(kāi)連接調(diào)用
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info(ctx.channel().remoteAddress() + " 已斷開(kāi)連接");
}
// 讀取信息完成事件 信息讀取完成后調(diào)用
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
// 異常處理 發(fā)生異常調(diào)用
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 異常后 關(guān)閉與客戶(hù)端連接
ctx.close();
}
}客戶(hù)端
NettyClient.class
客戶(hù)端啟動(dòng)和服務(wù)端幾乎一致,只不過(guò)啟動(dòng)類(lèi)成了Bootstrap ,而且我還加入了一個(gè)斷連邏輯
@Slf4j
public class NettyClient {
private EventLoopGroup group = new NioEventLoopGroup();
private int port=11111;
private String host="127.0.0.1";
public void start() throws InterruptedException {
try{
Bootstrap bootstrap = new Bootstrap();
// 客戶(hù)端不需要處理連接 所以一個(gè)線程組就夠了
bootstrap.group(group)
// 連接通道
.channel(NioSocketChannel.class)
.remoteAddress(host, port)
.option(ChannelOption.TCP_NODELAY, true)
// 數(shù)據(jù)處理
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
channel.pipeline().addLast(new NettyClientTestHandler());
}
});
ChannelFuture future = bootstrap.connect();
//客戶(hù)端斷線重連邏輯
future.addListener((ChannelFutureListener) future1 -> {
if (future1.isSuccess()) {
log.info("連接Netty服務(wù)端成功");
} else {
log.info("連接失敗,進(jìn)行斷線重連");
future1.channel().eventLoop().schedule(() -> {
try {
start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 20, TimeUnit.SECONDS);
}
});
future.channel().closeFuture().sync();
}catch (Exception e){
log.info("服務(wù)端異常");
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new NettyClient().start();
}
}NettyClientTestHandler.class
數(shù)據(jù)處理類(lèi)也是一樣的
@Slf4j
public class NettyClientTestHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf msg1 = (ByteBuf) msg;
log.info("服務(wù)端信息:" + msg1.toString(CharsetUtil.UTF_8));
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 連接上 就給服務(wù)端發(fā)送數(shù)據(jù)
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Server", CharsetUtil.UTF_8));
SocketAddress socketAddress = ctx.channel().remoteAddress();
log.info(socketAddress + " 已連接");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info(ctx.channel().remoteAddress() + " 已斷開(kāi)連接");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}到此這篇關(guān)于Netty簡(jiǎn)單的入門(mén)代碼示例的文章就介紹到這了,更多相關(guān)Netty入門(mén)代碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java異常繼承何類(lèi),運(yùn)行時(shí)異常與一般異常的區(qū)別(詳解)
下面小編就為大家?guī)?lái)一篇java異常繼承何類(lèi),運(yùn)行時(shí)異常與一般異常的區(qū)別(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
MybatisPlus調(diào)用原生SQL的三種方法實(shí)例詳解
這篇文章主要介紹了MybatisPlus調(diào)用原生SQL的三種方法,在有些情況下需要用到MybatisPlus查詢(xún)?cè)鶶QL,MybatisPlus其實(shí)帶有運(yùn)行原生SQL的方法,我這里列舉三種,需要的朋友可以參考下2022-09-09
spring源碼閱讀--aop實(shí)現(xiàn)原理講解
這篇文章主要介紹了spring源碼閱讀--aop實(shí)現(xiàn)原理講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
java線程池合理設(shè)置最大線程數(shù)和核心線程數(shù)方式
這篇文章主要介紹了java線程池合理設(shè)置最大線程數(shù)和核心線程數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
JAVA實(shí)現(xiàn)簡(jiǎn)單系統(tǒng)登陸注冊(cè)模塊
這篇文章主要介紹了一個(gè)簡(jiǎn)單完整的登陸注冊(cè)模塊的實(shí)現(xiàn)過(guò)程,文章條理清晰,在實(shí)現(xiàn)過(guò)程中加深了對(duì)相關(guān)概念的理解,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-07-07
關(guān)于SpringSecurity認(rèn)證邏輯源碼分析
這篇文章主要介紹了關(guān)于SpringSecurity認(rèn)證邏輯源碼分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07

