问道Netty。持续更新。。。

概念

Zero Copy

  • 0拷贝,速度快
  • 操作数据时, 不需要将数据 buffer 从一个内存区域拷贝到另一个内存区域. 因为少了一次内存的拷贝, 因此 CPU 的效率就得到的提升.
  • 在 OS 层面上的 Zero-copy 通常指避免在 用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据. 例如 Linux 提供的 mmap 系统调用, 它可以将一段用户空间内存映射到内核空间, 当映射成功后, 用户对这段内存区域的修改可以直接反映到内核空间; 同样地, 内核空间对这段区域的修改也直接反映用户空间. 正因为有这样的映射关系, 我们就不需要在 用户态(User-space) 与 内核态(Kernel-space) 之间拷贝数据, 提高了数据传输的效率.

而需要注意的是, Netty 中的 Zero-copy 与上面我们所提到到 OS 层面上的 Zero-copy 不太一样, Netty的 Zero-coyp 完全是在用户态(Java 层面)的, 它的 Zero-copy 的更多的是偏向于 优化数据操作 这样的概念.

实操

代码

服务器

新建Maven项目:
在这里插入图片描述
添加插件:

		<plugin>
          <groupId>org.codehaus.mojo</groupId>
          <artifactId>exec-maven-plugin</artifactId>
          <version>1.6.0</version>
          <executions>
            <execution>
<!--              // 可以添加对应的执行阶段-->
              <phase>test</phase>
<!--              ...-->
              <goals>
<!--                // 指定来的 goal为java,表示运行java程序-->
                <goal>java</goal>
              </goals>
            </execution>
          </executions>
          <configuration>
<!--            // 指定了运行的main class-->
            <mainClass>com.cc.netty.server.EchoServer</mainClass>
<!--            // 执行运行 main class的参数-->
<!--            // 其实就是传入main方法的String[]-->
            <arguments>
              <argument>argument1</argument>
<!--              ...-->
            </arguments>
<!--            // 运行java的程序的系统参数-->
            <systemProperties>
              <systemProperty>
                <key>myproperty</key>
                <value>myvalue</value>
              </systemProperty>
<!--              ...-->
            </systemProperties>
          </configuration>
        </plugin>

执行mvn exec:java:
报错:在这里插入图片描述
说明插件里的参数有问题。

  • 不好用,用下面的简单代码:
  • 服务端:
package com.cc.netty.test;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.handler.timeout.ReadTimeoutHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;

import java.util.Scanner;

/**
 * netty服务端
 * NioEventLoopGroup是一个处理I/O操作的多线程事件循环。
 * Netty为不同类型的传输提供了各种EventLoopGroup实现。
 * 我们在这个例子中实现了一个服务器端应用程序,
 * 因此将使用两个NioEventLoopGroup。第一个通常被称为“boss”,接受传入的连接。第二个通常称为“worker”,
 * 在boss接受连接并将接受的连接注册到worker之后,处理接受连接的流量。使用多少线程以及如何将它们映射到创建的通道取决于EventLoopGroup实现,
 * 甚至可以通过构造函数进行配置。
 * <p>
 * ServerBootstrap是一个设置服务器的助手类。您可以直接使用通道设置服务器。但是,请注意这是一个冗长的过程,在大多数情况下您不需要这样做。
 * 在这里,我们指定使用NioServerSocketChannel类,该类用于实例化一个新通道以接受传入连接。
 * <p>
 * 这里指定的处理程序总是由新接受的通道计算。ChannelInitializer是用于帮助用户配置新通道的特殊处理程序。
 * 您很可能希望通过添加一些处理程序(如DiscardServerHandler)来实现您的网络应用程序,来配置新通道的ChannelPipeline。
 * 随着应用程序变得复杂,您可能会向管道中添加更多的处理程序,并最终将这个匿名类提取到顶级类中。
 * <p>
 * 您还可以设置特定于通道实现的参数。我们正在编写TCP/IP服务器,因此我们可以设置套接字选项,如tcpNoDelay和keepAlive。
 * 请参阅ChannelOption的apidocs和特定的ChannelConfig实现,以获得受支持的ChannelOptions的概述。
 * <p>
 * 你注意到option()和childOption()了吗?option()用于接收传入连接的NioServerSocketChannel。
 * childOption()用于父服务器通道接受的通道,在本例中是NioServerSocketChannel。
 * 我们现在准备好出发了。剩下的就是绑定到端口并启动服务器。在这里,我们绑定到机器中所有NICs(网络接口卡)的端口8080。
 * 现在,您可以任意次数地调用bind()方法(使用不同的绑定地址)。
 * <p>
 * telnet 可以测试服务器是否工作
 * telnet localhost 8080
 */
