Java Socket 网络编程实例(阻塞IO、非阻塞IO、多路复用Selector、AIO)

news2025/1/15 12:53:03

文章目录

  • 1. 概述
  • 2. TCP 阻塞式IO 网络编程实例
    • 2.1 TCP网络编程服务端
    • 2.2 ByteBufferUtil
    • 2.3 客户端代码
    • 2.4 运行截图
  • 3. TCP 非阻塞式IO 网络编程实例
    • 3.1 服务端
    • 3.2 客户端
    • 3.3 运行截图
  • 4. 多路复用
    • 4.1 服务器端
    • 4.2 客户端
    • 4.3 运行截图
  • 5. AIO
    • 5.1 AIO 服务端
    • 5.2 客户端
    • 5.3 运行截图
  • 6. Channel / Buffer
    • 6.1 Channel
    • 6.2 ByteBuffer
  • 参考文献

1. 概述

  • 网络编程, 就是编写程序, 使两台联网的电脑可以交换数据,
  • 套接字是网络数据传输用的软件设备, 用来连接网络的工具
  • 在 linux中 socket被认为是文件中的一种, 在网络数据传输过程中, 使用文件I/O的相关函数
  • socket 帮助程序员封装了网络的底层细节,如:错误检测、包大小、包分解、包重传、网络地址等,让程序员将网络连接看作可以读/写字节的流
  • 套接字常用网络协议: TCP、UDP

之前还有一篇文章: Linux C++ Socket 套接字、select、poll、epoll 实例

套接字进行网络连接流程, 如下图:

服务器端:

  1. 创建服务器套接字 socket()
  2. 绑定端口 bind()
  3. 监听端口 listen()
  4. 接受客户端请求 accept()
  5. 读取客户端请求的数据 read()
  6. 返回客户端要响应的数据 write()
  7. 关闭与客户端的连接 close()
  8. 关闭服务器套接字 close()

客户端:

  1. 创建客户端套接字 socket()
  2. 连接服务端 connect()
  3. 请求服务端数据, 发送操作数和操作符到服务器 write()
  4. 从服务器读取操作结果 read()
  5. 关闭客户端套接字 close()

流程图如下, 具体代码示例可以看下面的 2. TCP 阻塞式IO 网络编程实例在这里插入图片描述

2. TCP 阻塞式IO 网络编程实例

accept 和 read 都是阻塞的, 当 accept 到新连接, 或者 read 到数据程序才往下走

为了提高服务端处理能力, 一个客户端连接一个线程处理

不能一个线程处理多个客户端, 某个客户端会阻塞这个线程处理其他客户端

在这里插入图片描述

2.1 TCP网络编程服务端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;

public class BlockServer {
    public static void main(String[] args) throws IOException {
        // 0. ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(16);
        // 1. 创建了服务器
        ServerSocketChannel ssc = ServerSocketChannel.open();

        // 2. 绑定监听端口
        ssc.bind(new InetSocketAddress(8080));

        // 3. 连接集合
        List<SocketChannel> channels = new ArrayList<>();
        while (true) {
            // 4. accept 建立与客户端连接, SocketChannel 用来与客户端之间通信
            System.out.println("等待客户端连接...");
            SocketChannel sc = ssc.accept(); // 阻塞方法,线程停止运行
            System.out.println("接收到客户端连接: " + sc);
            channels.add(sc);
            for (SocketChannel channel : channels) {
                // 5. 接收客户端发送的数据
                System.out.println("开始读取客户端中的数据:" + channel);
                channel.read(buffer); // 阻塞方法,线程停止运行
                buffer.flip();

                String request = ByteBufferUtil.read(buffer);
                System.out.println(request);
                buffer.clear();
                System.out.println("已经读取完客户端中的数据:" + channel);
            }
        }
    }
}

2.2 ByteBufferUtil

public class ByteBufferUtil {
    public static String read(ByteBuffer byteBuffer) throws CharacterCodingException {
        CharBuffer charBuffer = StandardCharsets.UTF_8.decode(byteBuffer);
        return charBuffer.toString();
    }

