服务器端使用Netty网络编程案例

群聊系统

服务端

  • YzxServerHandler
    public class YzxServerHandler extends SimpleChannelInboundHandler {
    
        //public static List channels = new ArrayList();
    
        //使用一个hashmap 管理不能自动管理没给加入的channel
        //public static Map channels = new HashMap();
    
        //定义一个channle 组,管理所有的channel
        //GlobalEventExecutor.INSTANCE) 是全局的事件执行器,是一个单例
        private static ChannelGroup  channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
    	    Channel channel = ctx.channel();
            //将该客户加入聊天的信息推送给其它在线的客户端
    		//该方法会将 channelGroup 中所有的channel 遍历,并发送 消息,我们不需要自己遍历
            channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + " 加入聊天" + sdf.format(new java.util.Date()) + " \n");
            ///将当前channel 加入到  channelGroup
            channelGroup.add(channel);
    
    		//提示 xx上线
            System.out.println(ctx.channel().remoteAddress() + " 上线了~");
        }
    
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    		Channel channel = ctx.channel();
            //将xx客户离开信息推送给当前在线的客户
            channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + " 离开了\n");
            System.out.println("channelGroup size" + channelGroup.size());	
            
            System.out.println(ctx.channel().remoteAddress() + " 离线了~");
        }
    
        //读取数据
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
    
            //获取到当前channel
            Channel channel = ctx.channel();
            //这时我们遍历channelGroup, 根据不同的情况,回送不同的消息
    
            channelGroup.forEach(ch -> {
                if(channel != ch) { //不是当前的channel,转发消息
                    ch.writeAndFlush("[客户]" + channel.remoteAddress() + " 发送了消息" + msg + "\n");
                }else {//回显自己发送的消息给自己
                    ch.writeAndFlush("[自己]发送了消息" + msg + "\n");
                }
            });
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            //关闭通道
            ctx.close();
        }
    }
    
  • YzxServer
    public class YzxServer {
    
        private int port; //监听端口
    
    
        public GroupChatServer(int port) {
            this.port = port;
        }
    
        //编写run方法,处理客户端的请求
        public void run() throws  Exception{
    
            //创建两个线程组
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup(); //8个NioEventLoop
    
            try {
                ServerBootstrap b = new ServerBootstrap();
    
                b.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, 128)
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        .childHandler(new ChannelInitializer() {
    
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
    
                                //获取到pipeline
                                ChannelPipeline pipeline = ch.pipeline();
                                //向pipeline加入解码器
                                pipeline.addLast("decoder", new StringDecoder());
                                //向pipeline加入编码器
                                pipeline.addLast("encoder", new StringEncoder());
                                //加入自己的业务处理handler
                                pipeline.addLast(new YzxServerHandler());
                            }
                        });
    
                System.out.println("netty 服务器启动");
                ChannelFuture channelFuture = b.bind(port).sync();
    
                //监听关闭
                channelFuture.channel().closeFuture().sync();
            }finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
    
        }
    
        public static void main(String[] args) throws Exception {
    
            new GroupChatServer(7000).run();
        }
    }
    

客户端

  • YzxClientHandler
    public class YzxClientHandler extends SimpleChannelInboundHandler {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(msg.trim());
        }
    }
    
  • YzxClient
    public class YzxClient {
    
        //属性
        private final String host;
        private final int port;
    
        public GroupChatClient(String host, int port) {
            this.host = host;
            this.port = port;
        }
    
        public void run() throws Exception{
            EventLoopGroup group = new NioEventLoopGroup();
    
            try {
    
    
            Bootstrap bootstrap = new Bootstrap()
                    .group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer() {
    
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
    
                            //得到pipeline
                            ChannelPipeline pipeline = ch.pipeline();
                            //加入相关handler
                            pipeline.addLast("decoder", new StringDecoder());
                            pipeline.addLast("encoder", new StringEncoder());
                            //加入自定义的handler
                            pipeline.addLast(new YzxClientHandler());
                        }
                    });
    
            ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
            //得到channel
                Channel channel = channelFuture.channel();
                System.out.println("-------" + channel.localAddress()+ "--------");
                //客户端需要输入信息,创建一个扫描器
                Scanner scanner = new Scanner(System.in);
                while (scanner.hasNextLine()) {
                    String msg = scanner.nextLine();
                    //通过channel 发送到服务器端
                    channel.writeAndFlush(msg + "\r\n");
                }
            }finally {
                group.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) throws Exception {
            new GroupChatClient("127.0.0.1", 7000).run();
        }
    }
    

Http服务器

