Netty相关
BIO 同步阻塞IO (Block IO)
一个连接一个线程 ,可以使用线程池改造 成伪异步I/O
一个acceptor线程负责监听客户端连接,然后单独起一个新的线程处理收发逻辑 伪异步IO利用了线程池的大小和消息队列机制,大大减少了内存溢出
NIO 同步非阻塞IO (No-Block IO)
一个线程可以处理多个客户端的IO事件。 采用selector的多路复用器,支持IO多路复用的有 selector 、pselect、poll、epoll。目前linux下使用的epoll的多路复用机制
AIO 异步非阻塞IO
摆脱了NIO的selector对注册的通道进行轮训,通过回调的方式,实现了异步读写,主动通知。
Netty的核心组件
*Buffer
了解NIO,要先来了解一下Buffer
Buffer是缓冲区的意思,也是一个对象,可以写入或者读取。
在NIO中所有的数据都是由缓冲区处理。
缓冲区实质上是一个数组,常用的有ByteBuffer CharBuffer 等等

* Channel
通道,传入和传出数据的载体。可以进行打开关闭操作 ,全双工。
下是常用的Channel:
-- EmbeddedChannel
-- LocalServerChannel
-- NioDatagramChannel
-- NioSctpChannel
-- NioSocketChannel
* Selector
selector 会不断的轮训注册在service上的Channel,如果存在读写事件,将被轮训出来,通过selectionKey获取就绪的集合操作。 改为epoll()轮训以后突破了最大连接句柄数的限制,意味着一个selector可以接入成千上万的客户端。
* ByteBuf
可以获取缓冲区的字节数,动态的创建byte数组,通过ByteBuf的readBytes方法将缓冲区的字节复制到新建的byte数组中。
* Future
netty中所有的I/O操作都是异步的,Netty 提供了ChannelFuture,用于在执行异步操作的时候使用。每个Netty的出站I/O操作都会返回一个ChannelFuture。ChannelFuture能够注册一个或者多个ChannelFutureListener 实例。监听器的回调方法operationComplete(),将会在对应的操作完成时被调用。
//主线程组
private final static EventLoopGroup bossGroup = new NioEventLoopGroup();
//从线程组
private final static EventLoopGroup workerGroup = new NioEventLoopGroup();
private static ServerBootstrap bootstrap;
private static ChannelFuture future = null;
public static void main(String[] args) {
try {
bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)//设置通道
.childHandler(new ServerChannelInitializer()); //设置字处理器
//启动server ,同时设置启动方式为同步
future = bootstrap.bind(8090).sync();
//进行方法阻塞,等待服务器链路关闭以后,main函数才退出
future.channel().closeFuture().sync();
} catch (Exception e) {
log.error("Netty 启动错误:", e);
} finally {
//优雅关闭
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
连个线程组其实就是Reator线程组(响应式)
NioServerSocketChannel 对应与NIO库中的ServerSocketChannel
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
/**
* 初始化方法
*/
@Override
protected void initChannel(SocketChannel channel) {
//获取line
ChannelPipeline pipeline = channel.pipeline();
System.out.println("报告");
System.out.println("信息:有一客户端链接到本服务端");
System.out.println("IP:" + channel.localAddress().getHostName());
System.out.println("Port:" + channel.localAddress().getPort());
System.out.println("报告完毕");
// 解码编码
// pipeline.addLast(new StringDecoder(Charset.forName("GBK")));
// pipeline.addLast(new StringEncoder(Charset.forName("GBK")));
//通过管道添加 http处理器,当请求的服务端我们需要解码,响应到客户端做编码
pipeline.addLast("HttpServerCodec",new HttpServerCodec());
//添加自定义的handler
// pipeline.addLast(new EchoServerHandler());
pipeline.addLast(new CustomHandler());
}
}
/**
* 自定义的handler
* SimpleChannelInboundHandler 入栈概念
* ws用于专门处理传输text
*/
public class CustomHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
//管理所有的channel
public static ChannelGroup groups = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
/**
* @param ctx 上下文对象
* @param msg 消息
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
String content = msg.text();
String id = ctx.channel().id().asShortText();
System.out.println("接收到的数据:"+content);
sendMsg(LocalDateTime.now()+" <br/> 用户"+id+"说:"+content);
//1、获取客户端发来的消息
/**
* 2、判断消息类型,根据不同的类型来处理不同的业务
* 2.1 连接类型 当ws第一次open的时候,初始化channel,把channel和userid关联起来
* 使用静态hashmap<String ,Channel>
* 2.2 聊天类型 聊天记录保存到数据库(加密,乱码同时表计消息的签发状态)
* 2.3 心跳类型
*/
}
/**
* 把channel放到管理类
* @param ctx
* @throws Exception
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("Channel助手类添加");
groups.add(ctx.channel());
String id = ctx.channel().id().asShortText();
sendMsg(LocalDateTime.now()+" <br/> 用户"+id+"进来了");
}
/**
* 当出发的时候,会自动移除客户端的channel
* @param ctx
* @throws Exception
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("Channel助手类移除");
System.out.println("当前channel的长id:"+ctx.channel().id().asLongText());
System.out.println("当前channel的短id:"+ctx.channel().id().asShortText());
String id = ctx.channel().id().asShortText();
sendMsg(LocalDateTime.now()+" <br/> 用户"+id+"离开了");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("Channel发生异常");
//发生异常以后关闭连接(channel) 随后从groups中移除
ctx.channel().close();
groups.remove(ctx.channel());
}
public void sendMsg(String msg){
for (Channel c:groups) {
c.writeAndFlush(new TextWebSocketFrame(msg));
}
//下面方式与上面一致,二选一 下面是使用组
// groups.writeAndFlush(new TextWebSocketFrame("[服务器接收到的消息]"+ LocalDateTime.now()+"接收到的消息为:"+content));
}
}