Netty章节三:Netty实现Socket编程

功能实现:客户端向服务端发送消息,服务器接收到消息后向客户端响应。(此程序是个死循环)

添加netty-all依赖根据不同的包管理工具以不同的方式引入 可查 https://search.maven.org/

服务端代码

服务端主启动类

public class MyServer {
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            /*
                所有的Handler() 都是针对bossGroup发挥作用
                所有的childHandler() 都是针对workerGroup发挥作用,连接丢给workerGroup
                    之后由childHandler对象对workerGroup这个Nio线程发挥作用
             */
            serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
                    .childHandler(new MyServerInitializer());
            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

初始化器 (Initializer)

public class MyServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        /*
            LengthFieldBasedFrameDecoder 解码器
            LengthFieldPrepender 编码器
        */
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
        pipeline.addLast(new LengthFieldPrepender(4));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new MyServerHandler());

    }
}

自定义处理器 (Handler)

//SimpleChannelInboundHandler<?> 的泛型表示客户端发来的数据 真正的类型是什么
public class MyServerHandler extends SimpleChannelInboundHandler<String> {

    /**
     * 读取客户端发过来的请求,并且向客户端返回响应的方法
     * @param ctx 上下文,可以获取远程的信息,地址、连接对象
     * @param msg 客户端发来的请求对象
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(ctx.channel().remoteAddress() + "," + msg);

        /*
            write 写入缓冲
            flush 把缓冲的内容清/推出去
            writeAndFlush 写入缓冲,并把缓冲的内容推出去
         */
        ctx.channel().writeAndFlush("from server:" + UUID.randomUUID());
    }

    /**
     * 出现异常的情况下怎么办
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

客户端代码

客户端主启动类

public class MyClient {
    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 MyClientInitializer());
            //与对应的url建立连接通道
            ChannelFuture channelFuture = bootstrap.connect("localhost",8899).sync();
            channelFuture.channel().closeFuture().sync();
        }finally {
            eventLoopGroup.shutdownGracefully();
        }
    }
}

初始化器 (Initializer)

public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        /*
            LengthFieldBasedFrameDecoder 解码器
            LengthFieldPrepender 编码器
        */
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
        pipeline.addLast(new LengthFieldPrepender(4));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new MyClientHandler());
    }
}

自定义处理器 (Handler)

public class MyClientHandler extends SimpleChannelInboundHandler<String> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        //打印远程地址
        System.out.println(ctx.channel().remoteAddress());

        System.out.println("client output:" + msg);
        ctx.writeAndFlush("from client:" + LocalDateTime.now());
    }

    /**通道活跃时调用*/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush("来自于客户端的问候");
    }

    /**发生异常时调用*/
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

运行调试

服务端输出

/127.0.0.1:54554,from client:2020-05-05T17:51:11.729
/127.0.0.1:54554,from client:2020-05-05T17:51:11.729
/127.0.0.1:54554,from client:2020-05-05T17:51:11.730
/127.0.0.1:54554,from client:2020-05-05T17:51:11.730
/127.0.0.1:54554,from client:2020-05-05T17:51:11.730
...

客户端输出

client output:from server:6cf30395-561c-43a6-80d0-429510b3cc84
localhost/127.0.0.1:8899
client output:from server:aa046e14-1535-4b62-9e14-afaa40d371a2
localhost/127.0.0.1:8899
client output:from server:44948915-295a-49b8-89f6-44ce958bb0e2
localhost/127.0.0.1:8899
client output:from server:5a6629d2-114e-4183-a842-2e766be030c0
localhost/127.0.0.1:8899
client output:from server:e519ebe6-417b-4b61-ae1f-efcd65ae7d62
localhost/127.0.0.1:8899
...

流程分析

当客户端和服务端通道建立连接的时候(触发channelActive方法),客户端向服务发送来“自与客户端的问题!”数据,服务器端channelRead0被触发,进行回写数据到客户端触发客户端的channelRead0回调方法被调用,依次重复。

原文地址:https://www.cnblogs.com/mikisakura/p/12983467.html