服务端

  • YzxServerHandler

    //HttpObject 是HttpServerCodec 编-解码后,客户端和服务器端相互通讯的数据被封装成 HttpObject
    public class YzxServerHandler extends SimpleChannelInboundHandler {
    
    
        //channelRead0 读取客户端数据
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    
    
            System.out.println("对应的channel=" + ctx.channel() + " pipeline=" + ctx
            .pipeline() + " 通过pipeline获取channel" + ctx.pipeline().channel());
    
            System.out.println("当前ctx的handler=" + ctx.handler());
    
            //判断 msg 是不是 httprequest请求
            if(msg instanceof HttpRequest) {	
                System.out.println("msg 类型=" + msg.getClass());
                System.out.println("客户端地址" + ctx.channel().remoteAddress());
    
                //获取到
                HttpRequest httpRequest = (HttpRequest) msg;
                //获取uri, 过滤指定的资源
                URI uri = new URI(httpRequest.uri());
                if("/favicon.ico".equals(uri.getPath())) {
                    System.out.println("请求了 favicon.ico, 不做响应");
                    return;
                }
                
                //回复信息给浏览器 [http协议]	
                ByteBuf content = Unpooled.copiedBuffer("hello, 我是服务器", CharsetUtil.UTF_8);
    
                //构造一个http的相应,即 httpresponse
                FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
                response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
                response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
    
                //将构建好 response返回
                ctx.writeAndFlush(response);
    
            }
        }	
    }
    
  • YzxServer

    public class YzxServer {
        public static void main(String[] args) throws Exception {
    
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup();
    
            try {
                ServerBootstrap serverBootstrap = new ServerBootstrap();
    
                serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer(){
    				            @Override
    						    protected void initChannel(SocketChannel ch) throws Exception {
    						        ChannelPipeline pipeline = ch.pipeline();
    						
    						        //HttpServerCodec 是netty 提供的处理http的 编-解码器
    						        pipeline.addLast(new HttpServerCodec());
    						        //2. 增加一个自定义的handler
    						        pipeline.addLast(new YzxServerHandler());					
    						    }
    			            }); 
    
                ChannelFuture channelFuture = serverBootstrap.bind(6668).sync();            
                channelFuture.channel().closeFuture().sync();
            }finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }
    

WebSocket服务器

服务端

  • YzxServerHandler
    //TextWebSocketFrame 类型,表示一个文本帧(frame)
    public class YzxServerHandler  extends SimpleChannelInboundHandler{
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
    
            System.out.println("服务器收到消息 " + msg.text());	
            //回复消息
            ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间" + LocalDateTime.now() + " " + msg.text()));
        }
    
        //当web客户端连接后, 触发方法
        @Override
        public void ChannelActive(ChannelHandlerContext ctx) throws Exception {
            //id 表示唯一的值,LongText 是唯一的 ShortText 不是唯一
            System.out.println("handlerAdded 被调用" + ctx.channel().id().asLongText());
            System.out.println("handlerAdded 被调用" + ctx.channel().id().asShortText());
        }
    
    
        @Override
        public void ChannelInActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("handlerRemoved 被调用" + ctx.channel().id().asLongText());
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            System.out.println("异常发生 " + cause.getMessage());
            ctx.close(); //关闭连接
        }
    }
    
  • YzxServer
    public class YzxServer {
        public static void main(String[] args) throws Exception{
    
    
            //创建两个线程组
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup(); //8个NioEventLoop
            try {
    
                ServerBootstrap serverBootstrap = new ServerBootstrap();
    
                serverBootstrap.group(bossGroup, workerGroup);
                serverBootstrap.channel(NioServerSocketChannel.class);
                serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
                serverBootstrap.childHandler(new ChannelInitializer() {
    
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
    
                        //因为基于http协议,使用http的编码和解码器
                        pipeline.addLast(new HttpServerCodec());
                        //是以块方式写,添加ChunkedWriteHandler处理器
                        pipeline.addLast(new ChunkedWriteHandler());
    
                        //http数据在传输过程中是分段, HttpObjectAggregator就是可以将多个段聚合,所以当浏览器发送大量数据时,就会发出多次http请求
                        pipeline.addLast(new HttpObjectAggregator(8192));
    
                        //WebSocketServerProtocolHandler 核心功能是通过状态码 101将 http协议升级为 ws协议 , 保持长连接
                        //浏览器请求时 ws://localhost:7000/hello 表示请求的uri
                        pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
    
                        //自定义的handler ,处理业务逻辑
                        pipeline.addLast(new YzxServerHandler ());
                    }
                });
    
                //启动服务器
                ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
                channelFuture.channel().closeFuture().sync();
    
            }finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }	
    

你可能感兴趣的:(NIO,源码,其他,netty,java)