具体功能:
1.服务器启动,n多个客户端与服务器进行连接,一个客户端上线之后,服务器端控制台会打印xx上线了,其他的客户端控制台打印xx上线了。如果一个客户端下线了,服务器端的控制台上打印,xx下线了,其他的客户端控制台打印xx下线了。
2.多个客户端都上线之后,一个客户端(比如说A)给服务端发送消息,那么客户端(比如说A,B,C,包括自己本身)都会收到消息,对于A来说,会标志此消息是自己发送自己的,其他的客户端则会收到具体的消息。
服务器代码
服务端主启动类
public class MyChatServer { public static void main(String[] args){ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class) .childHandler(new MyChatServerInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(8899).sync(); channelFuture.channel().closeFuture().sync(); }catch (Exception e){ System.out.println("服务端异常"); System.out.println(e.getMessage()); }finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
|
初始化器 (Initializer)
public class MyChatServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter())); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new MyChatServerHandler()); } }
|
自定义处理器 (Handler)
public class MyChatServerHandler extends SimpleChannelInboundHandler<String> {
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { Channel channel = ctx.channel(); channelGroup.forEach(ch -> { if(channel != ch){ ch.writeAndFlush(channel.remoteAddress() + " 发送的消息:" + msg + "\n"); }else { ch.writeAndFlush("【自己】" + msg + "\n"); } });
}
@Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel();
channelGroup.writeAndFlush("【服务器】 - " + channel.remoteAddress() + " 加入\n"); channelGroup.add(channel); }
@Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel();
channelGroup.writeAndFlush("【服务器】 - " + channel.remoteAddress() + " 离开\n"); }
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().remoteAddress() + " 上线"); }
@Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().remoteAddress() + " 下线"); }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }
}
|
客户端代码
客户端主启动类
public class MyChatClient { public static void main(String[] args) {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) .handler(new MyChatClientInitializer()); Channel channel = bootstrap.connect("localhost", 8899).sync().channel();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); for (; ;){ channel.writeAndFlush(br.readLine() + "\r\n"); }
}catch (Exception e){ System.out.println("异常"); System.out.println(e.getMessage()); }finally { eventLoopGroup.shutdownGracefully(); } } }
|
初始化器 (Initializer)
public class MyChatClientInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter())); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new MyChatClientHandler()); } }
|
自定义处理器 (Handler)
public class MyChatClientHandler extends SimpleChannelInboundHandler<String> {
@Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(msg); } }
|
测试
启动服务器,启动第一个客户端的时候,服务器端控制台打印
第二个客户端启动,则服务器控制台打印
/127.0.0.1:47024 上线 /127.0.0.1:47070 上线
|
此时第一个客户端打印,
【服务器】 -/127.0.0.1:47070 加入
|
启动第三个客户端依次可以自己验证,当我在客户端1控制台输入信息的时候,客户端1控制台打印了【自己】来自第一个客户端的问候
来自第一个客户端的问候 【自己】来自第一个客户端的问候
|
其他的二个客户端控制台打印
/127.0.0.1:47024 发送的消息:来自第一个客户端的问候
|
如果其他的客户端下线之后,比如客户端1下线,服务器端控制台打印
其他客户端控制台打印:
【服务器】 -/127.0.0.1:47024 离开
|
使用lsof
命令查看端口映射关系
