使用Netty3.X编写异步非阻塞Socket tcp通信

举报
程序员-上善若水 发表于 2022/06/23 23:55:28 2022/06/23
【摘要】 使用Netty3.X编写异步非阻塞Socket tcp通信 Netty应用场景 分布式开源框架中dubbo、Zookeeper,RocketMQ底层rpc通讯使用就是netty。游戏开发中,底层使用n...

使用Netty3.X编写异步非阻塞Socket tcp通信

Netty应用场景

  1. 分布式开源框架中dubbo、Zookeeper,RocketMQ底层rpc通讯使用就是netty。
  2. 游戏开发中,底层使用netty通讯。

netty的优点

  1. NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等;
  2. 需要具备其它的额外技能做铺垫,例如熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的NIO程序;
  3. 可靠性能力补齐,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大;
  4. JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该bug发生概率降低了一些而已,它并没有被根本解决。该BUG以及与该BUG相关的问题单如下:

集成

pom

<!-- https://mvnrepository.com/artifact/io.netty/netty -->
		<dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty</artifactId>
			<version>3.3.0.Final</version>
		</dependency>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果在android上集成,请去网上下载 netty.jar,引入到环境中。注意版本问题,本篇是根据3.x来写的。

实现服务端

1、回调

首先建立回调类,客户端发来的信息,以及其他一些日志信息,都会反馈到这个Handler类中,下面开启服务时,会指向这个类。

class ServerHandler extends SimpleChannelHandler {


    /**
     * 通道关闭的时候触发
     */
    @Override
    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        System.out.println("channelClosed");
    }

    /**
     * 必须是连接已经建立,关闭通道的时候才会触发.
     */
    @Override
    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        super.channelDisconnected(ctx, e);
        System.out.println("channelDisconnected");
    }

    /**
     * 捕获异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        super.exceptionCaught(ctx, e);
        System.out.println("exceptionCaught");

    }

    /**
     * 接受消息
     */
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        super.messageReceived(ctx, e);
//		System.out.println("messageReceived");
        System.out.println("服务器端收到客户端消息:"+e.getMessage());
        //回复内容
        ctx.getChannel().write("好的"); //反馈给客户端
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

2、开启服务

// netty 服务器端
public class NettyServer {

    public static void main(String[] args) {
        // 创建服务类对象
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 创建两个线程池 分别为监听监听端口 ,nio监听
        ExecutorService boos = Executors.newCachedThreadPool();
        ExecutorService worker = Executors.newCachedThreadPool();
        // 设置工程 并把两个线程池加入中
        serverBootstrap.setFactory(new NioServerSocketChannelFactory(boos, worker));
        // 设置管道工厂
        serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {

            public ChannelPipeline getPipeline() throws Exception {
                ChannelPipeline pipeline = Channels.pipeline();
                //将数据转换为string类型.
                pipeline.addLast("decoder", new StringDecoder());
                pipeline.addLast("encoder", new StringEncoder());
                pipeline.addLast("serverHandler", new ServerHandler());//此处指定上面的回调类
                return pipeline;
            }
        });
        // 绑定端口号
        serverBootstrap.bind(new InetSocketAddress(9090));
        System.out.println("netty server启动....");
    }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

此时在浏览器输入localhost:9090,看控制台打印信息,如果出现下面日志,说明服务端开启成功并可用。此时可在服务下面再做其耗时他操作,并通过浏览器发送请求,会发现请求可其他操作互不影响不会被服务阻塞掉。
在这里插入图片描述

客户端的实现

和服务端实现类似,只不过将其中一些对象换成客户端的对象。

回调

class ClientHandler extends SimpleChannelHandler {

    /**
     * 通道关闭的时候触发
     */
    @Override
    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        System.out.println("channelClosed");
    }

    /**
     * 必须是连接已经建立,关闭通道的时候才会触发.
     */
    @Override
    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        super.channelDisconnected(ctx, e);
        System.out.println("channelDisconnected");
    }

    /**
     * 捕获异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        super.exceptionCaught(ctx, e);
        System.out.println("exceptionCaught");
    }

    /**
     * 接受消息
     */
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        super.messageReceived(ctx, e);
//		System.out.println("messageReceived");
        System.out.println("服务器端向客户端回复内容:"+e.getMessage());
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

连接服务器,并发送信息

public class NettyClient {
    public static void main(String[] args) {
        System.out.println("netty client启动...");
        // 创建客户端类
        ClientBootstrap clientBootstrap = new ClientBootstrap();
        // 线程池
        ExecutorService boos = Executors.newCachedThreadPool();
        ExecutorService worker = Executors.newCachedThreadPool();
        clientBootstrap.setFactory(new NioClientSocketChannelFactory(boos, worker));
        clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() throws Exception {
                ChannelPipeline pipeline = Channels.pipeline();
                // 将数据转换为string类型.
                pipeline.addLast("decoder", new StringDecoder());
                pipeline.addLast("encoder", new StringEncoder());
                pipeline.addLast("clientHandler", new ClientHandler());
                return pipeline;
            }
        });
        //连接服务端
        ChannelFuture connect = clientBootstrap.connect(new InetSocketAddress("127.0.0.1", 9090));
        Channel channel = connect.getChannel();
        System.out.println("client start");
        Scanner scanner=	new Scanner(System.in);
        while (true) {
            System.out.println("请输输入内容...");
            channel.write(scanner.next());
        }
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

文章来源: blog.csdn.net,作者:小毕超,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq_43692950/article/details/107473532

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。