打卡日期(2019-07-05)
netty实现简单的聊天程序
在编写程序前需要了解netty 的channel执行流程,以及ChannelGroup的概念
channel 执行流程
- 1.handlerAdded 一个新的处理器上添加通道
- 2.channelRegistered 将通道注册到某个对象上
- 3.channelActive 通道处于活跃状态
- 4.channelRead0 通过通道开始处理IO请求
- 5.channelReadComplete 通道读取请求内容完毕并且返回信息
- 6.channelInactive 通道处于不活跃状态
- 7.channelUnregistered 通道取消注册
- 8.handlerRemoved 处理器将通道移除
- 1.ChannelGroup 消息广播,是Channel通道的一个Set集合,负责处理各个Channel的通讯,监听各个Channel的各种状态
需求
- 1.服务器端监听每个连接的加入
- 2.当有新的连接加入的时候,通知其他channel
- 3.当某个channel下线的时候,通知其他channel
- 4.当前channel发送消息的时候,显示"自己 + 消息内容",
服务端
package com.dragon.chart.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @Description: 跟 03章,04章 的一样,并没有什么区别
*/
public class MyChartServer {
public static void main(String[] args) throws InterruptedException {
//事件循环组 acceptorGroup用于接收请求
EventLoopGroup acceptorGroup = new NioEventLoopGroup();
//事件循环组 handlerGroup用于处理请求
EventLoopGroup handlerGroup = new NioEventLoopGroup();
// sub-class which allows easy bootstrap of 用于轻松的启动bootstrap
ServerBootstrap serverBootstrap = new ServerBootstrap();
try{
// group (acceptorGroup,handlerGroup)
// acceptorGroup which is used for the parent (acceptor) 用于接收所有的请求
// handlerGroup are used to handle all the events and IO 处理所有的事件和IO操作
serverBootstrap.group(acceptorGroup,handlerGroup)
// NioSctpServerChannel 利用nio模式接收一个新的连接创建NioSctpChannel
.channel(NioServerSocketChannel.class)
// 自定义处理器
.childHandler(new MyChartServerInit());
//绑定80端口,端口号可以自定义
ChannelFuture future = serverBootstrap.bind(8080).sync();
future.channel().closeFuture().sync();
}finally {
acceptorGroup.shutdownGracefully();
handlerGroup.shutdownGracefully();
}
}
}
package com.dragon.chart.server;
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;
import io.netty.util.CharsetUtil;
/**
* @Description:
*/
public class MyChartServerInit extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/***
* @Param maxFrameLength 解帧码的最大长度
* @Param delimiters 分隔符
*/
pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
/***
* 解码器处理器
*/
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
/***
* 编码器处理器
*/
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
/***
* 自定义处理器
*/
pipeline.addLast(new MyChartHandler());
}
}
package com.dragon.chart.server;
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;
/**
* @Description:
*/
public class MyChartHandler extends SimpleChannelInboundHandler<String> {
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
private static String server = "[服务器]:";
private static String client = "客户";
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("服务器接收到消息;【"+msg+"】");
Channel ch = ctx.channel();
for(Channel channel : channelGroup){
if(ch != channel){
channel.writeAndFlush( ch.remoteAddress() + ":"+ msg +"\n");
}else{
channel.writeAndFlush("[自己]:"+msg +"\n");
}
}
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
super.channelRegistered(ctx);
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
super.channelUnregistered(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
System.out.println(server + channel.remoteAddress() + client + ",加入\n" );
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
System.out.println(server + channel.remoteAddress() + client + ",退出\n" );
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channel.writeAndFlush(server + channel.remoteAddress() + client + ",已上线\n" );
channelGroup.add(channel);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channel.writeAndFlush(server + channel.remoteAddress() + client + ",下线\n" );
channelGroup.remove(channel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
Channel channel = ctx.channel();
channel.close();
}
}
客户端
package com.dragon.chart.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
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.IOException;
import java.io.InputStreamReader;
/**
* @Description: 客户端
*/
public class MyChartClient {
public static void main(String[] args) throws InterruptedException, IOException {
EventLoopGroup clientGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
try{
bootstrap.group(clientGroup).channel(NioSocketChannel.class).handler(new MyChartClientInit());
Channel channel = bootstrap.connect("localhost",8080).sync().channel();
// 获取键盘输入的事件,通过通道channel将获取的内容写进通道
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
for(;;){
channel.writeAndFlush(br.readLine() + "\r\n");
}
}finally {
clientGroup.shutdownGracefully();
}
}
}
package com.dragon.chart.client;
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;
import io.netty.util.CharsetUtil;
/**
* @Description:
*/
public class MyChartClientInit extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/***
* @Param maxFrameLength 解帧码的最大长度
* @Param delimiters 分隔符
*/
pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
/***
* 解码器处理器
*/
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
/***
* 编码器处理器
*/
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
/***
* 自定义处理器
*/
pipeline.addLast(new MyChartClientHandler());
}
}
package com.dragon.chart.client;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* @Description:
*/
public class MyChartClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
/***
* 客户端不需要处理信息内容,能查看就行,所以不做任何处理,打印出来即可
*/
System.out.println(msg);
}
}
启动:MyChartServer
启动:MyChartClient(理解为client01)
启动:MyChartClient(理解为client02)
启动:MyChartClient(理解为client03)
MyChartServer
[服务器]:/127.0.0.1:6281客户,加入
[服务器]:/127.0.0.1:6305客户,加入
[服务器]:/127.0.0.1:6328客户,加入
MyChartClient(理解为client01)
[服务器]:/127.0.0.1:6305客户,已上线
[服务器]:/127.0.0.1:6328客户,已上线
/127.0.0.1:6328:大家好!
MyChartClient(理解为client02)
[服务器]:/127.0.0.1:6328客户,已上线
/127.0.0.1:6328:大家好!
MyChartClient(理解为client03)
输入:大家好
大家好!
[自己]:大家好!
此时一个简单的聊天功能实现
坑点:
1.注意服务端向客户端发送消息的时候 最后一定要加换行符号"\n",否则客户端不会显示收到的内容。
channel.writeAndFlush( ch.remoteAddress() + ":"+ msg +"\n");
channel.writeAndFlush("[自己]:"+msg +"\n");
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("服务器接收到消息;【"+msg+"】");
Channel ch = ctx.channel();
for(Channel channel : channelGroup){
if(ch != channel){
channel.writeAndFlush( ch.remoteAddress() + ":"+ msg +"\n");
}else{
channel.writeAndFlush("[自己]:"+msg +"\n");
}
}
}