【netty in action】学习笔记-第二章 编写你的第一个netty程序
这一章简单粗暴,整个章节都是讲一个例子,例子很简单,但是麻雀虽小五脏俱全。通过这个示例你会对编写基于netty的应用程序有个直观的认识。
我先上代码,后面再分析。
先看看服务端的示例,
public class EchoServer {
public int port;
public EchoServer(int port) {
this.port = port;
}
public void start() {
EventLoopGroup group = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoServerHandler());
}
});
ChannelFuture f = b.bind().sync();
System.out.println(EchoServer.class.getName() + "started and listen on " + f.channel().localAddress());
f.channel().closeFuture().sync();
}catch (Exception e) {
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
new EchoServer(8888).start();
}
}
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("netty rocks", CharsetUtil.UTF_8));
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf msg) throws Exception {
ByteBuf recieveMsg=(ByteBuf) msg;
String result = ByteBufUtil.hexDump(recieveMsg).toUpperCase();//将bytebuf中的可读字节 转换成16进制数字符串
String result2 = ByteBufUtil.hexDump(msg.readBytes(msg.readableBytes()));
//看下两种方式输出的结果有什么区别
System.out.println("client received:" + result);
System.out.println("client received:" + result2);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
然后是客户端的示例,
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture f = b.connect().sync();
f.channel().closeFuture().sync();
}catch (Exception e) {
}finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) {
String host = "127.0.0.1";
int port = 8888;
new EchoClient(host, port).start();
}
}
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.write(Unpooled.copiedBuffer("netty rocks", CharsetUtil.UTF_8));
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf msg) throws Exception {
ByteBuf recieveMsg=(ByteBuf) msg;
String result = ByteBufUtil.hexDump(recieveMsg).toUpperCase();//将bytebuf中的可读字节 转换成16进制数字符串
String result2 = ByteBufUtil.hexDump(msg.readBytes(msg.readableBytes()));
//看下两种方式输出的结果有什么区别
System.out.println("client received:" + result);
System.out.println("client received:" + result2);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
这个代码是可以运行的。你可以自己跑一下。
下面就来解释下上面这些代码。当然这里只做简单的解释,因为每个概念在后面的章节都有详细的描述。
Bootstrap
用来启动服务,客户端和服务端都使用这个。区别在于服务端使用的是ServerBootstrap
。这两个类都是继承自AbstractBootstrap
这个抽象类。
NioEventLoopGroup
用来处理多个连接,可以把它理解为线程池。
NioServerSocketChannel
当然就是netty里最核心的概念channel。这里我们指定channel的类型。当然这里你可以指定OioServerSocketChannel
,表示阻塞的IO channel。
我们通过childHandler
指定业务处理核心逻辑的handler,这些handler是以链的方式管理的。ChannelPipeline
是这个链的名字。这其实也是设计模式中的责任链模式。
接着我们通过bind
方法绑定服务器,sync
表示这是一个阻塞的同步调用,在绑定成功之前都会等待。
EchoServerHandler
和EchoClientHandler
是我们业务处理类,他们的设计方法就是我们前面章节说到的回调。看房方法的名字也能猜出一二。我们需要继承ChannelInboundHandlerAdapter
表明这是一个入口处的处理类。(出口和入口的后面会详细说明)。
channelRead
方法在服务端(客户端)收到数据时被调用,exceptionCaught
是异常发生时调用。
需要注意的是,在接收数据的时候,channelRead
(channelRead0)可能会调用多次,比如接收5个字节,第一次收到3个,第二次收到2个。这其实是netty里面拆包,粘包的概念,这个在后面的章节也会讲到,这里不多说。尽管会出现拆包,粘包的情况,但是netty能保证在TCP协议下,数据接收的顺序和发送的顺序是一致的。(这个也是netty很多自带的解码器处理粘包的前提)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。