Netty入门指南之NIO 网络编程

news2024/9/21 2:41:11

作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客
当前专栏:Netty应用专栏_Aomsir的博客-CSDN博客

文章目录

  • 参考文献
  • 前言
  • 基础扫盲
    • 问题所在
  • 调整非阻塞
  • 技巧
  • 总结

参考文献

  • 孙哥suns说Netty
  • Netty官方文档

前言

在前面的文章中,我们详细学习并使用了NIO中的Channel和Buffer。然而,我们的示例中Channel的两端都是文件,我们一直使用的是FileChannel,没有与网络通信结合起来。从今天开始,我们将进入网络编程领域,使用NIO中的SocketChannelServerSocketChannel,为后续的Netty学习打下坚实的基础。

基础扫盲

由于我们现在的开发都是使用的SpringBoot,底层的网络通信都被Tomcat、Jetty等所处理,我们很少能够接触Socket套接字编程,这一个段落就来个基础性知识的扫盲。我们的客户端与服务端之间是使用Socket进行通信的,Socket是计算机网络中的传输层的内容,和TCP与UDP挂钩。在服务端有一个东西叫做ServerSocket,它是用来接收客户端请求,然后和客户端建立Socket连接的。下面的演示案例中,我写了一个服务端和客户端的案例。

服务端就是创建ServerSocketChannel,绑定端口启动,监听请求获取对应的SocketChannel
客户端就是去连接服务端所在机器的对应端口,发送数据相应数据即可
注意:

  • 服务端有两次阻塞,第一次阻塞是接收请求的时候,第二次阻塞是建立后连接等待接收数据的时候。说明NIO是有阻塞的,后面会写案例解决阻塞的
  • 端口,是应用进程启动后在传输层给其分配的,客户端是一个进程,服务端是一个进程。有了端口号两个进程就可以跨进程互相访问
  • 当服务端启动,就是一个主线程,接收请求和数据处理都是由主线程挨个处理,只要SocketChannel一经建立,服务端或者客户端不主动销毁就会一直在,但其中会不会有数据是两码子事
public class MyServer {
    public static void main(String[] args) throws Exception{
        // 1、创建ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        // 2、设置服务端的监听端口
        serverSocketChannel.bind(new InetSocketAddress(8000));

        List<SocketChannel> channelList = new ArrayList<>();
        ByteBuffer buffer = ByteBuffer.allocate(20);

        // 3、接受客户端的连接,让它一直转(我们不知道啥时候会接收到请求)
        while (true) {
            // 4、SocketChannel代表服务端与Client链接的一个通道
            System.out.println("等待连接服务器...");

            // 发生阻塞,服务端在等待客户端的请求,接收到才会放行
            SocketChannel socketChannel = serverSocketChannel.accept();
            System.out.println("服务器已连接..." + socketChannel);

            // 4-1、接受一个客户端就存一个
            channelList.add(socketChannel);

            // 5、client与服务端 通信 NIO
            for (SocketChannel channel : channelList) {
                System.out.println("开始实际的数据通信...");

                // 此处阻塞,对于的IO通信的阻塞,将channel中的数据读取到buffer中
                channel.read(buffer);

                // 开启读模式
                buffer.flip();
                CharBuffer result = StandardCharsets.UTF_8.decode(buffer);
                System.out.println("result = " + result);

                // 读完后开启写模式
                buffer.clear();

                System.out.println("实际的通信已经结束...");
            }
        }
    }
}
public class MyClient {
    public static void main(String[] args) throws Exception{
        // 1、创建客户端channel,并连接8000端口服务端
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress(8000));
        
        // 2、向服务端发送数据
        socketChannel.write(Charset.defaultCharset().encode("你好,我是Aomsir\n"));
        System.out.println("-------------------------------------");
    }
}

问题所在

