前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >netty 构建server和client 服务

netty 构建server和client 服务

作者头像
潇洒
发布2023-10-20 12:49:32
发布2023-10-20 12:49:32
23400
代码可运行
举报
文章被收录于专栏:石头岛石头岛
运行总次数:0
代码可运行

前言

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

Server服务端

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

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

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

启动类

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

代码语言:javascript
代码运行次数:0
运行
复制
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
代码运行次数:0
运行
复制
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
代码运行次数:0
运行
复制
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
代码运行次数:0
运行
复制
/**
 * 客户端
 *
 * @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
代码运行次数:0
运行
复制
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
代码运行次数:0
运行
复制
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 的刚开始学习时,只需要了解到它的这个套路,后面的开发其实大同小异,无非是对协议和序列化相关的东西进行处理。实现自己的业务需求。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Server服务端
    • 启动类
    • 初始化
    • 处理器
  • 客户端
    • 启动类
    • 初始化
    • 处理器
  • 效果
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档