    public static ByteBuffer read(String string) throws CharacterCodingException {
        return StandardCharsets.UTF_8.encode(string);
    }

    public static void main(String[] args) throws CharacterCodingException {
        System.out.println(ByteBufferUtil.read(ByteBufferUtil.read("test")));
    }

}

2.3 客户端代码

public class BlockClient {
    public static void main(String[] args) throws IOException {
        SocketChannel sc = SocketChannel.open();
        System.out.println("开始连接服务端...");
        sc.connect(new InetSocketAddress("localhost", 8080));
        String str = "test";
        System.out.println("连接服务端成功,写入数据: " + str);
        sc.write(ByteBufferUtil.read(str));
    }
}

2.4 运行截图

在这里插入图片描述
在这里插入图片描述

3. TCP 非阻塞式IO 网络编程实例

不停的轮询, 看看有没有accept 到新连接, 没有连接不阻塞等待, 继续去看看已经建立的连接有没有read到客户端的新数据, read到新数据处理, read不到不处理

为了提高服务端处理能力, 可以一个客户端连接一个线程处理, 线程不停的轮询自己要处理的客户端

也可以一个线程处理多个客户端, 相较于上面的阻塞I/O模型, 非阻塞不至于某个客户端阻塞这个线程处理其他客户端

在这里插入图片描述

3.1 服务端

ssc.configureBlocking(false); 设置为非阻塞模式

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class NonBlockServer {
    public static void main(String[] args) throws IOException, InterruptedException {
        // 0. ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(16);
        // 1. 创建了服务器
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false); // 非阻塞模式
        // 2. 绑定监听端口
        ssc.bind(new InetSocketAddress(8080));
        // 3. 连接集合
        List<SocketChannel> channels = new ArrayList<>();
        while (true) {
            // 4. accept 建立与客户端连接, SocketChannel 用来与客户端之间通信
            SocketChannel sc = ssc.accept(); // 非阻塞,线程还会继续运行,如果没有连接建立,但sc是null
            if (sc != null) {
                System.out.println("接收到客户端连接: " + sc);
                sc.configureBlocking(false); // 非阻塞模式
                channels.add(sc);
            }
            for (SocketChannel channel : channels) {
                System.out.println("开始读取客户端中的数据:" + channel);
                // 5. 接收客户端发送的数据
                int read = channel.read(buffer);// 非阻塞,线程仍然会继续运行,如果没有读到数据,read 返回 0
                if (read > 0) {
                    buffer.flip();
                    System.out.println((ByteBufferUtil.read(buffer)));
                    buffer.clear();
                    System.out.println("已经读取完客户端中的数据:" + channel);
                } else {
                    TimeUnit.MILLISECONDS.sleep(100);
                }
            }
        }
    }
}

3.2 客户端

客户端同上

3.3 运行截图

在这里插入图片描述

在这里插入图片描述

4. 多路复用

可以调用 select/poll/epoll , 阻塞在select/poll/epoll, select/poll/epoll 监听多个客户端连接事件或写入的数据, 然后这些事件可再有多个线程分一分处理掉

在这里插入图片描述

4.1 服务器端

打开选择器并将其与通道注册,监听接受连接操作:

Selector selector = Selector.open();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_ACCEPT);

监听选择器上的事件,返回已就绪的通道数量:

int count = selector.select();

获取所有事件(连接、读取):

Set<SelectionKey> keys = selector.selectedKeys();
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

