zoukankan      html  css  js  c++  java
  • Netty 应用:Socket服务器

    Socket服务器

    Netty作为Socket服务器,需要编写服务端和客户端,服务器端和客户端收发消息通过自定义的Handler.channelRead0方法来交互,客户端连接上服务器后,需要在active时向服务器发送一条消息来触发服务器的行为。

    服务端实现

    /**
     * Created by fubin on 2019/7/11.
     */
    public class SocketServer {
        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 SocketServerInitializer());
    
                ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
                channelFuture.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }
    class  SocketServerInitializer extends ChannelInitializer<SocketChannel>{
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            ChannelPipeline channelPipeline = socketChannel.pipeline();
            channelPipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
            channelPipeline.addLast(new LengthFieldPrepender(4));
            channelPipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
            channelPipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
            channelPipeline.addLast(new SocketServerHandler());
        }
    }
    class SocketServerHandler extends SimpleChannelInboundHandler<String>{
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            //远程地址
            System.out.println(ctx.channel().remoteAddress()+","+msg);
            //处理完业务,把结果返回给客户端
            ctx.channel().writeAndFlush("from server:"+ UUID.randomUUID());
        }
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
            //出现异常,关闭连接
            ctx.close();
        }
    }

    客户端实现

    /**
     * Created by fubin on 2019/7/11.
     */
    public class SocketClient {
        public static void main(String[] args) {
            EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    
            try{
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new SocketClientInitializer());
    
                ChannelFuture future = bootstrap.connect("localhost",8899).sync();
                future.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                eventLoopGroup.shutdownGracefully();
            }
        }
    }
    class SocketClientInitializer extends ChannelInitializer<SocketChannel>{
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline channelPipeline = ch.pipeline();
    
            channelPipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
            channelPipeline.addLast(new LengthFieldPrepender(4));
            channelPipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
            channelPipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
            channelPipeline.addLast(new SocketClientHandler());
        }
    }
    class SocketClientHandler extends SimpleChannelInboundHandler<String>{
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(ctx.channel().remoteAddress());
            System.out.println("client output:"+msg);
            ctx.channel().writeAndFlush("from client:"+ LocalDateTime.now());
        }
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
            ctx.close();
        }
        //在连接激活时,客户端向服务器发送一条消息
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.channel().writeAndFlush("hello world socket");
        }
    }

    Socket实现的聊天程序消息群发

    netty实现多客户端消息群发的关键 : ChannelGroup

     

    服务端实现

    /**
     * Created by fubin on 2019/7/11.
     * 聊天程序,消息群发
     */
    public class ChatServer {
        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 ChatServerInitializer());
    
                ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
                channelFuture.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }
    class  ChatServerInitializer extends ChannelInitializer<SocketChannel> {
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            ChannelPipeline channelPipeline = socketChannel.pipeline();
            //分隔符编解码器
            channelPipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
            channelPipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
            channelPipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
            channelPipeline.addLast(new ChatServerHandler());
        }
    }
    class ChatServerHandler extends SimpleChannelInboundHandler<String> {
    
        //保存channel对象
        private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            Channel channel = ctx.channel();
            //广播消息
            channels.forEach(ch ->{
                if(channel != ch){
                    ch.writeAndFlush(channel.remoteAddress() + "发送的消息:"+ msg + "
    ");
                }else{
                    ch.writeAndFlush(" 【自己】" + msg + "
    ");
                }
            });
        }
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
            //出现异常,关闭连接
            ctx.close();
        }
        /**
         * 连接建立,告诉其他客户端,xxx已经上线
         * @param ctx
         * @throws Exception
         */
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            //广播
            channels.writeAndFlush("【服务器】 - " + channel.remoteAddress() + "加入
    ");
    
            channels.add(channel);
        }
        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            //广播
            channels.writeAndFlush("【服务器】 - " + channel.remoteAddress() + "离开
    ");
            //此代码可不要,netty自动移除
            //channels.remove(channel);
            //验证
            System.out.println("channelGroup size :"+channels.size());
        }
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            System.out.println("【服务器】- "+ channel.remoteAddress() + "下线
    ");
        }
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            System.out.println("【服务器】- "+ channel.remoteAddress() + "上线
    ");
        }
    }

    客户端实现

    /**
     * Created by fubin on 2019/7/11.
     */
    public class ChatClient {
        public static void main(String[] args) {
            EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    
            try{
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ChatClientInitializer());
                Channel channel = bootstrap.connect("localhost",8899).sync().channel();
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                //循环监听客户端输入
                for(;;){
                    channel.writeAndFlush(br.readLine()+"
    ");
                }
            } catch (InterruptedException | IOException e) {
                e.printStackTrace();
            } finally {
                eventLoopGroup.shutdownGracefully();
            }
        }
    }
    class ChatClientInitializer extends ChannelInitializer<SocketChannel>{
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline channelPipeline = ch.pipeline();
    
            channelPipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
            channelPipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
            channelPipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
            channelPipeline.addLast(new ChatClientHandler());
        }
    }
    class ChatClientHandler extends SimpleChannelInboundHandler<String>{
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(msg);
        }
    }

    应用内的推送的思考: 服务器端和客户端维持长连接推送给app,如果客户端断线,服务端需要把消息保存在数据库或者mongodb中,当客户端重连后,服务端再再重新发送。

     

     

  • 相关阅读:
    正则匹配 sql语句参数
    正则判断是不是移动端浏览
    .net 2.0 后台多线程
    Oracle 获取当天数据
    C# 图片转Base64
    Js FileReader图片加载
    KendoUI操作笔记
    Android Studio解析Json文件内容
    LitePal
    C#最基本的小说爬虫
  • 原文地址:https://www.cnblogs.com/fubinhnust/p/11940620.html
Copyright © 2011-2022 走看看