如题,只是为了实验,我将所有的客户端的channel存在静态变量ChannelGroup实例中,对消息进行群发。
当然,如果实际的环境中,我估计要将channel存在缓存数据库中,具体怎么做,后面再研究。
现在,我们来做这次简单的实验:
源代码:https://github.com/YangZhouChaoFan/netty-learn/tree/master/netty-start
1:NettyServer
package com.netty.start.server; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * 服务器类. */ public class NettyServer { public void start(int port) throws Exception { //创建接收者的事件循环组 EventLoopGroup parentGroup = new NioEventLoopGroup(); //创建访问者的事件循环组 EventLoopGroup childGroup = new NioEventLoopGroup(); try { //创建服务器引导程序 ServerBootstrap b = new ServerBootstrap(); //设置消息循环 b.group(parentGroup, childGroup); //设置通道 b.channel(NioServerSocketChannel.class); //配置通道参数:连接队列的连接数 b.option(ChannelOption.SO_BACKLOG, 1024); //设置客户端请求的处理操作 b.childHandler(new ChildChannelHandler()); //绑定端口,并获取通道io操作的结果 ChannelFuture f = b.bind(port).sync(); //等待服务端监听端口关闭 f.channel().closeFuture().sync(); } finally { //关闭接收器事件循环 parentGroup.shutdownGracefully(); //关闭访问者的事件循环 childGroup.shutdownGracefully(); } } }
2:ChildChannelHandler
package com.netty.start.server; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; /** * 客户端通道处理类. */ public class ChildChannelHandler extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel e) throws Exception { ChannelPipeline pipeline = e.pipeline(); // 以("\n")为结尾分割的 解码器 pipeline.addLast(new LineBasedFrameDecoder(1024)); // 字符串解码 和 编码 pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); //添加消息处理 e.pipeline().addLast(new NettyServerHandler()); } }
3:NettyServerHandler
package com.netty.start.server; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.util.concurrent.GlobalEventExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.SocketAddress; /** * 服务器处理类. */ public class NettyServerHandler extends ChannelHandlerAdapter { static private Logger logger = LoggerFactory.getLogger(NettyServerHandler.class); //创建频道组 public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); /** * 连接通道. * * @param ctx * @param remoteAddress * @param localAddress * @param promise * @throws Exception */ @Override public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception { logger.info(remoteAddress + ":连接通道"); super.connect(ctx, remoteAddress, localAddress, promise); } /** * 活跃通道. * * @param ctx * @throws Exception */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { logger.info(ctx.channel().remoteAddress() + ":通道激活"); super.channelActive(ctx); ctx.writeAndFlush("欢迎访问服务器\r\n"); channels.add(ctx.channel()); } /** * 非活跃通道. * * @param ctx * @throws Exception */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { logger.info(ctx.channel().remoteAddress() + ":通道失效"); super.channelInactive(ctx); channels.remove(ctx.channel()); } /** * 接收消息. * * @param ctx * @param msg * @throws Exception */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { logger.info(ctx.channel().remoteAddress() + ":" + msg); Channel currentChannel = ctx.channel(); for (Channel channel : channels) { if (channel != currentChannel) { channel.writeAndFlush("[" + currentChannel.remoteAddress() + "]" + msg + "\n"); } } } /** * 接收完毕. * * @param ctx * @throws Exception */ @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { super.channelReadComplete(ctx); } /** * 关闭通道. * * @param ctx * @param promise * @throws Exception */ @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { logger.info(ctx.channel().remoteAddress() + ":关闭通道"); super.close(ctx, promise); } /** * 异常处理. * * @param ctx * @param cause * @throws Exception */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.info("异常信息:" + cause.getMessage()); } }
4:NettyClient
package com.netty.start.client; import com.netty.start.server.ChildChannelHandler; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; 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.InputStreamReader; /** * 客户端类. */ public class NettyClient { public void connect(String host, int port) throws Exception { //创建事件循环组 EventLoopGroup group = new NioEventLoopGroup(); try { //创建引导程序 Bootstrap b = new Bootstrap(); //设置消息循环 b.group(group); //设置通道 b.channel(NioSocketChannel.class); //配置通道参数:tcp不延迟 b.option(ChannelOption.TCP_NODELAY, true); //设置通道处理 b.handler(new ChannelHandler()); //发起异步链接,等待输入参数 Channel channel = b.connect(host, port).sync().channel(); BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); while (true) { channel.writeAndFlush(in.readLine() + "\r\n"); } } finally { //关闭 group.shutdownGracefully(); } } }
5:ChannelHandler
package com.netty.start.client; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; /** * 通道处理类. */ public class ChannelHandler extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); // 以("\n")为结尾分割的 解码器 pipeline.addLast(new LineBasedFrameDecoder(1024)); // 字符串解码 和 编码 pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); socketChannel.pipeline().addLast(new NettyClientHandler()); } }
6:NettyClientHandler
package com.netty.start.client; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.SocketAddress; /** * 客户端处理类. */ public class NettyClientHandler extends ChannelHandlerAdapter { static private Logger logger = LoggerFactory.getLogger(NettyClientHandler.class); /** * 连接通道. * * @param ctx * @param remoteAddress * @param localAddress * @param promise * @throws Exception */ @Override public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception { logger.info(remoteAddress + ":连接通道"); super.connect(ctx, remoteAddress, localAddress, promise); } /** * 活跃通道. * * @param ctx * @throws Exception */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { logger.info(ctx.channel().remoteAddress() + ":通道激活"); super.channelActive(ctx); } /** * 非活跃通道. * * @param ctx * @throws Exception */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { logger.info(ctx.channel().remoteAddress() + ":通道失效"); super.channelInactive(ctx); } /** * 接收消息. * * @param ctx * @param msg * @throws Exception */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { logger.info(ctx.channel().remoteAddress() + ":" + msg); } /** * 接收完毕. * * @param ctx * @throws Exception */ @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { super.channelReadComplete(ctx); } /** * 关闭通道. * * @param ctx * @param promise * @throws Exception */ @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { super.close(ctx, promise); } /** * 异常处理. * * @param ctx * @param cause * @throws Exception */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.info("异常信息:" + cause.getMessage()); } }
7:ServerTest
package com.netty.start.test; import com.netty.start.server.NettyServer; /** * Created by chenhao on 2016/3/17. */ public class ServerTest { public static void main(String[] args) throws Exception { NettyServer server = new NettyServer(); server.start(3000); } }
8:ClientTest
package com.netty.start.test; import com.netty.start.client.NettyClient; /** * Created by chenhao on 2016/3/17. */ public class ClientTest { public static void main(String[] args) throws Exception { NettyClient client = new NettyClient(); client.connect("127.0.0.1", 3000); } }
通过7和8两个类,启动测试实例。