public class SelectorServer {
    public static void main(String[] args) {
        try (ServerSocketChannel channel = ServerSocketChannel.open()) {
            // 绑定端口并打印通道信息
            channel.bind(new InetSocketAddress(6666));
            System.out.println(channel);

            // 打开选择器并将其与通道注册,监听接受连接操作
            Selector selector = Selector.open();
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_ACCEPT);

            // 无限循环,等待选择器上的事件
            while (true) {
                // 监听选择器上的事件,返回已就绪的通道数量
                int count = selector.select();
                System.out.println("select count: " + count);

                // 如果没有就绪的通道,则继续循环等待
                if (count <= 0) {
                    continue;
                }

                // 获取并迭代处理所有就绪的事件
                // 获取所有事件
                Set<SelectionKey> keys = selector.selectedKeys();
                // 遍历所有事件,逐一处理
                Iterator<SelectionKey> iter = keys.iterator();
                while (iter.hasNext()) {
                    SelectionKey key = iter.next();

                    // 处理接受连接事件
                    // 判断事件类型
                    if (key.isAcceptable()) {
                        ServerSocketChannel c = (ServerSocketChannel) key.channel();
                        // 必须处理
                        SocketChannel sc = c.accept();
                        sc.configureBlocking(false);
                        sc.register(selector, SelectionKey.OP_READ);
                        System.out.println("连接已建立:" + sc);
                    }
                    // 处理读取数据事件
                    else if (key.isReadable()) {
                        SocketChannel sc = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(128);
                        int read = sc.read(buffer);
                        if (read == -1) {
                            // 如果读取返回-1,表示连接已关闭
                            key.cancel();
                            sc.close();
                        } else {
                            // 否则,将缓冲区反转并打印读取的数据
                            buffer.flip();
                            System.out.println(new String(buffer.array(), StandardCharsets.UTF_8));
                        }
                    }
                    // 事件处理完毕后,从迭代器中移除,避免重复处理
                    // 处理完毕,必须将事件移除
                    iter.remove();
                }
            }
        } catch (IOException e) {
            // 打印IO异常堆栈跟踪
            e.printStackTrace();
        }
    }
}

4.2 客户端

import netty.ByteBufferUtil;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class SelectorClient {

    public static void main(String[] args) throws IOException {
        // 创建Socket通道并连接到服务器
        SocketChannel sc = SocketChannel.open();
        sc.connect(new InetSocketAddress("localhost", 6666));

        // 初始化输入和输出ByteBuffer
        ByteBuffer inputBuffer = ByteBuffer.allocate(512);
        ByteBuffer serverOutput = ByteBuffer.allocate(512);

        // 循环接收用户输入并发送给服务器
        while (true) {
            // 使用Scanner获取用户输入
            Scanner in = new Scanner(System.in);
            String input = in.nextLine();
            System.out.println("user input: " + input);

            // 清空输入缓冲区,放入用户输入,然后反转准备写入
            inputBuffer.clear();
            inputBuffer.put(input.getBytes(StandardCharsets.UTF_8));
            inputBuffer.flip();

            // 将输入数据写入Socket通道
            sc.write(inputBuffer);
            System.out.println("send to server " + input);

            // 循环读取服务器响应
            while (true) {
                // 清空服务器响应缓冲区,准备读取数据
                serverOutput.clear();
                // 从Socket通道读取数据
                sc.read(serverOutput);

                // 如果没有读取到数据,继续尝试读取
                if (!serverOutput.hasRemaining()) {
                    continue;
                }

                // 反转缓冲区,读取数据并打印
                serverOutput.flip();
                System.out.println("server response " + ByteBufferUtil.read(serverOutput));

                // 读取完成后退出内层循环
                break;
            }
        }
    }
}

4.3 运行截图

在这里插入图片描述
在这里插入图片描述

5. AIO

异步I/O模型

告诉内核启动某个操作, 并且把数据copy到用户缓冲区再通知我们

在这里插入图片描述

5.1 AIO 服务端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;

/**
 * AIO服务器类,用于演示异步IO的服务器端实现。
 * 使用AsynchronousServerSocketChannel处理客户端连接和数据传输。
 */
public class AIOServer {
    /**
     * 程序入口,初始化并启动AIO服务器。
     * 绑定服务器端口并等待客户端连接。
     *
     * @param args 命令行参数
     * @throws IOException 如果绑定端口失败
     */
    public static void main(String[] args) throws IOException {
        AsynchronousServerSocketChannel ssc = AsynchronousServerSocketChannel.open();
        ssc.bind(new InetSocketAddress(6666));
        ssc.accept(null, new AcceptHandler(ssc));
        while (true) ;
    }

