<2021SC@SDUSC>netty初步使用

2021SC@SDUSC

一、前言

在学习的第一个礼拜,先完成第二个目标,即使用netty编写服务端和客户端的程序,实现服务端和客户端的双向通信。当然,会在之后的学习过程中逐步完善。

二、服务端代码

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

public class Server {

    private final int PORT;

    public Server(int port) {
        this.PORT = port;
    }

    public void run() {
        //创建两个线程池
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(8);
        try {
            //netty的引导程序
            ServerBootstrap bootstrap = new ServerBootstrap();
            //设置parentGroup和childGroup
            bootstrap.group(bossGroup, workerGroup)
                    //指定Channel类型
                    .channel(NioServerSocketChannel.class)
                    //设置队列中最大连接数
                    .option(ChannelOption.SO_BACKLOG, 128)
                    //监控连接是否有效
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //客户端连接成功后,添加handler,注意ChannelInitilizer在注册成功后,会将自己删除
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            ChannelPipeline pipeline = channel.pipeline();
                            //添加编解码器
                            pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
                            pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
                            //自定义handler
                            pipeline.addLast(new SimpleChannelInboundHandler<String>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                                    System.out.println(ctx.channel().remoteAddress() + ": " +msg);
                                    //将读取到的消息回写给客户端
                                    ctx.writeAndFlush("服务器收到消息: " + msg);
                                }

                                @Override
                                public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                    System.out.println("连接已建立,来自" + ctx.channel().remoteAddress());
                                }

                                @Override
                                public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                                    System.out.println("出现异常: " + cause.getMessage());
                                    //出现异常,关闭连接
                                    ctx.channel().close();
                                }
                            });
                        }
                    });
            //为服务器绑定端口,同步等待
            ChannelFuture future = bootstrap.bind(PORT).sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new Server(8080).run();
    }

}

三、客户端代码

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Client {

    private static final int PORT = 8080;
    private static final String HOST = "127.0.0.1";

    public Client() {}

    public void run() {
        //创建线程池,注意,与服务端不同,客户端只需要一个线程池 
        EventLoopGroup workerGroup = new NioEventLoopGroup(8);
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(workerGroup)
                    //指定Channel类型
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            ChannelPipeline pipeline = channel.pipeline();
                            pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
                            pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
                            pipeline.addLast(new SimpleChannelInboundHandler<String>() {
                                //将服务器发送的消息打印
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                                    System.out.println(msg);
                                }

                                @Override
                                public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                    //IO事件,创建其它线程来处理
                                    ExecutorService executor = Executors.newSingleThreadExecutor();
                                    executor.submit(() -> {
                                        Scanner scanner = new Scanner(System.in);
                                        while (scanner.hasNextLine()) {
                                            String message = scanner.nextLine();
                                            ctx.writeAndFlush(message);
                                        }
                                    });
                                }

                                @Override
                                public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                                    System.out.println(cause.getMessage());
                                    ctx.channel().closeFuture().sync();
                                }
                            });
                        }
                    });
            ChannelFuture future = bootstrap.connect(HOST, PORT).sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new Client().run();
    }

}

四、总结

使用netty框架完成服务端对客户端发送消息的读取并且回写,初步完成了服务端与客户端的通信,之后预计将代码改为多个客户端的群聊系统。
此外,代码中存在的一个问题是客户端的退出问题,目前是强制关闭,之后会成其他方式。
之后的几篇博客,会就服务端的代码,分析netty启动流程的细节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东羚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值