在上面的案例中,服务端程序会面临两次阻塞。首先,在接收连接请求时,服务端程序需要等待新的连接请求。其次,一旦建立连接后,服务端可能需要等待客户端发送数据。这可能会导致一些问题:

  1. 单客户端多次请求问题: 如果今天只有一个客户端请求,按照上面的案例代码我们知道服务端在处理完这个客户端的请求后双方都没有选择断开Channel,那客户端第一次给服务端发送完数据后就会回到循环的开始一直阻塞新的连接请求到来,而不会去执行下面的IO处理,导致第一个客户端的后续请求得不到处理。这是当前单线程服务端模型的局限性,只有在接受新连接后才能处理请求。 这是由于ServerSocketChannel等待客户端请求时的阻塞
  2. 客户端保持连接问题: 第一个客户端发送完数据后没有断开与服务端的连接,服务端channelList中第一个Channel永远是第一个客户端的,服务端会一直等待来自该客户端的数据,但是第一个客户端已经没有数据发送了,这会导致服务端阻塞无法处理其他连接,因为它会一直在等待当前连接上的数据。这是由于SocketChannel处理IO数据时的阻塞。这需要特别处理的情况,通常可以通过设置超时或实现多线程来解决。

调整非阻塞

在上面的代码中,我们使用了标准的NIO编程方法,但同时又称它为非阻塞编程。然而,在代码中的两次阻塞操作似乎没有展现出非阻塞的特性,这是因为我们还未进行必要的设置。通过对ServerSocketChannel和SocketChannel的配置,我们可以将它们转换为非阻塞模式。只需执行以下两行代码:serverSocketChannel.configureBlocking(false);socketChannel.configureBlocking(false);

一旦这些设置生效,之前阻塞的客户端请求接收和IO处理不再会阻塞整个程序。当没有客户端请求或没有进行IO请求时,从ServerSocketChannel获取的SocketChannel将为null,从SocketChannel中读取的字节数将为0。为了进一步说明这一点,以下是示例代码:

public class MyServer1 {
    public static void main(String[] args) throws Exception{
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        // 设置serverSocketChannel为非阻塞,解决连接阻塞
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(8000));

        List<SocketChannel> channelList = new ArrayList<>();
        ByteBuffer buffer = ByteBuffer.allocate(20);

        while (true) {
            // 此处等待请求不会阻塞,如果此时没有客户端连接进来,会返回null
            SocketChannel socketChannel = serverSocketChannel.accept();

            // 4、如果接收到的socketChannel不为null,则将其添加到channelList中
            if (socketChannel != null) {
                // 设置socketChannel为非阻塞,解决数据读取阻塞
                socketChannel.configureBlocking(false);
                channelList.add(socketChannel);
            }

            // 5、客户端与服务端 通信 NIO
            for (SocketChannel channel : channelList) {

                // 此处从channel中读取数据到buffer中会阻塞
                int read = channel.read(buffer);
                if (read > 0) {
                    System.out.println("开始实际的数据通信...");

                    buffer.flip();  // 开启读模式
                    CharBuffer result = StandardCharsets.UTF_8.decode(buffer);
                    System.out.println("result = " + result);
                    buffer.clear();  // 开启写模式

                    System.out.println("实际的通信已经结束...");
                }
            }
        }
    }
}

技巧

对于一个Java类,比如我们的客户端,我们可能需要在IDEA中运行多个。但是默认情况下我们在IDEA里面将客户端启动以后,它处于一直运行的状态,如果我这个时候再去点击运行,那就会将这个客户端重新运行,而不是启动一个新的进程,我们可以按照下面的方式进行设置。

  1. 打开IntelliJ IDEA,并确保你的项目已经打开。
  2. 在IDEA的顶部菜单栏中,点击 “Run”(运行)。
  3. 选择 “Edit Configurations”(编辑配置)。
  4. 在左侧窗格中,点击加号(+)以创建一个新的运行配置。
  5. 选择 “Application”(应用程序)作为配置类型。
  6. 在 “Name”(名称)字段中,为你的运行配置命名,例如 “Client1”。
  7. 在 “Main class”(主类)字段中,指定你想要运行的Java客户端的主类。
  8. 在 “Program arguments”(程序参数)字段中,如果有需要的话,添加客户端程序的参数。
  9. 在 “Working directory”(工作目录)字段中,指定客户端应运行的工作目录。
  10. 点击 “OK” 以保存该运行配置。
  11. 重复上述步骤,创建其他客户端的运行配置,每个配置使用不同的名称,主类和参数。
  12. 现在,你可以通过选择不同的运行配置来启动不同的客户端。在顶部工具栏 中,选择你想要运行的配置,然后点击运行按钮(绿色的播放按钮)。
    在这里插入图片描述

