1、Netty是什么?
- netty是一种NIO的封装框架,他主要是以API的形式,封装了NIO的核心思想、代码
- 众所周知,netty的核心思想是简化NIO复杂的代码。
2、Netty和NIO有啥区别?
1、主要的区别在于NIO操作繁琐,复杂,没有特别好的多线程熟练度和NIO熟练度,一般人用起来比较吃力,而且NIO扩展性也不如Netty方便!
但是Netty的思想是运用NIO的思想:IO多路复用的思想
3、Neety的代码实现
我们可以先实现server端
package com.example.netty.nettyServer;
import com.example.netty.nettycofing.SimpleChatServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @ClassName SimpleChatServer
* @Description TODO
* @Author zhurongfei
* @Data 2020/5/8 15:04
* Version 1.0
**/
public class SimpleChatServer {
private static int port=8000;//端口号
public static void main(String[] args) {
//创建连接线程
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
//创建工作线程
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//ServerBootstrap是一个启动 NIO 服务的辅助启动类
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new SimpleChatServerInitializer()) //(4)
.option(ChannelOption.SO_BACKLOG, 2000) // (5)不可过小,具体可在网上看该参数的意思
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)是否是长连接
System.out.println("SimpleChatServer 启动了");
// 绑定端口,开始接收进来的连接
ChannelFuture f = b.bind(port).sync(); // (7)
// 等待服务器 socket 关闭 。
// 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
f.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭线程
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("SimpleChatServer 关闭了");
}
}
}
package com.example.netty.nettycofing;
import com.example.netty.nettyRead.SimpleChatServerHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* @ClassName SimpleChatServerInitializer
* @Description TODO
* @Author zhurongfei
* @Data 2020/5/8 15:16
* Version 1.0
* SimpleChatServerInitializer 用来增加多个的处理类到 ChannelPipeline 上,包括编码、解码、SimpleChatServerHandler 等
**/
public class SimpleChatServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));//读取多少字节
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new SimpleChatServerHandler());//读取数据的方法
System.out.println("SimpleChatClient:"+socketChannel.remoteAddress() +"连接上");
}
}
package com.example.netty.nettyRead;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
/**
* @ClassName SimpleChatServerHandler
* @Description TODO
* @Author zhurongfei
* @Data 2020/5/8 15:19
* Version 1.0
**/
public class SimpleChatServerHandler extends SimpleChannelInboundHandler {//(1)
public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
/**
* 每当从服务端收到新的客户端连接时,客户端的 Channel 存入ChannelGroup列表中,并通知列表中的其他客户端 Channel
* @param ctx
* @throws Exception
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2)
Channel incoming = ctx.channel();
for (Channel channel : channels) {
channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
}
channels.add(ctx.channel());
System.out.println("连接数:"+channels.size());
}
/**
*每当从服务端收到客户端断开时,客户端的 Channel 移除 ChannelGroup 列表中,并通知列表中的其他客户端 Channel
* @param ctx
* @throws Exception
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3)
Channel incoming = ctx.channel();
for (Channel channel : channels) {
channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");
}
channels.remove(ctx.channel());
}
/**
* 每当从服务端读到客户端写入信息时,将信息转发给其他客户端的 Channel
* @param ctx
* @param s
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object s) throws Exception {//(4)
Channel incoming = ctx.channel();
for (Channel channel : channels) {
if (channel != incoming){
channel.writeAndFlush("[" + incoming.remoteAddress() + "]" + s + "\nok");
} else {
channel.writeAndFlush("[you]" + s + "\n");
}
}
}
/**
* 服务端监听到客户端活动
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
Channel incoming = ctx.channel();
System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"在线");
}
/**
* 服务端监听到客户端不活动
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
Channel incoming = ctx.channel();
System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"掉线");
}
/**
* 即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时。
* 在大部分情况下,捕获的异常应该被记录下来并且把关联的 channel 给关闭掉。
* 然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现,比如你可能想在关闭连接之前发送一个错误码的响应消息。
* @param ctx
* @param cause
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (7)
Channel incoming = ctx.channel();
if(!incoming.isActive())
System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"异常");
// 当出现异常就关闭连接
// cause.printStackTrace();
ctx.close();
}
}
接下来是cliect端,基本和Server端差不多
package com.example.netty.nettyClient;
import com.example.netty.nettycofing.SimpleChatClientInitializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* @ClassName SimpleChatClient
* @Description TODO
* @Author zhurongfei
* @Data 2020/5/8 15:27
* Version 1.0
**/
public class SimpleChatClient {
private static int port=8000;//端口号
private static String host="127.0.0.1";
public static void main(String[] args) {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new SimpleChatClientInitializer());
Channel channel = bootstrap.connect(host, port).sync().channel();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while(true){
channel.writeAndFlush(in.readLine() + "\r\n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
package com.example.netty.nettycofing;
import com.example.netty.nettyRead.SimpleChatClientHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* @ClassName SimpleChatClientInitializer
* @Description TODO
* @Author zhurongfei
* @Data 2020/5/8 15:29
* Version 1.0
**/
public class SimpleChatClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new SimpleChatClientHandler());
}
}
package com.example.netty.nettyRead;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* @ClassName SimpleChatClientHandler
* @Description TODO
* @Author zhurongfei
* @Data 2020/5/8 15:30
* Version 1.0
**/
public class SimpleChatClientHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
System.out.println(o);//服务器返回数据
}
}