上篇博客使用Nio实现了一个多人聊天Demo,可以发现,Nio主要有三大核心部分:Channel、Buffer和Selector,但Nio编程比较复杂,而Netty是一个基于NIO的网络编程框架,可以帮助我们快速简单的开发一个网络应用。使用Netty实现多人聊天Demo的代码如下:
pom文件如下:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.50.Final</version>
</dependency>
服务端代码如下:
package com.netty.chat;
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;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class ChatServer {
private int port;
public ChatServer(int port) {
this.port = port;
}
public void run(){
// 每个 EventLoop 维护着一个 Selector 实例
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 服务器端启动助手
ServerBootstrap sb = new ServerBootstrap();
sb.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 socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
// 添加解码器
pipeline.addLast("decoder", new StringDecoder());
// 添加编码器
pipeline.addLast("encoder", new StringEncoder());
// 添加自定义处理类 一定要添加在编码器和解码器之后
pipeline.addLast(new ChatServerHandler());
}
});
System.out.println("Netty chat Server启动.....");
ChannelFuture sync = sb.bind(port).sync();
sync.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
System.out.println("Netty chat Server关闭");
}
}
public static void main(String[] args) {
ChatServer chatServer = new ChatServer(9999);
chatServer.run();
}
}
服务端业务处理代码如下:
package com.netty.chat;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.ArrayList;
import java.util.List;
/**
* SimpleChannelInboundHandler 可以避免消息类型转换
*/
public class ChatServerHandler extends SimpleChannelInboundHandler<String> {
// 该集合存储所有channel,用于广播消息
public static List<Channel> channels = new ArrayList<>();
/**
* 通道断开后执行该方法
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel inChannel = ctx.channel();
channels.remove(inChannel);
System.out.println("[Server:]" + inChannel.remoteAddress().toString() + "下线");
}
/**
* 通道就绪执行该方法
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel inChannel = ctx.channel();
channels.add(inChannel);
System.out.println("[Server:]" + inChannel.remoteAddress().toString() + "上线");
}
/**
* 服务器端读取消息
* @param ctx
* @param s
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
Channel inChannel = ctx.channel();
for (Channel channel : channels) {
if (channel != inChannel){
channel.writeAndFlush("[" + inChannel.remoteAddress().toString() + "] 说:" + s + "\n");
}
}
}
}
客户端代码如下:
package com.netty.chat;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.Scanner;
public class ChatClient {
private int port;
private String host;
public ChatClient(int port, String host) {
this.port = port;
this.host = host;
}
public void run(){
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bt = new Bootstrap();
bt.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
// 添加解码器
pipeline.addLast("decoder", new StringDecoder());
// 添加编码器
pipeline.addLast("encoder", new StringEncoder());
// 添加自定义处理类 一定要添加在编码器和解码器之后
pipeline.addLast(new ChatClientHandler());
} // 添加自定义业务处理类
});
ChannelFuture connect = bt.connect(host, port).sync();
Channel channel = connect.channel();
System.out.println("--------" + channel.localAddress().toString() + "---------");
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()){
String msg = sc.nextLine();
channel.writeAndFlush(msg + "\r\n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
System.out.println("Netty chat Client关闭");
}
}
public static void main(String[] args) {
ChatClient chatClient = new ChatClient(9999, "127.0.0.1");
chatClient.run();
}
}
客户端业务处理代码如下:
package com.netty.chat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
//SimpleChannelInboundHandler 可以避免消息类型转换
public class ChatClientHandler extends SimpleChannelInboundHandler<String> {
/**
* 接受服务器端广播发送过了的消息
* @param ctx
* @param s
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
System.out.println(s);
}
}