总结

在今天的文章中,我们将深入探讨SocketChannel和ServerSocketChannel,这两个在网络通信中起着关键作用的组件。我们将揭开网络通信中服务端的两次阻塞现象的神秘面纱,并详细介绍NIO如何有效地处理非阻塞。让我们一起扫除对这些概念的盲点,深入理解网络通信的内在机制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1193775.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅聊反射系数——为何有共轭?

文章目录 1、基于行波理论的行波反射系数2、共轭匹配与功率波反射系数3、总结 不知道大家是否有和我一样&#xff0c;有下列疑惑&#xff1a;为什么反射系数定义中分子有的时候存在共轭&#xff0c;有的时候又没有共轭。比如&#xff1a; 通俗解释就是&#xff1a;一般来说&…

AD教程 (十一)封装的统一管理

AD教程 (十一)封装的统一管理 PCB封装添加 一个一个手动添加&#xff0c;效率太低&#xff0c;不建议使用 使用封装管理器快速添加&#xff0c;根据BOM表&#xff08;元器件清单&#xff09;&#xff0c;修改PCB封装 点击工具&#xff0c;选择封装管理器&#xff0c;进入封装…

MSVCP140_CODECVT_IDS.dll丢失怎么办?推荐三个解决方法帮你解决

MSVCP140_CODECVT_IDS.dll是Microsoft Visual C 2015 Redistributable的一个组件&#xff0c;它包含了一些运行时库文件。当您在运行某些程序时&#xff0c;可能会遇到“msvcp140_codecvt_ids.dll丢失”的错误提示。为了解决这个问题&#xff0c;您可以尝试以下三种方法&#x…

Flutter笔记:绘图示例 - 一个简单的(Canvas )时钟应用

Flutter笔记 绘图示例 - 一个简单的&#xff08;Canvas &#xff09;时钟应用 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_2855…

kubernetes istio

目录 一、部署 二、部署示例应用 三、部署遥测组件 四、流量管理 五、熔断 官网&#xff1a;https://istio.io/latest/zh/about/service-mesh/ 一、部署 提前准备好文件 tar zxf 15t10-1.19.3-linux-amd64.tar.gz cd 15t10-1.19.3/ export PATH$PWD/bin:$PATHistioctl install …

一篇文章让你了解Java中的继承

目录 继承一.什么是继承二.为什么要使用继承三.继承的语法四.继承中有重复怎么办&#xff1f;1.**访问原则** 五.super和this1.**this**2.**super**3.**super注意事项**4.**super和this异同点**六.构造方法的引入1.父类不带参数的构造方法2.父类带有参数的构造方法 七.继承中的…

基于Docker容器DevOps应用方案

文章目录 基于docker容器DevOps应用方案环境基础配置1.所有主机永久关闭防火墙和selinux2.配置yum源3.docker的安装教程 配置主机名与IP地址解析部署gitlab.server主机1.安装gitlab2.配置gitlab3.破解管理员密码4.验证web页面 部署jenkins.server主机1.部署tomcat2.安装jenkins…

计算机技术专业CSIT883系统分析与项目管理介绍

文章目录 前言一、学科学习成果二、使用步骤三、最低出勤要求四、讲座时间表五、项目管理 前言 本课程介绍了信息系统开发中的技术和技术&#xff0c;以及与管理信息技术项目的任务相关的方法和过程。 它研究了系统分析师、客户和用户在系统开发生命周期中的互补角色。 它涵盖…

基于SSM的中学课内小说阅读与学习系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

关于Maven中pom.xml文件不报错但无法导包解决方法

问题 我的pom文件没有报红&#xff0c;但是依赖无法正常导入。 右下角还总出现这种问题。 点开查看报错日志。大致如下 1) Error injecting constructor, java.lang.NoSuchMethodError: org.apache.maven.model.validation.DefaultModelValidator: method <init>()V no…

【数据结构】树与二叉树(九):二叉树的后序遍历(非递归算法NPO)