    /**
     * 关闭客户端通道的方法。
     * 用于处理读取或写入操作失败时关闭通道。
     *
     * @param sc 客户端通道
     */
    private static void closeChannel(AsynchronousSocketChannel sc) {
        try {
            System.out.printf("[%s] %s close\n", Thread.currentThread().getName(), sc.getRemoteAddress());
            sc.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取数据的完成处理器,实现读取客户端数据并响应的逻辑。
     */
    private static class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
        private final AsynchronousSocketChannel sc;

        public ReadHandler(AsynchronousSocketChannel sc) {
            this.sc = sc;
        }

        /**
         * 当读取操作完成时被调用。
         * 解析读取的数据并写回响应到客户端。
         *
         * @param result     读取操作的结果
         * @param attachment 读取操作的附加上下文
         */
        @Override
        public void completed(Integer result, ByteBuffer attachment) {
            try {
                if (result == -1) {
                    return;
                }
                System.out.printf("[%s] %s read\n", Thread.currentThread().getName(), sc.getRemoteAddress());
                attachment.flip();
                String request = Charset.defaultCharset().decode(attachment).toString();
                System.out.println(request.toString());
                attachment.clear();

                attachment.put(("你好:" + request).getBytes());
                attachment.flip();
                sc.write(attachment);
                attachment.clear();

                // 读取下一个读时间
                sc.read(attachment, attachment, new ReadHandler(sc));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * 当读取操作失败时被调用。
         * 关闭客户端通道并打印异常堆栈跟踪。
         *
         * @param exc        引发的异常
         * @param attachment 读取操作的附加上下文
         */
        @Override
        public void failed(Throwable exc, ByteBuffer attachment) {
            closeChannel(sc);
            exc.printStackTrace();
        }
    }

    /**
     * 接受连接的完成处理器,用于处理客户端的连接请求。
     */
    private static class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, Object> {
        private final AsynchronousServerSocketChannel ssc;

        public AcceptHandler(AsynchronousServerSocketChannel ssc) {
            this.ssc = ssc;
        }

        /**
         * 当接受操作完成时被调用。
         * 设置读取缓冲区并开始读取客户端发送的数据。
         *
         * @param sc         接受到的客户端通道
         * @param attachment 接受操作的附加上下文
         */
        @Override
        public void completed(AsynchronousSocketChannel sc, Object attachment) {
            try {
                System.out.printf("[%s] %s connected\n", Thread.currentThread().getName(), sc.getRemoteAddress());
            } catch (IOException e) {
                e.printStackTrace();
            }
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            // 读事件由 ReadHandler 处理
            System.out.println("开始读");
            sc.read(buffer, buffer, new ReadHandler(sc));
            System.out.println("读完成");

            // 处理完第一个 accept 时,需要再次调用 accept 方法来处理下一个 accept 事件
            ssc.accept(null, this);
        }

        /**
         * 当接受操作失败时被调用。
         * 打印异常堆栈跟踪。
         *
         * @param exc        引发的异常
         * @param attachment 接受操作的附加上下文
         */
        @Override
        public void failed(Throwable exc, Object attachment) {
            exc.printStackTrace();
        }
    }
}

5.2 客户端

同 4.2

5.3 运行截图

在这里插入图片描述
在这里插入图片描述

6. Channel / Buffer

6.1 Channel

Channel: 传输数据的通道

其实和数据流挺像的,不过数据流是单向的而Channel 是双向的,可以向channel中写数据,也可以从channel中读取数据

NIO 基础组件之 Channel

6.2 ByteBuffer

ByteBuffer是Buffer子类,是字节缓冲区,特点如下所示。

大小不可变。一旦创建,无法改变其容量大小,无法扩容或者缩容;
读写灵活。内部通过指针移动来实现灵活读写;
支持堆上内存分配和直接内存分配

一文搞懂ByteBuffer使用与原理

参考文献

  • UNIX 网络编程 卷1: 套接字联网API
  • TCP/IP网络编程 尹圣雨 著 金国哲 译
  • Linux IO模式及 select、poll、epoll详解
  • 浅谈select,poll和epoll的区别
  • 黑马 Netty 课程

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

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

相关文章

Transformer学习(4)

上篇文章完成了Transformer剩下组件的编写&#xff0c;因此本文就可以开始训练。 本文主要介绍训练时要做的一些事情&#xff0c;包括定义损失函数、学习率调整、优化器等。 下篇文章会探讨如何在多GPU上进行并行训练&#xff0c;加速训练过程。 数据集简介 从网上找到一份中…

区块链--Ubuntu上搭建以太坊私有链

1、搭建私链所需环境 操作系统&#xff1a;ubuntu16.04&#xff0c;开虚拟机的话要至少4G&#xff0c;否则会影响测试挖矿时的速度 软件&#xff1a; geth客户端 Mist和Ethereum Wallet&#xff1a;Releases ethereum/mist GitHub 2、安装geth客户端 sudo apt-get update …

《C++避坑神器·二十六》结构体报重定义错误问题和std::variant同时存储不同类型的值使用方式

1、结构体重定义错误问题&#xff1a; struct person {int age; }p;p是一个已经创建好的对象&#xff0c;相当于struct person p; 如果放在头文件中容易被多个文件包含报重定义错误 typedef struct person {int age; }person;person就是struct person&#xff0c;这时候并没有…

统一响应,自定义校验器,自定义异常,统一异常处理器

文章目录 1.基本准备&#xff08;构建一个SpringBoot模块&#xff09;1.在A_universal_solution模块下创建新的子模块unified-processing2.pom.xml引入基本依赖3.编写springboot启动类4.启动测试 2.统一响应处理1.首先定义一个响应枚举类 RespBeanEnum.java 每个枚举对象都有co…

全息之镜,未来的眼镜

全息之镜&#xff0c;作为未来眼镜的一种设想和展望&#xff0c;凭借其独特的全息技术&#xff0c;将在未来带来全新的视觉体验和应用场景。以下是关于全息之镜未来的详细分析和展望&#xff1a; 一、技术原理与特点 全息之镜利用全息技术&#xff0c;通过干涉、衍射和折射等…

Arthas调优工具使用

1&#xff0c;服务器端下载 curl -O https://arthas.aliyun.com/arthas-boot.jar 2&#xff0c;服务器端启动 java -jar arthas-boot.jar 选定要绑定的Java进程ID 3&#xff0c;本地idea安装Arthas idea 4&#xff0c;选定方法右键trace,生成命令 trace com.xxx.xxx.xxx.vouche…

6_5 test

Lucene 存储引擎 https://www.cnblogs.com/tech-lee/p/15225276.html\ 规范 问问题的技巧 提问者&#xff1a;要实现怎样的目标&#xff1f;自己计划是如何实现这个目标的&#xff1f;问题出现在哪个环节&#xff1f;自己为了解决这个问题&#xff0c;已经做了哪些尝试和工…

json和axion结合

目录 java中使用JSON对象 在pom.xml中导入依赖 使用 public static String toJSONString(Object object)把自定义对象变成JSON对象 json和axios综合案例 使用的过滤器 前端代码 响应和请求都是普通字符串 和 请求时普通字符串&#xff0c;响应是json字符串 响应的数据是…

【kubeflow文档】Kubeflow Training Operator

What is Training Operator Training Operator是一个Kubernetes原生项目&#xff0c;用于对使用各种ML框架&#xff08;如PyTorch、TensorFlow、XGBoost等&#xff09;创建的机器学习&#xff08;ML&#xff09;模型进行微调和可扩展的分布式训练。 用户可以将HuggingFace、Dee…

HarmonyOS App开发造轮子--自定义圆形图片

思路&#xff1a; 1、对比之前自己在其他程序开发中自定义组件的思路&#xff0c;首先寻找父组件Image和Component相关的Api&#xff0c;看看是否具备OnDraw方法。 2、了解Canvas相关Api操作&#xff0c;特别是涉及到位图的操作。 通过翻阅大量资料&#xff0c;发现了两个关…

【漏洞复现】Apache OFBiz 路径遍历导致RCE漏洞(CVE-2024-36104)

0x01 产品简介 Apache OFBiz是一个电子商务平台&#xff0c;用于构建大中型企业级、跨平台、跨数据库、跨应用服务器的多层、分布式电子商务类应用系统。是美国阿帕奇(Apache)基金会的一套企业资源计划(ERP)系统。该系统提供了一整套基于Java的Web应用程序组件和工具。 0x02 …

德克萨斯大学奥斯汀分校自然语言处理硕士课程汉化版(第五周) - Transformer

Transformer 1. 注意力机制 在语言建模中&#xff0c;注意力(attention)是一个关键机制&#xff0c;用于在给定上下文中访问相关信息以进行预测。注意力机制允许模型根据输入上下文中的重要信息来加权关注不同的部分&#xff0c;并根据其重要性来决定对不同部分的关注程度。 …

C#操作MySQL从入门到精通(10)——对查询数据进行通配符过滤

前言 我们有时候需要查询数据,并且这个数据包含某个字符串,这时候我们再使用where就无法实现了,所以mysql中提供了一种模糊查询机制,通过Like关键字来实现,下面进行详细介绍: 本次查询的表中数据如下: 1、使用(%)通配符 %通配符的作用是,表示任意字符出现任意次数…

C++ list链表的使用和简单模拟实现

目录 前言 1. list的简介 2.list讲解和模拟实现 2.1 默认构造函数和push_back函数 2.2 迭代器实现 2.2.1 非const正向迭代器 2.2.2 const正向迭代器 2.2.3 反向迭代器 2.3 插入删除函数 2.3.1 insert和erase 2.3.2 push_back pop_back push_front pop_front 2.4 构…

QT+FFmpeg+Windows开发环境搭建(加薪点)

01、Windows 环境搭建 FFMPEG官网:http://ffmpeg.org/ 02、下载4.2.1版本源码 源码:https://ffmpeg.org/releases/ffmpeg-4.2.1.tar.bz2 03、下载4.2.1编译好的文件 下载已经编译好的FFMPEG)(迅雷下载很快) 网址:https://ffmpeg.zeranoe.com/builds/ 32位下载地址:(迅雷…

这家公司的39亿存款,无法收回了?

新闻提要 4日晚间&#xff0c;亿利洁能发布公告称&#xff0c;亿利财务公司对于公司存放在亿利财务公司的 39.06 亿元货币资金的用途主要是向亿利集团及其关联方发放贷款&#xff0c;近日公司获悉相关贷款已被划分为次级贷款&#xff08;不良贷款的一种&#xff09;&#xff0…

重大变化,2024软考!

根据官方发布的2024年度计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试安排&#xff0c;2024年软考上、下半年开考科目有着巨大变化&#xff0c;我为大家整理了相关信息&#xff0c;大家可以看看&#xff01; &#x1f3af;2024年上半年&#xff1a;5月25日&am…

特征工程及python实现

一、特征构建 概述 从原始数据中构建新的特征&#xff0c;一般需要根据业务分析&#xff0c;生成能更好体现业务特性的新特征&#xff0c;这些新特征要与目标关系紧密&#xff0c;能提升模型表现或更好地解释模型。 方法 时间周期&#xff1a;不同的时间切片长度&#xff0…

Linux信号大揭秘-从中断到控制进程,一步步掌握进程通信利器!

在Linux环境下&#xff0c;信号(Signal)是一种软件中断&#xff0c;用于通知进程发生了某些重要事件。无论你是在编写命令行工具、服务程序&#xff0c;还是开发图形界面应用&#xff0c;都离不开对信号的处理。本文将全面解析信号的工作原理&#xff0c;并通过实例代码让你彻底…

实用软件分享---简单菜谱 0.3版本 几千种美食(安卓)

专栏介绍:本专栏主要分享一些实用的软件(Po Jie版); 声明1:软件不保证时效性;只能保证在写本文时,该软件是可用的;不保证后续时间该软件能一直正常运行;不保证没有bug;如果软件不可用了,我知道后会第一时间在题目上注明(已失效)。介意者请勿订阅。 声明2:本专栏的…