写了上一篇觉得写的不太好,今天开始看Netty了,好好学,好好写。
Netty入门
Netty的环境搭建呢咱们就不细说了大家百度搜一下,挺简单的。书中的入门是写了一个Netty的例子,那咱们也写这个例子看看,Netty到底是怎么玩的。写例子之前回忆一下NIO的时候,写一个时间服务器的步骤
- 创建一个 ServerSocketChannel,设置为非阻塞的。
- 绑定监听端口,设置最大的连接数。
- 创建单独的线程用来轮询 Selector。
- 创建Selector,将之前创建的 ServerSocketChannel 注册进来,并关心对应的事件。
- 启动线程,执行 selector.select() 方法,轮询就绪的Channel
- 当轮询到就绪的Channel之后,判断其所关注的事件。
- 如果是新加入的连接,将其设置为非阻塞式,设置其他的TCP参数,并注册到Selector上监听OP_READ
- 如果是读事件,证明SocketChannel中有新的数据,创建ByteBuffer处理
- 如果是写事件,证明SocketChannel中有数据没写完,继续处理
大家可以看到,一个简单的NIO服务端,我们直接使用JDK的类库开发的话需要大概十来步,相当麻烦。下面我们看看Netty开发的服务端代码。
TimeServer
package NettyServerBasic;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class TimeServer {
public void bind(int port) throws Exception {
//两个线程组,各包含一组NIO线程
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
//ServerBootstrap是Netty的启动NIO的辅助类,目的降低服务端开发的难度
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workGroup)
//书上说这个对应的是JDK的NIO类库的NioServerSocketChannel
//我个人认为应该netty给咱创建一个NioServerSocketChannel类似的channel
.channel(NioServerSocketChannel.class)
//配置NioServerSocketChannel的TCP参数 1024是最大连接数
//跟serverSocketChannel的bind方法类似吧
.option(ChannelOption.SO_BACKLOG, 1024)
//绑定一个处理请求的类 我这里用一个匿名内部类写了
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new TimeServerHandler());
}
});
//绑定端口 ChannelFuture异步回调
ChannelFuture future = bootstrap.bind(port).sync();
//等待服务器监听端口关闭
future.channel().close().sync();
}finally {
//关系线程池
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8989;
new TimeServer().bind(port);
}
}
TimeServerHandler
package NettyServerBasic;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
/*** 服务端读取到网络数据后的处理*/
@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");
String currentTime = "获取当前时间".equals(body)?new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()):"指令有误";
ByteBuf buf = Unpooled.copiedBuffer(currentTime.getBytes());
//将消息写到缓冲数组中
ctx.write(buf);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//将消息推到SocketChannel中发送给客户端
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
大家注意这有一个坑,TimeServerHander书上是继承自 ChannelHandlerAdapter,当我继承完之后发现没有channelRead、channelReadComplete、exceptionCaught三个方法,其实Netty4和Netty5的版本不一样了,我这用的是Netty4书上用的是Netty5,大家练习的时候一定要注意,不然找好久都找不到问题所在。下面我在网上找的,大家看一下。
Netty4的ChannelHandlerAdapter:
Netty5的ChannelHandlerAdapter:
但是在Netty4中用的话只能用ChannelHandlerAdapter的子类,我这就是用的他的子类ChannelInboundHandlerAdapter ,下图所示:
服务器端开发到这里就结束了,具体里面的每个组件什么意思咱们后边慢慢补上,先黑盒的看代码就好。下面开始客户端的代码了。
TimeClent
package NettyServerBasic;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class TimeClient {
public void connect(int port, String host) {
//配置客户端的NIO线程
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
//服务器端是ServerBootstrap
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel){
socketChannel.pipeline().addLast(new TimeClientHandler());
}
});
//发起异步连接
ChannelFuture channelFuture = bootstrap.connect(host,port).sync();
//等待客户端的连接关闭
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
//释放IO线程组
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new TimeClient().connect(8989,"127.0.0.1");
}
}
TimeClientHandler:
package NettyServerBasic;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class TimeClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
private final ByteBuf sendMsg;
public TimeClientHandler() {
byte[] bytes = "获取当前时间".getBytes();
sendMsg = Unpooled.buffer(bytes.length);
sendMsg.writeBytes(bytes);
}
//读取到服务器端的数据后的操作
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
byte[] bytes = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(bytes);
String body = new String(bytes,"UTF-8");
System.out.println("当前时间是:"+body);
}
/*客户端被通知channel活跃以后,做事*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//往服务器写数据
ctx.writeAndFlush(sendMsg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
好了以上就是Netty客户端和服务端的开发基本的东西,大家先黑盒的了解一下。