服务端代码如下
package netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//以上我们创建了两个线程组
//一是负责连接的请求. 二是负责真正的业务逻辑处理
//在真正的运行过程中都是无限循环
ServerBootstrap serverBootstrap = new ServerBootstrap();
//这个是服务器的启动对象,配置参数
serverBootstrap
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,128)//对列数
.childOption(ChannelOption.SO_KEEPALIVE,true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new SerHandler());//pipeline 和 channel 是两个对象中,都有各自的引用。
//把自己写的handler 加入到管道中。
}
});
System.out.println("server is starting ------------");
ChannelFuture channelFuture = serverBootstrap.bind(6677).sync();
channelFuture.channel().closeFuture().sync();//对关闭进行监听
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
NioEventLoopGroup源码的解析
package netty;
import io.netty.util.NettyRuntime;
public class Test {
public static void main(String[] args) {
System.out.println(NettyRuntime.availableProcessors()); //获取的时cpu的核心数,也就是任务管理器的资源监视器的cpu个数。
}
}
通过debug可以看到,这个线程组存在一个EventExecutor的数组中。
private static final int DEFAULT_EVENT_LOOP_THREADS;
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
}
}
对应的Handler
package netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* 因为我们自定义的Hander要继承自框架的才能让框架管理,如果没有是
*/
public class SerHandler extends ChannelInboundHandlerAdapter {
//这个就是读取数据的事件。也就是浏览器传过来的数据,被框架封装到了具体的对象中,让我们用handler来处理。ChannelHandlerContext这个对象中封装了所有的数据。包括channel pipeline 二。这个msg 就是客户端传输过来的数据。 当有数据读取的时候,这个方法就会被触发。就像那个监听器模式,应该是调用了相应的方法。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//这个方法是干嘛的呢。有上下文对象,我们可以拿到很多东西,还有msg
System.out.println("ctx = " + ctx);
//就像我们之前用ByteBuffer 一样。我们需要将数据封装到缓冲对象
ByteBuf byteBuf = (ByteBuf) msg;//这个是netty封装后的buf 性能明显是比之前的高的。
System.out.println("byteBuf.toString(CharsetUtil.UTF_8) = " + byteBuf.toString(CharsetUtil.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//数据读取完成之后
//将数据写入,并刷新缓存
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客户端", CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//如果发生了异常,我们应该正常的关闭通道。
ctx.close();
//如果发生异常了调用。
}
}
客户端代码
package netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
//客户需要一个事件循环组
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
//客户端我们启动的配置类是哪个呢 用的是netty的
Bootstrap bootstrap = new Bootstrap();
//因为这个每个客户端就是对应一个group 。server端是因为要处理accept 和 read 和 write 而分为的两个
bootstrap.group(eventLoopGroup)//这个是线程组
.channel(NioSocketChannel.class) //客户端和服务器端就是差了一个 server 我们不需要设置child 队列和连接状态了。
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new CliHandler());
}
});
System.out.println("client is started---");
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6677);//这个方法,直接会有返回值。也就是异步。关于ChannelFuture 后面继续分析
channelFuture.channel().closeFuture().sync();//异步的监听的一个机制
} finally {
eventLoopGroup.shutdownGracefully();
}
}
}
客户端Handler
package netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import java.nio.charset.Charset;
public class CliHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//当通道准备好了之后,就会触发这个方法。
System.out.println("client :ctx = " + ctx);
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,服务端~ 🐶", CharsetUtil.UTF_8));//Unpooled 这个就是一个用来处理数据的工具类。
}
//当有能读取的事件时,就会触发这个方法。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("服务器说:"+((ByteBuf) msg).toString(CharsetUtil.UTF_8));//在nio中,我们的ByteBufer 是不能直接转的,需要 .arr()方法,也不用编码解码。
System.out.println("ctx.channel().remoteAddress() = " + ctx.channel().remoteAddress());
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
}