public class NettyServer {
    public static void main(String[] args) throws Exception {
        new NettyServer().run(8666);
    }

    //新建一个netty服务器
    public void run(int port) throws Exception {
        //NioEventLoopGroup是一个处理I/O操作的多线程循环
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        System.out.println("准备运行端口:" + port);
        try {
            //服务器的设置助手类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap = serverBootstrap.group(bossGroup, workGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //这里指定的处理程序总是由新接受的通道计算。
                    .childHandler(new ChildChannelHandler());
            //绑定端口,同步等待成功
            ChannelFuture future = serverBootstrap.bind(port).sync();
            //等待服务监听端口关闭
            future.channel().closeFuture().sync();
        } finally {
            //退出,释放线程资源
            workGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

//ChannelInitializer是用于帮助用户配置新通道的特殊处理程序
class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

    //请求到达后调用
    protected void initChannel(SocketChannel socketChannel) throws Exception {
//        ByteBuf byteBuf= Unpooled.copiedBuffer("$".getBytes());
//        socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,byteBuf));
        socketChannel.pipeline().addLast(new StringDecoder());//进行字符串的编解码设置
        socketChannel.pipeline().addLast(new StringEncoder());
        socketChannel.pipeline().addLast(new ReadTimeoutHandler(60));//设置超时时间
        socketChannel.pipeline().addLast(new DiscardServerHandler());
    }
}

/**
 * 服务器类型设置
 **/
class DiscardServerHandler extends ChannelHandlerAdapter {
    @Override
    //只要接收到数据,就会调用channelRead()方法
    public void channelRead(ChannelHandlerContext ctx, Object msg) {

        try {
            //ByteBuf是一个引用计数的对象,必须通过release()方法显式地释放它
            System.out.println(String.format("%s === %s","收到信息",msg));
//            ByteBuf in = (ByteBuf) msg;
            System.out.println("传输内容是");
//            System.out.println(in.toString(CharsetUtil.UTF_8));
            //返回信息
            ByteBuf resp = Unpooled.copiedBuffer("服务端收到信息,ack$".getBytes());
            ctx.writeAndFlush(resp);
//            Scanner scanner = Scanner.
        } finally {
            System.out.println(msg);
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 出现异常就关闭
        cause.printStackTrace();
        ctx.close();
    }

}
  • 客户端:
package com.cc.netty.test;


import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.DelimiterBasedFrameDecoder;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;

/**
 * netty 编写的客户端
 */
public class TimeClient {
    public static void main(String[] args) throws Exception {
        new TimeClient().connect(8666, "localhost");
    }

    public void connect(int port, String host) throws Exception {
        //配置客户端
        System.out.println("连接=>" + host + ":" + port);
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ByteBuf byteBuf = Unpooled.copiedBuffer("$".getBytes());
                            socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, byteBuf));
                            socketChannel.pipeline().addLast(new TimeClientHandler());
                        }
                    });
            //绑定端口,同步等待成功
            ChannelFuture future = bootstrap.connect(host, port).sync();
            //等待服务监听端口关闭
            future.channel().closeFuture().sync();
        } finally {
            //优雅退出,释放线程资源
            eventLoopGroup.shutdownGracefully();
        }
    }
}

class TimeClientHandler extends ChannelHandlerAdapter {
    private byte[] req;

    public TimeClientHandler() {
        req = "中国必胜!武汉加油!".getBytes();
//        req = "$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$".getBytes();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ByteBuf message = null;
        for (int i = 0; i < 5; i++) {
            message = Unpooled.buffer(req.length);
            message.writeBytes(req);
            ctx.writeAndFlush(message);
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            ByteBuf in = (ByteBuf) msg;
            System.out.println(in.toString(CharsetUtil.UTF_8));
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 出现异常就关闭
        cause.printStackTrace();
        ctx.close();
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值