承接上一章,接下來開始進(jìn)入Netty入門,本文所采用的Netty版本號是5.0州弟,這個請大家注意下钧栖。
還是上一個場景,一個簡單的客戶端和服務(wù)端直接發(fā)送字符串的程序婆翔,如果使用Netty框架如何展示呢拯杠?廢話不多說,直接上代碼啃奴。
服務(wù)端:
package com.dlb.note.server;
importio.netty.bootstrap.ServerBootstrap;
importio.netty.buffer.ByteBuf;
importio.netty.buffer.Unpooled;
importio.netty.channel.*;
importio.netty.channel.nio.NioEventLoopGroup;
importio.netty.channel.socket.SocketChannel;
importio.netty.channel.socket.nio.NioServerSocketChannel;
/**
* 功能:時間服務(wù)器
* 版本:1.0
* 日期:2016年12月8日14:58:06
* 作者:馟蘇
*/
public classTimeServer {
/**
* 主函數(shù)
*/
public static voidmain(String []args) {
// 配置服務(wù)端的NIO線程池
EventLoopGroup bossGroup =newNioEventLoopGroup();
EventLoopGroup workerGroup =newNioEventLoopGroup();
try{
ServerBootstrap serverBootstrap =newServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
// 當(dāng)服務(wù)器請求處理線程全滿時潭陪,用于臨時存放已完成三次握手的請求的隊(duì)列的最大長度
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(newChildChannelHandler());
// 綁定端口,同步等待成功
ChannelFuture future = serverBootstrap.bind(8888).sync();
System.out.println("服務(wù)器在8888端口監(jiān)聽hello");
// 等待服務(wù)端監(jiān)聽端口關(guān)閉
future.channel().closeFuture().sync();
System.out.println("服務(wù)器關(guān)閉bye");
}catch(Exception e) {
e.printStackTrace();
}finally{
// 優(yōu)雅退出最蕾,釋放線程池資源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
classChildChannelHandlerextendsChannelInitializer {
@Override
protected voidinitChannel(SocketChannel socketChannel)throwsException {
socketChannel.pipeline().addLast(newTimerServerHandler());
}
}
classTimerServerHandlerextendsChannelHandlerAdapter {
// 可讀
@Override
public voidchannelRead(ChannelHandlerContext ctx,Object msg)throwsException {
// 讀數(shù)據(jù)
ByteBuf buf = (ByteBuf) msg;
byte[] req =new byte[buf.readableBytes()];
buf.readBytes(req);
String body =newString(req,"UTF-8");
System.out.println("receive:"+ body);
// 寫數(shù)據(jù)
ByteBuf res = Unpooled.copiedBuffer("hello,client!".getBytes());
ctx.write(res);
ctx.flush();
}
// 連接
@Override
public voidchannelActive(ChannelHandlerContext ctx)throwsException {
System.out.println("client come,ip:"+ ctx.channel().remoteAddress());
}
// 關(guān)閉
@Override
public voidchannelInactive(ChannelHandlerContext ctx)throwsException {
System.out.println("client close,ip:"+ ctx.channel().remoteAddress());
ctx.close();
}
// 異常
@Override
public voidexceptionCaught(ChannelHandlerContext ctx,Throwable cause)throwsException {
System.out.println(cause.toString());
ctx.close();
}
}
現(xiàn)在來分析一下:首先服務(wù)端做了兩個線程池依溯,一個是boss線程池,一個worker線程瘟则,這是Netty框架給我們提供的黎炉。并且在使用完畢后,可以優(yōu)雅的關(guān)閉醋拧。然后做一個服務(wù)器啟動引導(dǎo)serverBootStrap慷嗜,我們可以設(shè)置TCP連接的屬性,Netty給我們提供了很多屬性丹壕。接下來庆械,注冊管道,設(shè)置處理器菌赖,綁定端口監(jiān)聽缭乘。
其中比較重要的是處理器,我們可以這么考慮盏袄,Netty為我們提供了一個管道忿峻,管道中存在許多個處理器。一個客戶端鏈接對應(yīng)著一個管道辕羽。這樣我們就可以把處理邏輯寫在處理器中逛尚。在處理器中我們主要關(guān)心這么幾個方法,可讀:channelRead刁愿、鏈接到來:channelActive绰寞、鏈接斷開:channelInactive、異常:exceptionCaught铣口。并且在鏈接關(guān)閉后滤钱,直接通過ctx.close();可以將關(guān)聯(lián)的通道等都關(guān)閉,并且釋放資源脑题。
客戶端:
package com.dlb.note.client;
importio.netty.bootstrap.Bootstrap;
importio.netty.buffer.ByteBuf;
importio.netty.buffer.Unpooled;
importio.netty.channel.*;
importio.netty.channel.nio.NioEventLoopGroup;
importio.netty.channel.socket.nio.NioSocketChannel;
/**
* 功能:支持tcp粘包/拆包的時間客戶端
* 版本:1.0
* 日期:2016/12/9 15:40
* 作者:馟蘇
*/
public classTimeClient {
/**
* main函數(shù)
*@paramargs
*/
public static voidmain(String []args) {
EventLoopGroup group =newNioEventLoopGroup();
try{
Bootstrap bootstrap =newBootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(newChannelInitializer() {
protected voidinitChannel(Channel channel)throwsException {
channel.pipeline().addLast(newMyHandler());
}
});
// 等待客戶端鏈接成功
ChannelFuture future = bootstrap.connect("localhost",8888).sync();
System.out.println("客戶端鏈接成功件缸!");
// 等待客戶端鏈接關(guān)閉
future.channel().closeFuture().sync();
}catch(Exception e) {
e.printStackTrace();
}finally{
group.shutdownGracefully();
}
}
}
classMyHandlerextendsChannelHandlerAdapter {
@Override
public voidchannelActive(ChannelHandlerContext ctx)throwsException {
for(inti =0;i <1000;i++) {
ByteBuf msg = Unpooled.copiedBuffer(("你好啊,服務(wù)器叔遂,我是revoid八丁争剿!$_".getBytes()));
ctx.writeAndFlush(msg);
}
ctx.close();
super.channelActive(ctx);
}
}
客戶端也是非常簡單的,首先創(chuàng)建一個線程池痊末,然后注冊管道蚕苇,處理器,在鏈接服務(wù)器成功后凿叠,給服務(wù)器發(fā)送消息涩笤。
通過上述介紹,我們可以發(fā)現(xiàn)盒件,通過Netty框架編寫一個簡單的客戶端和服務(wù)端通信程序蹬碧,只需要寥寥幾行就可以實(shí)現(xiàn)。但是仍然還有一個問題沒有解決炒刁,那就是沒有解決锰茉,TCP的粘包和分包問題,這個問題切心,將會在下一章介紹飒筑。