【网络编程socket】java NIO编程示例以及流程详解

2023-11-06


相关文章:
【网络编程socket】图解 Java NIO BIO MIO AIO 四大IO模型与原理
【网络编程socket】BIO & Socket和ServerSocket API & 入门例子
【网络编程socket】java NIO编程示例以及流程详解

1. NIO架构图

在这里插入图片描述

2. 服务端代码


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;

public class ServerSocketChannels implements Runnable {

    private ServerSocketChannel serverSocketChannel;

    protected Selector selector;

    private volatile boolean stop;


    public ServerSocketChannels() {

    }

    public ServerSocketChannels(int port) {

        try {
            //创建多路复用器selector,工厂方法
            selector = Selector.open();
            //创建ServerSocketChannel,工厂方法
            serverSocketChannel = ServerSocketChannel.open();
            //绑定ip和端口号,默认的IP=127.0.0.1,对连接的请求最大队列长度设置为backlog=1024,
            // 如果队列满时收到连接请求,则拒绝连接
            serverSocketChannel.socket().bind(new InetSocketAddress(port), 1024);
            //设置非阻塞方式
            serverSocketChannel.configureBlocking(false);
            //注册serverSocketChannel到selector多路服用器上面,监听accept请求
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("the time is start port = " + port);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void stop() {
        this.stop = true;
    }

    @Override
    public void run() {
        //如果server没有停止
        while (!stop) {
            try {
                //selector.select()会一直阻塞到有一个通道在你注册的事件上就绪了
                //selector.select(1000)会阻塞到1s后然后接着执行,相当于1s轮询检查
                selector.select(1000);
                //找到所有准备接续的key
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                SelectionKey key = null;
                while (it.hasNext()) {
                    key = it.next();
                    it.remove();
                    try {
                        //处理准备就绪的key
                        handle(key);
                    } catch (Exception e) {
                        if (key != null) {
                            //请求取消此键的通道到其选择器的注册
                            key.cancel();
                            //关闭这个通道
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        if (selector != null) {
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void handle(SelectionKey key) throws IOException {
        //如果key是有效的
        if (key.isValid()) {
            //监听到有新客户端的接入请求
            //完成TCP的三次握手,建立物理链路层
            if (key.isAcceptable()) {
                doAccept(key);
            }
            //监听到客户端的读请求
            if (key.isReadable()) {
                doReadable(key);
            }
        }
    }


    /**
     * 处理客户端连接事件
     *
     * @param key
     * @throws IOException
     */
    public void doAccept(SelectionKey key) throws IOException {
        ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
        SocketChannel sc = (SocketChannel) ssc.accept();
        //设置客户端链路为非阻塞模式
        sc.configureBlocking(false);
        //将新接入的客户端注册到多路复用器Selector上
        sc.register(selector, SelectionKey.OP_READ);
    }

    public void doReadable(SelectionKey key) throws IOException {
        //获得通道对象
        SocketChannel sc = (SocketChannel) key.channel();
        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
        //从channel读数据到缓冲区
        int readBytes = sc.read(readBuffer);
        if (readBytes > 0) {
            //Flips this buffer.  The limit is set to the current position and then
            // the position is set to zero,就是表示要从起始位置开始读取数据
            readBuffer.flip();
            //eturns the number of elements between the current position and the  limit.
            // 要读取的字节长度
            byte[] bytes = new byte[readBuffer.remaining()];
            //将缓冲区的数据读到bytes数组
            readBuffer.get(bytes);
            String body = new String(bytes, "UTF-8");
           System.out.println(Thread.currentThread().getName() + ": the time server receive order: " + body);
            String currenttime = "query time order".equals(body) ? new Date(System.currentTimeMillis()).toString() : "bad order";
            doWrite(sc, currenttime);
        } else if (readBytes < 0) {
            key.channel();
            sc.close();
        }
    }


    public void doWrite(SocketChannel channel, String response) throws IOException {
        if (response != null && !"".equals(response)) {
            byte[] bytes = response.getBytes();
            //分配一个bytes的length长度的ByteBuffer
            ByteBuffer write = ByteBuffer.allocate(bytes.length);
            //将返回数据写入缓冲区
            write.put(bytes);
            write.flip();
            //将缓冲数据写入渠道,返回给客户端
            channel.write(write);
        }
    }


    public static void main(String[] args) {
        int port = 8010;
        ServerSocketChannels server = new ServerSocketChannels(port);
        // ServerSocketChannels2 server = new ServerSocketChannels2(port);
        new Thread(server, "timeserver-001").start();
    }
}



3. 客户端代码


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class TimeClientHandler implements Runnable {

    //服务器端的ip
    private String host;
    //服务器端的端口号
    private int port;
    //多路服用选择器
    private Selector selector;

    private SocketChannel socketChannel;

    private volatile boolean stop;


    public TimeClientHandler(String host, int port){
        this.host = host == null ? "127.0.0.1": host;
        this.port = port;
        try {
            //初始化一个Selector,工厂方法
            selector = Selector.open();
            //初始化一个SocketChannel,工厂方法
            socketChannel = SocketChannel.open();
            //设置非阻塞模式
            socketChannel.configureBlocking(false);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

    }


    /**
     * 首先尝试连接服务端
     * @throws IOException
     */
    public void doConnect() throws IOException {
        //如果连接成功,像多路复用器selector监听读请求
        if(socketChannel.connect(new InetSocketAddress(this.host, this.port))){
            socketChannel.register(selector, SelectionKey.OP_READ);
            //执行写操作,像服务器端发送数据
            doWrite(socketChannel);
        }else {
            //监听连接请求
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
        }
    }


    public static void doWrite(SocketChannel sc) throws IOException {
        //构造请求消息体
        byte [] bytes = "query time order".getBytes();
        //构造ByteBuffer
        ByteBuffer write = ByteBuffer.allocate(bytes.length);
        //将消息体写入发送缓冲区
        write.put(bytes);
        write.flip();
        //调用channel的发送方法异步发送
        sc.write(write);
        //通过hasRemaining方法对发送结果进行判断,如果消息全部发送成功,则返回true
        if(!write.hasRemaining()){
            System.out.println("send order to server successd");
        }
    }

    @Override
    public void run() {
        try {
            doConnect();
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        while (!stop){
            try {
                selector.select(1000);
                Set<SelectionKey> keys =  selector.selectedKeys();
                Iterator<SelectionKey> its =keys.iterator();
                SelectionKey key = null;
                while (its.hasNext()){
                    key = its.next();
                    its.remove();
                    try {
                        handle(key);
                    }catch (Exception e){
                        if(key != null){
                            key.cancel();
                            if(key.channel() != null){
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
    }

    public  void handle(SelectionKey key) throws IOException {
        if(key.isValid()){
            SocketChannel sc = (SocketChannel) key.channel();
            if(key.isConnectable()){
                //如果连接成功,监听读请求
                if(sc.finishConnect()){
                    sc.register(this.selector, SelectionKey.OP_READ);
                    //像服务端发送数据
                    doWrite(sc);
                }else{
                    System.exit(1);
                }
            }
            //监听到读请求,从服务器端接受数据
            if(key.isReadable()){
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                int readBytes = sc.read(byteBuffer);
                if(readBytes > 0){
                    byteBuffer.flip();
                    byte []  bytes = new byte[byteBuffer.remaining()];
                    byteBuffer.get(bytes);
                    String body = new String(bytes,"UTF-8");
                    System.out.println("now body is "+ body);
                    stop = true;

                }else if(readBytes < 0){
                    key.cancel();
                    sc.close();
                }


            }
        }
    }


        public static void main(String[] args) {
            int port = 8010;
            TimeClientHandler client = new TimeClientHandler("",port);
            new Thread(client,"client-001").start();
        }

}

4. 运行

4.1 非nio客户端

启动服务端后,可以用任意的方式给服务端发送数据,而客户端可以是telnet,可以是原始的sokety,也是nio写的客户端。

客户端向服务器发送一个非字符串“query time order”,然后服务器回错误提示“bad order”,意思是错误的请求。

我们直接telnet localhost 8010,分别输入一些字符,分别输入“a,1,3”查看:
在这里插入图片描述
服务端控制台输出:

timeserver-001: the time is start port = 8010
timeserver-001: the time server receive order: a
timeserver-001: the time server receive order: 1
timeserver-001: the time server receive order: 3

4.2 nio客户端

nio客户端向服务器发送一个字符串“query time order”,然后服务器回写一个日期。

服务端控制台输出:

the time is start port = 8010
timeserver-001: the time server receive order: query time order

客户端控制台输出:

send order 2 server successd
now body is Tue Jun 15 10:39:03 CST 2021  '收到了服务器端发来的日期'




参考:

《java NIO示例以及流程详解》 参考主体
《Java Nio服务端怎么发送数据给Socket Io》
《Socket+NIO实现客户端与服务器的通信的Demo》
《NIO包下SocketChannel的用法》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【网络编程socket】java NIO编程示例以及流程详解 的相关文章

  • C# 与 JAVA 接口实例

    我不知道该如何回答我的问题 它是关于Android可以实例化接口的 我正在尝试用 C 来做 现在我非常确定 Java 和 C 的规则是不能创建抽象和接口的实例 但我很想知道Android是如何做到这一点的 在 Android 中你可以这样做
  • 我可以在 Java 枚举上使用构建器模式吗

    我正在重写一些代码 并且我已经决定了重新创建类的方法 因为有固定数量的工作表 我将它们创建为枚举 这是基于构建器模式与伸缩构造器的可读性的决定 我的代码获取一些 xls 文件 添加标题 并从其他 xls 文件中读取一些 也许还有一些子表 然
  • 相当于 java PBKDF2WithHmacSHA1 的 Python

    我的任务是构建一个 API 的使用者 该 API 需要带有 UNIX 时间种子值的加密令牌 我看到的示例是使用我不熟悉的 Java 实现的 在阅读文档和其他堆栈文章后一直无法找到解决方案 使用javax crypto SecretKey j
  • Google App Engine 数据存储写入:如何远程启用/禁用只读模式?

    在阅读备份时GAE 的数据存储 https developers google com appengine docs adminconsole datastoreadmin where 我们强烈建议您在备份或恢复期间将应用程序设置为只读模式
  • Java中如何合并两个数组?

    它不是连接而是合并两个数组 使它们成为名称值对的数组 firstarray a aa aaa secondarray b bb bbb result a b aa bb aaa bbb 最好的方法是什么 in Java public sta
  • 我需要在 Java 9 中使用哪个模块才能使用 JPA?

    我正在使用一个需要 JPA 的项目测试 Java 9 javax persistence 类 当我添加module info java并声明我的模块 下的所有类javax persistece包变得不可用 我搜索了很多 但找不到在 Java
  • Java 7u51/7u55 带星号的清单变量

    我正在部署一个小程序 其中包含清单中的下一个变量 Manifest Version 2 0 Ant Version Apache Ant 1 8 2 Trusted Library true Permissions all permissi
  • Java:等于和==

    让我们看看我们有 2 个对用户定义类实例的引用 即 Java 中的 a 和 b 会不会有一种情况 a b 但 a equals b 返回 false 当然 实施 equals 完全取决于班级 所以我可以写 class Foo public
  • 我从 String placeName = placeText.getText().toString(); 收到空指针异常

    您好 想从编辑文本中获取地名并在地图上标记 这是我的代码 其中出现空指针异常 请帮助我应该做什么以及哪里出错了 因为我从对话框中的编辑文本字段获取地名 View layout View inflate this R layout alert
  • 如何对 jar 文件资源使用 File.separator?

    我正在尝试读取位于 jar 文件中的属性文件 我想使用 File separator 因为应用程序将在多个平台上运行 我正在构建路径如下 jarFilePath jar file jarFile getAbsolutePath jarPro
  • 使用 POJO 仅更新 JOOQ 记录中已更改的字段

    我想使用 POJO 作为源来更新 JOOQ 记录中已更改的字段 Record from Object http www jooq org javadoc 3 8 x org jooq Record html from java lang O
  • Spark SQL 失败,因为“常量池已超过 JVM 限制 0xFFFF”

    我在 EMR 4 6 0 Spark 1 6 1 上运行此代码 val sqlContext SQLContext getOrCreate sc val inputRDD sqlContext read json input try inp
  • 如何在 PuTTY 中保存并运行 Java 文件?

    我是 AWS 亚马逊网络服务 的新手 所以这可能是一个基本问题 我在 AWS 上创建了一个 EC2 实例 我有一台 Windows 计算机 因此我使用 PUTTY 来连接 Linux 实例 连接到我的 EC2 实例后 我使用以下命令编写 J
  • 无法删除临时文件夹(有时)

    当我启动应用程序时 我创建一个临时文件夹 public static File createTempDir String name throws IOException File tempDir File createTempFile na
  • Java 执行器和长寿命线程

    我继承了一些使用 Executors newFixedThreadPool 4 的代码运行 4 个长寿命线程来完成应用程序的所有工作 这是推荐的吗 我读过Java 并发实践 https rads stackoverflow com amzn
  • 为什么 OOP 中静态类的最佳实践有所不同?

    我目前正在阅读有关 Java 最佳实践的内容 我发现根据这本书 https rads stackoverflow com amzn click com 0321356683我们必须优先选择静态类而不是非静态类 我记得在 C 最佳实践中 我们
  • 如果 @transactional 在类级别应用,如何拦截 @transactional 参数

    我想捕获 transactional 的参数 如果它应用于类级别 例如如果 transactional应用在方法级别 例如 class A transactional readOnly true public void someMethod
  • “mvn”不被识别为内部或外部命令、可操作程序或批处理文件

    可能是之前问过问题 但我发现了非常奇怪的错误 当我跑步时mvn versionbin 中的命令给出了版本和其他信息 但是 当我跑到垃圾箱外面时 它会出现异常 下面提到的结果 C Program Files apache maven 3 0
  • 如何在 tomcat 上部署 Java Web 应用程序 (.war)?

    我有一个 warJava Web 应用程序的文件 现在我想将它上传到我的 ftp 服务器 以便我可以执行它 我应该执行哪些步骤来运行它 webapp的上下文路径是 mywebapp Edit 实际上 我的 ftp 服务器名称是ftp bil
  • RetentionPolicy CLASS 与 RUNTIME

    两者之间有什么实际区别RetentionPolicy CLASS and RetentionPolicy RUNTIME 看起来两者都被记录到字节码中 并且无论如何都可以在运行时访问 无论如何 两者都可以在运行时访问 那不是那个javado

随机推荐

  • 理论总结

    作业1 2 1 Python程序是区分大小写的 2 解释程序对高级语言编写的程序是一边翻译 一边执行的 下次执行同样的程序时 还必须重新翻译 3 Python是一种用途广泛 解释型 面向对象的程序设计语言 4 程序设计语言包括机器语言 汇编
  • Into Clause VS Let Clause

    1 Into Clause 用来将select join 或者group的结果存储到一个临时变量中 目的 在之后的查询中需要使用此结果 例如 var developersGroupedByLanguage from d in develop
  • 面了一个测试工程师,明显感觉他背了很多面试题...

    最近有朋友去字节面试 面试前后进行了20天左右 包含4轮电话面试 1轮笔试 1轮主管视频面试 1轮hr视频面试 据他所说 80 的人都会栽在第一轮面试 要不是他面试前做足准备 估计都坚持不完后面几轮面试 其实 第一轮的电话面试除了一些常规的
  • LRU缓存淘汰算法

    概念理解 1 LRU是Least Recently Used的缩写 即最近最少使用页面置换算法 是为虚拟页式存储管理服务的 2 操作系统课程里有学过 在内存不够的场景下 淘汰就内容的策略 淘汰掉最不经常使用 LRU原理 可以用一个特殊的栈来
  • CentOS 7安装Gnome GUI 图形界面

    http www centoscn com image text config 2015 0528 5552 html
  • jwt在线解密工具分享

    前言 之前调用一个第三方api的时候 看到需要在Authorization填写bearer token 英文不好 看成了熊 bear 心里很疑惑 实际上 bearer 指的是持票人 Bearer Token用于授权访问资源 任何Bearer
  • splunk之获取数据(Ingesting Data)

    Ingesting Data 下载数据地址 http splk it f1data use uname in the Username field and 5p1unkbcup for the Password field
  • GDB调试命令详解

    GDB是什么 调试程序 程序中出现的语法错误可以借助编译器解决 但逻辑错误则只能靠自己解决 实际场景中解决逻辑错误最高效的方法 就是借助调试工具对程序进行调试 所谓调试 Debug 就是让代码一步一步慢慢执行 跟踪程序的运行过程 比如 可以
  • Vue如何实现反向代理(配置proxy)

    Vue如何实现反向代理 那问题来了 反向代理是什么 反向代理 Reverse Proxy 实际运行方式是指以代理服务器来接受internet上的连接请求 然后将请求转发给内部网络上的服务器 并将从服务器上得到的结果返回给internet上请
  • Mysql的B+树高度计算

    问题 假设B 树的高度是2 一行数据的记录大小是1K 主键ID是int类型 问 该B 树存放的总记录数 知识点 Mysql的默认存储引擎是Innodb Innodb的最小存储单位是页 一页大小等于16K B 树的叶子节点存放数据 内部节点存
  • 黑马 Spring_day01

    Spring day01 今日目标 掌握Spring相关概念 完成IOC DI的入门案例编写 掌握IOC的相关配置与使用 掌握DI的相关配置与使用 1 课程介绍 对于一门新技术 我们需要从为什么要学 学什么以及怎么学这三个方向入手来学习 那
  • 计算机提示d3dcompiler43.dll缺失怎么修复,多个解决方法分享

    在游戏玩家中 遇到游戏提示找不到 d3dcompiler43 dll 文件的情况并不罕见 这使得许多玩家在启动游戏时感到困扰 因为这意味着他们可能无法正常运行游戏 那么 d3dcompiler43 dll 文件到底是什么呢 为什么游戏会提示
  • 数据库实现学生管理系统

    1 QT将数据库分为三个层次 1 gt 数据库驱动层 QSqlDriver QSqlDriverCreator QSqlDriverCreatorBase QSqlDriverPlugin 2 gt sql接口层 QSqlDatabase
  • 在剪贴板上有大量信息,是否保留其内容, 以便此后粘贴到其他程序中? VBA 对策

    在剪贴板上有大量信息 是否保留其内容 以便此后粘贴到其他程序中 对策a 是文件关闭前 随便复制一个空单元格就可以了 对策b Application DisplayAlerts False 关闭任何提醒 但复制的信息将仍旧保存在剪贴板中 对策
  • Qt数据库编程

    Qt数据库编程 版本说明 版本 作者 日期 备注 0 1 loon 2018 10 25 初稿 目录 文章目录 Qt数据库编程 版本说明 目录 一 需求和目的 二 使用说明 1 Qt 5 6 0的数据库操作类 2 Driver Layer
  • 堆栈指针SP

    1 什么是堆栈指针 在51系列单片机里 堆栈指针sp在片内RAM128B中开辟栈区 并随时跟踪栈顶地址 它是按 先进后出 的原则存取数据 开机复位后 单片机栈底地址为07H 主要用来保存临时数据 局部变量和中断 调用子程序程序的返回地址 堆
  • java的传值调用什么_Java的传值调用

    本文非引战或diss 只是说出自己的理解 欢迎摆正心态观看或探讨 引子 之所以写这篇文章是因为前些天写了一篇 Java中真的只有值传递么 探讨了网上关于Java只有值传递的说法 当时写这篇文章的缘由是因为之前看的文章讲解的Java只有值传递
  • pytorch基础学习笔记

    import torch import numpy as np def describe x print Type format x type print Shape size format x shape print Values n f
  • 第四题

    第四题 找MD5解密网址 ok
  • 【网络编程socket】java NIO编程示例以及流程详解

    文章目录 1 NIO架构图 2 服务端代码 3 客户端代码 4 运行 4 1 非nio客户端 4 2 nio客户端 参考 相关文章 网络编程socket 图解 Java NIO BIO MIO AIO 四大IO模型与原理 网络编程socke