文章目录 5.2.1 二叉树二叉树性质引理5.1&#xff1a;二叉树中层数为i的结点至多有 2 i 2^i 2i个&#xff0c;其中 i ≥ 0 i \geq 0 i≥0。引理5.2&#xff1a;高度为k的二叉树中至多有 2 k 1 − 1 2^{k1}-1 2k1−1个结点&#xff0c;其中 k ≥ 0 k \geq 0 k≥0。引理5.3&…

【考研数据结构代码题4】求树中度为1的结点数(递归方式)

题目&#xff1a;用C语言描述树的孩子兄弟链表结构&#xff0c;并编写递归程序求树中度为1的结点数 难度&#xff1a;★★ 算法思路&#xff1a;递归地遍历当前结点的左孩子子树与右兄弟子树&#xff0c;分别求二者中度为1的结点数记为h1,h2,若当前结点仅有1个结点&#xff0c;…

好用的vscode插件

一、代码管理 git GitLens — Git supercharged Git History gitignore 项目管理 Project Manager 管理多个项目 Todo Tree 快速定位代码中的todo WakaTime 用于在编程活动中自动统计工作量、代码提交和时间跟踪等 VS Code Counter 该插件用于帮助我们统计项目代码的行数…

防火墙部署模式 -- 单臂路由模式

防火墙单臂路由部署模式 如图&#xff0c;PC 1与PC 2通信&#xff0c;想在中间加上防火墙对其进行监控&#xff0c;并实现对两台设备的通信阻断&#xff0c;可以在交换机的另一个接口上连接防火墙&#xff0c;交换机将两台PC发送的数据引流到防火墙上&#xff0c;防火墙也配置下…

virtualBox虚拟机局域网访问配置

在VirtualBox中&#xff0c;桥接网络是一种网络连接类型&#xff0c;它允许虚拟机连接到物理网络上的路由器或交换机&#xff0c;在物理网络上获得独立的网络地址和访问权限。 一、设置VirtualBox桥接网络的步骤&#xff1a; 打开VirtualBox软件&#xff0c;并选择你想要配置…

P1547 [USACO05MAR] Out of Hay S 题解

文章目录 题目描述输入格式输出格式样例样例输入样例输出 完整代码 题目描述 Bessie 计划调查 N N N&#xff08; 2 ≤ N ≤ 2 000 2 \leq N \leq 2\,000 2≤N≤2000&#xff09;个农场的干草情况&#xff0c;它从 1 1 1 号农场出发。农场之间总共有 M M M&#xff08; 1 ≤…

Matlab导出高清图片方法

一、背景 使用matlab绘制图片后&#xff0c;需要将图片导出为.jpg或.eps格式以便后期使用。但通过文件–另存为.jpg时&#xff0c;并没有清晰度选择&#xff0c;导出的图片只有30几k&#xff0c;以至于图片很模糊。 二、Matlab导出高清图片方法 文件—导出设置 1、大小&…

kubernetes etcd

目录 一、备份 二、回复 官网&#xff1a; https://v1-25.docs.kubernetes.io/zh-cn/docs/tasks/administer-cluster/configure-upgrade-etcd/#restoring-an-etcd-cluster 一、备份 从镜像中拷贝etcdctl二进制命令 输入ctrlpq快捷键&#xff0c;把容器打入后台 docker run…

opencv dnn模块 示例(22) 目标检测 object_detection 之 yolov7

在YOLOv6 初版出来不久&#xff0c;YOLOv7就立马横空出世了。与YOLOv5、YOLOv6不同&#xff0c;YOLOv7是由YOLOv4团队的原班人马提出的&#xff08;官方出品&#xff09;。从论文的表上来看&#xff0c;目前YOLOv7无论是在实时性还是准确率上都已经超过了当时已知的所有目标检测…

【二叉树】如何构建一个包含大量随机数节点的二叉树测试用例

【二叉树】如何构建一个包含大量随机数节点的二叉树测试用例 前言一、案例准备二、自动生成随机二叉树工具类&#xff08;TreegenerateUtils&#xff09;三、如何调用随机二叉树工具类&#xff08;TreegenerateUtils&#xff09;&#xff1f; 前言 今天笔者在测试有关二叉树的…