Netty案例群聊
服务端
package com.netty.nettydeom.netty.groupchat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
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 GroupChatServer {
private int port;
public GroupChatServer(int port) {
this.port = port;
}
//编写run方法
public void run() {
NioEventLoopGroup boos = new NioEventLoopGroup(1);
NioEventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boos, worker)
.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 {
//获取到pipeline
ChannelPipeline pipeline = socketChannel.pipeline();
//向pipeline添加编码器
pipeline.addLast("decoder", new StringDecoder());
//向pipeline解码器
pipeline.addLast("encoder", new StringEncoder());
//加入自己的业务处理
pipeline.addLast(new GroupChatServerHandler());
}
});
System.out.println("netty服务器启动");
ChannelFuture future = null;
try {
future = bootstrap.bind(port).sync();
//监听关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
boos.shutdownGracefully();
worker.shutdownGracefully();
}
}
public static void main(String[] args) {
new GroupChatServer(7000).run();
}
}
package com.netty.nettydeom.netty.groupchat;
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;
import lombok.val;
import java.text.SimpleDateFormat;
import java.util.Date;
public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> {
//定义一个channle 组 管理所有的channel
//GlobalEventExecutor.INSTANCE 是全局的事件执行器 是一个单例
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//handlerAdded 表示连接建立 一旦连接 第一个被执行
//将当前channel 加入到channelGroup
@Override
public void handlerAdded(ChannelHandlerContext channelHandlerContext) throws Exception {
Channel channel = channelHandlerContext.channel();
/**
* 将该客户加入聊天的信息推送给其他在线的客户端
*
* 该方法会将channlGroup 表示连接 中所有的channel 遍历并发送消息
*/
channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + "加入聊天\n");
channelGroup.add(channel);
}
//表示channel 处于活动状态 提示xx上线
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + "上线了~");
}
//断开连接 将xx客户离开信息推送给当前在线的客户
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + "离开了\n");
System.out.println("channel length =" + channelGroup.size());
}
//表示channel 不活动状态 提示xx 离线
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + "离线了~");
}
//读取信息
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
//获取到当前channel
val channel = channelHandlerContext.channel();
//这时我们遍历channelGroup 根据不同的情况 会送不同的信息
channelGroup.forEach(ch -> {
if (channel != ch) {
//表示当前的channel 转发消息
ch.writeAndFlush("时间:" + sdf.format(new Date()) + ",[客户]" + channel.remoteAddress() + "发送给信息:" + s + "\n");
} else {
ch.writeAndFlush("时间:" + sdf.format(new Date()) + ",[自己]发送消息:" + s + "\n");
}
});
}
//出现异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//关闭通过
ctx.close();
}
}
客户端
package com.netty.nettydeom.netty.groupchat;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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 GroupChatClient {
//属性
private final String host;
private final int port;
public GroupChatClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run() {
NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventExecutors)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//向pipeline添加编码器
pipeline.addLast("decoder", new StringDecoder());
//向pipeline解码器
pipeline.addLast("encoder", new StringEncoder());
//加入相管自定义的handler
pipeline.addLast(new GroupChatClienHandler());
}
});
ChannelFuture future = null;
try {
future = bootstrap.connect(host
, port).sync();
Channel channel = future.channel();
//输入信息
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
channel.writeAndFlush(scanner.nextLine() + "\r\n");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
eventExecutors.shutdownGracefully();
}
}
public static void main(String[] args) {
new GroupChatClient("127.0.0.1", 7000).run();
}
}
package com.netty.nettydeom.netty.groupchat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class GroupChatClienHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
System.out.println(s.trim());
}
}
netty的心跳检测
package com.netty.nettydeom.netty.heartbeat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.val;
import java.util.concurrent.TimeUnit;
public class Myserver {
public static void main(String[] args) {
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup worker = new NioEventLoopGroup();
try {
//创建服务器端的启动对象 配置参数
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//加入一netty 提供idlestateHandler
ChannelPipeline pipeline = socketChannel.pipeline();
/**
* IdleStateHandler 是netty 提供的处理空闲状态的处理器
*
* readerIdleTime : 表示多长时间没有读 就会发送一个心跳检测包检测是否连接
* writerIdleTime : 表示多长时间没有写 就会发送一个心跳检测包检测是否连接
* allIdleTime :表示多长时间没有读和写 就会发送一个心跳检测包检测是否连接
当IdleStateHandler 触发后 就会传递给管道的下一个handler 去处理
通过调用触发 下一个handler 的 userEventTriggered 在该方法中去处理
IdleStateHandler 读空闲 写空闲 读写空闲
*/
pipeline.addLast(new IdleStateHandler(3, 5, 7, TimeUnit.SECONDS));
//加入一个对空闲检测进一步处理的handler 自定义
pipeline.addLast(new MyseverHandler());
}
});
//启动服务
val connect = bootstrap.bind(7000).sync();
//给关闭通道进行监听
connect.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
worker.shutdownGracefully();
boss.shutdownGracefully();
}
}
}
package com.netty.nettydeom.netty.heartbeat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
public class MyseverHandler extends ChannelInboundHandlerAdapter {
/**
* @param ctx 上下文
* @param evt 事件
* @throws Exception
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
//将 evy 向下转型 IdleStateEvent
IdleStateEvent event = (IdleStateEvent) evt;
String eventType = "";
switch (event.state()) {
case READER_IDLE:
eventType = "读空闲";
break;
case WRITER_IDLE:
eventType = "写空闲";
break;
case ALL_IDLE:
eventType = "读写空闲";
break;
}
System.out.println(ctx.channel().remoteAddress() + "--超时时间--" + eventType);
}
}
}