结构:
本案例实现一个Netty版本多人聊天室案例。
实现步骤:
- 编写聊天程序服务端:配置线程组,配置编解码器,配置自定义业务处理类,绑定端口号,然后启
动Server,等待Client连接 - 编写服务端-业务处理类Handler:
- 当通道就绪时,输出上线
- 当通道未就绪时,输出离线
- 当通道发来数据时,读取数据,进行广播
- 编写聊天程序客户端:配置了线程组,配置编解码器,配置了自定义的业务处理类,然后启动
Client,连接Server。
连接服务端成功后,获取客户端与服务端建立的Channel
获取系统键盘输入,将用户输入信息通过Channel发送给服务端 - 编写客户端-业务处理类:
读取事件:监听服务端广播消息
1.聊天服务端程序ChatServer
//聊天程序服务器端
public class NettyChatServer {
private int port; //服务器端端口号
public NettyChatServer(int port) {
this.port = port;
}
public void run() throws Exception {
//定义线程组bossGroup
EventLoopGroup bossGroup = new NioEventLoopGroup();
//定义线程组workerGroup
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
//往pipeline链中添加一个解码器
pipeline.addLast("decoder", new StringDecoder());
//往pipeline链中添加一个编码器
pipeline.addLast("encoder", new StringEncoder());
//往pipeline链中添加自定义的handler(业务处理类)
pipeline.addLast(new NettyChatServerHandler());
}
});
System.out.println("网络真人聊天室 Server 启动......");
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("网络真人聊天室 Server 关闭......");
}
}
public static void main(String[] args) throws Exception {
new NettyChatServer(9999).run();
}
}
上述代码通过 Netty 编写了一个服务端程序
注意:我往 Pipeline 链中添加了处理字符串的编码器和解码器,它们加入到 Pipeline 链中后会自动工作,使得服务端读写字符串数据时更加方便,不用人工处理 编解码操作。
2.服务端业务处理类ChatServerHandler
//自定义一个服务器端业务处理类
public class NettyChatServerHandler extends SimpleChannelInboundHandler<String> {
public static List<Channel> channels = new CopyOnWriteArrayList<>();
@Override //通道就绪
public void channelActive(ChannelHandlerContext ctx) {
Channel inChannel=ctx.channel();
channels.add(inChannel);
System.out.println("[Server]:"+inChannel.remoteAddress().toString().substring(1)+"上线");
}
@Override //通道未就绪
public void channelInactive(ChannelHandlerContext ctx) {
Channel inChannel=ctx.channel();
channels.remove(inChannel);
System.out.println("[Server]:"+inChannel.remoteAddress().toString().substring(1)+"离线");
}
@Override //读取数据
protected void channelRead0(ChannelHandlerContext ctx, String s) {
Channel inChannel=ctx.channel();
SocketAddress remoteAddress = inChannel.remoteAddress();
System.out.println(remoteAddress.toString().substring(11)+":" + s);//为什么可以拿到字符串呢?字节数组呢!已经进行过解码
//遍历所有上线Channel,广播消息
for(Channel channel:channels){
if(channel!=inChannel){
//写出的操作,编码,字符串-->字节数组
channel.writeAndFlush("["+inChannel.remoteAddress().toString().substring(1)+"]"+"说:"+s+"\n");
}
}
}
}
上述代码通过继承 SimpleChannelInboundHandler 类自定义了一个服务端业务处理类,并在该类中重
写了四个方法。
当通道就绪时,输出上线
当通道未就绪时,输出离线
当通道发来数据时,读取数据,进行广播
3.聊天程序客户端ChatClient
//聊天程序客户端 public class NettyChatClient { private final String host; //服务器端IP地址 private final int port; //服务器端端口号 public NettyChatClient(String host, int port) { this.host = host; this.port = port; } public void run(){ EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap() .group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch){ ChannelPipeline pipeline=ch.pipeline(); //往pipeline链中添加一个解码器 pipeline.addLast("decoder",new StringDecoder()); //往pipeline链中添加一个编码器 pipeline.addLast("encoder",new StringEncoder()); //往pipeline链中添加自定义的handler(业务处理类) pipeline.addLast(new NettyChatClientHandler()); } }); ChannelFuture cf = bootstrap.connect(host, port).sync(); Channel channel = cf.channel(); //"127.0.0.1:111" System.out.println("------"+channel.localAddress().toString().substring(1)+"------"); Scanner scanner=new Scanner(System.in); while (scanner.hasNextLine()){ String msg=scanner.nextLine(); channel.writeAndFlush(msg+"\r\n"); } //---end cf.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } public static void main(String[] args) throws Exception { new NettyChatClient("127.0.0.1",9999).run(); } }上述代码通过 Netty 编写了一个客户端程序。客户端同样需要配置编解码器
4. 客户端业务处理类ChatClientHandler
//自定义一个客户端业务处理类
public class NettyChatClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
System.out.println(s.trim());
}
}
上述代码通过继承 SimpleChannelInboundHandler 自定义了一个客户端业务处理类,重写了一个方法用来读取服务端发过来的数据。
5.启动测试
1个服务器,3个客户端
- 启动服务器

- 启动3个客户端

- 客户端发送消息



- 下线提醒
- 停止服务,服务器打印:

- 停止服务,服务器打印: