实现场景: 聊天
服务端,客户端A,客户端B,客户端C。当客户端发送消息给服务端后,服务端在将这条消息广播个所有客户端户端A,客户端B,客户端C。
需求1: 客户端上线后,会通知所有客户端上线。
如客户端A先建立连接,不需要通知。
当客户端B与服务端建立连接,服务端告诉A,客户端B上线。
A和B建立连接后,客户端C和服务端建立连接。服务端广播一条信息给A和B。
需求2: A、B、C都已经建立连接,当A发送一条给服务端,服务端广播这条消息给所有客户端,客户端A会提示这条消息是自己发送的。
一、服务端程序的编写
1、MyChartServer 类
public class MyChartServer { public static void main(String[] args) throws Exception{ 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(); }finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
2、MyChatServerInitializer 类
public class MyChatServerInitializer extends ChannelInitializer<SocketChannel>{ protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.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 MyChartServerHandle()); } }
3、MyChartServerHandle 类
public class MyChartServerHandle extends SimpleChannelInboundHandler<String>{ //用于保存所有Channel对象 private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { //channel为当前客户端发给服务端的channel 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 { //chanel可以理解成Connection 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"); //下面这行代码Netty会自动调用 //channelGroup.remove(channel); } //表示连接时活动状态 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); //广播消息给所有的客户端 CommonUtil.println( channel.remoteAddress() + " 上线 \n"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); //广播消息给所有的客户端 CommonUtil.println( channel.remoteAddress() + " 下线 \n"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace();; ctx.close(); } }
二、客户端程序编写
1、MyChatClient类
public class MyChatClient { public static void main(String[] args) throws Exception{ EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) .handler(new MyChatClientInitializer()); //channel表示与服务端的Connection Channel channel = bootstrap.connect("localhost",8899).sync().channel(); //不断读取客户端输入的内容 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); for(;;){ channel.writeAndFlush(br.readLine() + "\r\n"); } }finally { eventLoopGroup.shutdownGracefully(); } } }
2、MyChatClientInitializer 类
public class MyChatClientInitializer extends ChannelInitializer<SocketChannel> { 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 MyChartClientHandle()); } }
3、MyChartClientHandle 类
public class MyChartClientHandle extends SimpleChannelInboundHandler<String> { // 对于客户端来说,输入来自控制台 protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { //仅仅打印来自服务端的信息 CommonUtil.println(msg); } }
三、测试
1、启动MyChartServer,然后启动MyChatClient
MyChartServer打印 59786 上线
2、再启动一个客户端
可以发现60635 上线,
并且第一个客户端提示 60635 加入
3、再启动一个客户端
提示60966上线
第一个客户端 提示60966 加入
第二个客户端 提示60966 加入
四、测试2
1、客户端A发送消息: 大家好,我是客户端A
客户端A接收到写信息: [自己]大家好,我是客户端A
客户端B接收到信息
客户端C接收到的信息
自此,实现了通过多个Socket实现的通信的过程
作者:Work Hard Work Smart
出处:http://www.cnblogs.com/linlf03/
欢迎任何形式的转载,未经作者同意,请保留此段声明!