netty 构建server和client 服务

2023-10-20 12:49:32 浏览数 (2)

前言

编程学习的方法,我认为是以小见大,在理解一个东西之前一定要先会用,并用熟它,这样理解才会快。 就跟理解自行车一样,不会骑,然后先开始研究,最终可能会研究明白,但是毕竟还是事倍功半。 所以先构建一个可以使自己理解的项目,再一点一点学习原理是一种比较好的方式。

Server服务端

构建netty的话,就是一个流程三件套,最基础的三个框架组件摆出来,然后在上面写代码,分别是:

  1. Server 启动类
  2. Initializer 实始化组件类
  3. Handle 请求处理类

劳记这一个流程三件套,基本netty的开发,你已经入门了,就是这么回事。

启动类

套路第一步,写一个启动类,这个是入口,netty服务的话,一般都是先启动服务端,再启动客户端。 这个好理解,如果服务端都不提供服务,客户端还有必要连接吗。当然如果是要启动着玩,就另说。

代码语言:javascript复制
package com.liukai.netty.test02.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * 服务端
 *
 * @author liu kai
 * @since 2020-01-03 00:40
 */
public class ServerNettyServer {

    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ServerNettyChannelInitializer02());

            ChannelFuture channelFuture = serverBootstrap.bind(8889).sync();
            System.out.println("Netty 服务端启动完毕");
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            bossGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
            e.printStackTrace();
        }
    }
}

初始化

实始化必要组件,这也在其它的编常中也是非常常见的一种模式。

代码语言:javascript复制
package com.liukai.netty.test02.server;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

/**
 * 服务 Initializer
 *
 * @author liu kai
 * @since 2020-01-03 23:52
 */
public class ServerNettyChannelInitializer02 extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        //解码器
        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 ServerNettyServerHandle());
    }
}

处理器

处理具体业务,其实看下来,就是这个模式,是不是很好理解。

代码语言:javascript复制
package com.liukai.netty.test02.server;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.util.UUID;

/**
 * 处理器
 *
 * @author liu kai
 * @since 2020-01-04 22:08
 */
public class ServerNettyServerHandle extends SimpleChannelInboundHandler<String> {

    /**
     * 处理请求
     * @param ctx 表示请求上下文信息。可用于获得channel,远程地址等
     * @param msg 客户端消息
     * @return void
     * @author liu kai
     * @since 2020-01-04 22:19
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("server: [remote ip]-> "   ctx.channel().remoteAddress() ", [msg]-> " msg);
        //向客户端发送消息
        ctx.channel().writeAndFlush("server: "  UUID.randomUUID());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }
}

客户端

启动类

代码语言:javascript复制
/**
 * 客户端
 *
 * @author liu kai
 * @since 2020-01-04 22:23
 */
public class ClientServer {

    public static void main(String[] args) {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ClientNettyChannelInitializer());
            ChannelFuture channelFuture = bootstrap.connect("localhost",8889).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            eventLoopGroup.shutdownGracefully();
            e.printStackTrace();
        }
    }
}

初始化

代码语言:javascript复制
package com.liukai.netty.test02.client;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

/**
 * 客户端初始化
 *
 * @author liu kai
 * @since 2020-01-04 22:23
 */
public class ClientNettyChannelInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        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 ClientNettyHandle());
    }
}

处理器

代码语言:javascript复制
package com.liukai.netty.test02.client;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.time.LocalDateTime;

/**
 * 处理器
 *
 * @author liu kai
 * @since 2020-01-04 22:27
 */
public class ClientNettyHandle extends SimpleChannelInboundHandler<String> {

    /**
     * 接收服务器返回消息
     * @param ctx 上下文请求对象
     * @param msg 表示服务端发来的消息
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("client: [remote ip]->"   ctx.channel().remoteAddress()   ", [msg]->"   msg);
        //向服务端发送消息
        Thread.sleep(1000);
        ctx.writeAndFlush("client:"  LocalDateTime.now());
    }

    /**
     * 如果没有这个方法,Client并不会主动发消息给Server
     * 那么Server的channelRead0无法触发,导致Client的channelRead0也无法触发
     * 这个channelActive可以让Client连接后,发送一条消息
     *      但是问题在于,有一这个方法后,服务端
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush("客户端:发送数据");
    }
}

效果

可以启动看一下这个代码的效果:

1.启动服务端Server

Netty 服务端启动完毕 server: [remote ip]-> /127.0.0.1:65320, [msg]-> 客户端:发送数据 server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:42.653 server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:43.657 server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:44.661 server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:45.664 server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:46.668 server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:47.674 server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:48.678

2.启动客户端

client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: bd34689c-237a-4499-be9f-be7b55d1f7e2 client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: afe29b02-65f8-44c5-8b1b-c5286acd0a72 client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: 806f346f-9ae8-4dce-ad20-7a308d8b0c46 client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: 670fb4af-15cd-4471-9990-bc86ff07932c client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: b103ed1a-127b-42df-9c86-58f9e5dd2d4a client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: 3948c322-1cb7-4ba0-bade-7f7161fc2712 client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: e3daf473-6dd6-4e6c-8bca-27f87e43854f client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: 64bf61e2-4126-4238-b53c-4bf90e1c21d8

总结

netyy 的刚开始学习时,只需要了解到它的这个套路,后面的开发其实大同小异,无非是对协议和序列化相关的东西进行处理。实现自己的业务需求。

0 人点赞