很多伙伴问我是Netty怎么用,抽时间给大家写了一个demo级别的入门案例,仅供参考。
Server端代码如下:
package com.netty.groupchat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
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;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class GroupChatServer {
private int port;
public GroupChatServer(int port) {
this.port = port;
}
//编写一个run方法,处理客户端的请求
public void run(){
EventLoopGroup boosGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
try {
serverBootstrap.group(boosGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,128)
.option(ChannelOption.SO_KEEPALIVE,true)
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("decoder", new StringDecoder());
socketChannel.pipeline().addLast("encoder", new StringEncoder());
socketChannel.pipeline().addLast(new GroupChatServerHandler());
}
});
System.out.println("Netty 服务器启动...");
try {
ChannelFuture cf = serverBootstrap.bind(new InetSocketAddress(port)).sync();
cf.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}catch (Exception e){
}finally {
boosGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new GroupChatServer(6677).run();
}
}
/**
* 自定义消息处理
*/
class GroupChatServerHandler extends SimpleChannelInboundHandler<String>{
//Netty提供的ChannelGroup 可以用来保存 channel
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
//保存新来的channels
private List<Channel> channels = new ArrayList<Channel>();
public static Map<User, Channel> channelsMap = new ConcurrentHashMap<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* handlerAdded 表示建立链接,一旦链接,第一个被执行
* @param ctx
* @throws Exception
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerAdded...");
//将该客户加入聊天的信息推送给其他在线的客户端
channelGroup.add(ctx.channel());
//用来区分用户
channelsMap.put(new User(100,"张三"), ctx.channel());
channelGroup.writeAndFlush("[客户端]" + ctx.channel().remoteAddress() + "加入聊天\n");
}
/**
* 用来提示上线通知,表示channel处于活动的状态,提示 xxxx 上线
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + "上线了...\n");
}
/**
* 当Channel处于非活动状态,提示xxx下线了
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + "离线了...");
}
/**
* 断开链接会被出发,将xx客户离开信息推送给当前在线的用户
* @param ctx
* @throws Exception
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + "离线了\n");
System.out.println("channelGroup size "+channelGroup.size());
}
/**
* 处理逻辑
* @param s
* @throws Exception
*/
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
Channel channel = ctx.channel();
channelGroup.forEach(ch->{
//不是当前的channel
if (ch != channel){
ch.writeAndFlush("[客户]" + channel.remoteAddress() + "发送了消息:" + s + "\n");
}else{
ch.writeAndFlush("[自己]发送了消息" + s + "\n");
}
});
}
}
Client端代码如下:
package com.netty.groupchat;
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.net.InetSocketAddress;
import java.util.Scanner;
public class GroupChatClient {
private String host;
private int port;
public GroupChatClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run(){
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
try{
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<io.netty.channel.socket.SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("decoder", new StringDecoder());
socketChannel.pipeline().addLast("encoder", new StringEncoder());
socketChannel.pipeline().addLast(new GroupChatClientHandler());
}
});
ChannelFuture sync = bootstrap.connect(new InetSocketAddress(host, port)).sync();
System.out.println("------"+sync.channel().remoteAddress()+"------");
// sync.channel().closeFuture().sync();
//客户端要是收入信息
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String msg = scanner.nextLine();
sync.channel().writeAndFlush(msg+"\n");
}
}catch (Exception e){
e.printStackTrace();
}finally {
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new GroupChatClient("127.0.0.1", 6677).run();
}
}
class GroupChatClientHandler extends SimpleChannelInboundHandler<String>{
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
System.out.println(s.trim());
}
}
心跳检测server端代码,可以用上面的client代码来进行测试哦
package com.netty.xtjc;
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.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import static io.netty.handler.timeout.IdleState.*;
import static io.netty.handler.timeout.IdleState.READER_IDLE;
/**
* 心跳检测
*/
public class MyServer {
public static void main(String[] args) {
EventLoopGroup boosGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boosGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//加入一个Neety 提供的IdleStatehandler
/**
* 1 readerIdleTime 表示多长时间没有读,就会发送一个心跳检测包,检测是否有链接
* 2 writerIdletime 表示多长时间没有写
* 3 allIdleTime 表示多长时间没有读也没有写
* 当IdleStateEvent触发后,就会传给管道的下一个Handler去处理,通过调用(回调)的 userEventTriggerd
*/
pipeline.addLast(new IdleStateHandler(3, 5, 7, TimeUnit.SECONDS));
//加入一个对空闲检测进一步处理自定义的Handler
pipeline.addLast(new MyServerHandler());
}
});
ChannelFuture cf = null;
try {
cf = serverBootstrap.bind(new InetSocketAddress(6677)).sync();
cf.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyServerHandler extends ChannelInboundHandlerAdapter {
/**
*
* @param ctx
* @param evt
* @throws Exception
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
IdleStateEvent event = (IdleStateEvent) evt;
String eventType = null;
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);
// ctx.channel().close();
}
}
以下为聊天室的全部代码,目前实现的有群聊功能,并没有实现单聊功能,如果有兴趣的伙伴可以私聊哦,有问题在下面评论,技术问题可以私聊我。