需求:
1. 当有客户端连上服务器了,则通知其他的客户端,XXX连上服务器了(注:自己就不在通知自己了)
2. 当有客户端断线的时候,通知其他的客户端,XXX断线了
3. 当有客户端处于激活状态的时候,通知其他的客户端,XXX上线了(注: 上线了和连上服务器是2个概念)
4. 当有客户端处于失活状态的时候,通知其他的客户端,XXX下下了
5. 当有客户端想服务器发送消息的时候,广播给其他的客户端包括自己
编写步骤如下:
1. 编写服务器端的启动程序
package com.baidu.netty.thirdExample;
import com.sun.security.ntlm.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;
public class MyServer {
public static void main(String[] args) throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(new MyServerInitializer());
ChannelFuture future = bootstrap.bind(8899).sync();
future.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
2. 编写服务器端的初始化程序
package com.baidu.netty.thirdExample;
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;
public class MyServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,Delimiters.lineDelimiter()));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new MyServerHandler());
}
}
3. 编写服务器端处理程序
package com.baidu.netty.thirdExample;
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;
public class MyServerHandler extends SimpleChannelInboundHandler<String> {
/**定义一个集合来存储连上来的客户端*/
private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
/**读取客户端的数据,然后广播给其他客户端,如果是自己给自己发消息则显示: 自己对自己说:xxx 如果是别人的消息则显示:XXX对我说:xxx*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
Channel channel = ctx.channel();
group.forEach(en -> {
if (en.equals(channel))
{
en.writeAndFlush("自己对自己说:" + msg + "\n");
}else
{
en.writeAndFlush(channel.remoteAddress() +"对我说: " + msg + "\n");
}
});
}
/**客户端连上服务器的时候通知其他的客户端,XXX连上服务器了*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
group.writeAndFlush("【服务器端消息】:" + channel.remoteAddress() + "加入了\n"); //调用集合的写方法会通知到所有在集合中的客户端
//先调用group的写方法在加入,则可以避免对自己写入
group.add(channel);
}
/**当客户端断开的时候通知其他的客户端XXX失去服务器端的连接了*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
//group.remove(channel); //删除某一个客户端,但是没有必要显示的调用,ChannelGroup会自动检查是否在线
group.writeAndFlush("【服务器端消息】:" + channel.remoteAddress() + "离开了\n");
}
/**客户端上线的时候,通知其他客户端,XXX上线了*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
group.writeAndFlush("【服务器端消息】:" + channel.remoteAddress() + "上线了");
}
/**当客户端下线的时候,通知其他的客户端,XXX下线了*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
group.writeAndFlush("【服务器端消息】:" + channel.remoteAddress() + "下线了");
}
/**出现异常的时候关闭客户端*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
4. 编写客户端启动程序
package com.baidu.netty.thirdExample;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
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 MyClient {
public static void main(String[] args) throws Exception{
EventLoopGroup client = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(client).channel(NioSocketChannel.class).handler(new MyClientInitializer());
ChannelFuture future = bootstrap.connect("localhost",8899);
Channel channel = future.channel();
//编写一个死循环来从控制台接收数据然后发送给服务器
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
for (;;)
{
channel.writeAndFlush(reader.readLine() + "\r\n"); //该处必须要加上换行符,否则服务器端收不到消息,因为我们使用Delimiters.lineDelimiter()方式进行解码
}
}finally {
client.shutdownGracefully();
}
}
}
5. 编写客户端初始化程序
package com.baidu.netty.thirdExample;
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;
public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,Delimiters.lineDelimiter()));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new MyClientHandler());
}
}
6. 编写客户端处理程序
package com.baidu.netty.thirdExample;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class MyClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
7. 测试