Linux-socket套接字

前言

        在当今数字化时代,网络通信作为连接世界的桥梁,成为计算机科学领域中至关重要的一部分。理解网络编程是每一位程序员必备的技能之一,而掌握套接字编程则是深入了解网络通信的关键。本博客将深入讨论套接字编程中的基本概念、常见API以及实际应用,通过一步步的学习,帮助读者逐渐掌握网络编程的精髓。


一、预备知识

1.1 IP

IP 是全球网络的基础,使用 IP 地址来标识公网环境下主机的唯一性,我们可以根据 目的IP地址 进行跨路由器的远端通信

仅仅使用 IP 只能定位到目标主机,并且目标主机不是最终目的地,要想定位目的地,需要依靠 端口号

目标主机中存在很多进程,网络通信实际是不同主机中的进程在进行通信,并非主机与主机直接通信

  • IP:源地址 与 目标地址
  • Port:目标地址中的哪个进程

1.2 端口号

        端口号 是一个用于标识网络进程唯一性的标识符,是一个 2 字节的整数,取值范围为 [0, 65535],可以通过 端口号 定位主机中的目标进程

        抛开网络其他知识,将信息从主机 A 中的进程 A 发送至主机 B 中的 进程 B,这不就是 进程间通信 吗?之前学习的 进程间通信 是通过 匿名管道、命名管道、共享内存 等方式实现,而如今的 进程间通信 则是通过 网络传输 的方式实现

需要进行网络通信的进程有很多,为了方便进行管理,就诞生了 端口号 这个概念,同进程的 PID 一样,端口号 也可以用于标识进程

1.3 端口号与进程PID

端口号 用于标识进程,进程 PID 也是用于标识进程,为什么在网络中,不直接使用进程 PID 呢?

  • 进程 PID 隶属于操作系统中的进程管理,如果在网络中使用 PID,会导致网络标准中被迫中引入进程管理相关概念(进程管理与网络强耦合
  • 进程管理 属于 OS 内部中的功能,OS 可以有很多标准,但网络标准只能有一套,在网络中直接使用 PID 无法确保网络标准的统一性
  • 并不是所有的进程都需要进行网络通信,如果端口号、PID 都使用同一个解决方案,无疑会影响网络管理的效率

        所以综上所述,网络中的 端口号 需要通过一种全新的方式实现,也就是一个 2 字节的整数 port,进程 A 运行后,可以给它绑定 端口号 N,在进行网络通信时,根据 端口号 N 来确定信息是交给进程 A 的

所以将之前的结论再具体一点:IP + Port 可以标识公网环境下,唯一的网络进程

网络传输中的必备信息组 [目的IP 源 IP || 目的 Port 源 Port]

  • 目的 IP需要把信息发送到哪一台主机
  • 源 IP信息从哪台主机中发出
  • 目的 Port将信息交给哪一个进程
  • 源 Port信息从哪一个进程中发出

注意: 端口号与进程 PID 并不是同一个概念

进程 PID 就好比你的身份证,端口号 相当于学号,这两个信息都可以标识唯一的你,但对于学校来说,使用学号更方便进行管理


一个进程可以绑定多个 端口号 吗?一个 端口号 可以被多个进程绑定吗?

        端口号 的作用是配合 IP 地址标识网络世界中进程的唯一性,如果一个进程绑定多个 端口号,依然可以保证唯一性(因为无论使用哪个 端口号,信息始终只会交给一个进程);但如果一个 端口号 被多个进程绑定了,在信息递达时,是无法分辨该信息的最终目的进程的,存在二义性

所以一个进程可以绑定多个端口号,一个 端口号 不允许被多个进程绑定,如果被绑定了,可以通过 端口号 顺藤摸瓜,找到占用该 端口号 的进程

如果某个端口号被使用了,其他进程再继续绑定是会报错的,提示 该端口已被占用


主机(操作系统)是如何根据 端口号 定位具体进程的?

这个实现起来比较简单,创建一张哈希表,维护 <端口号, 进程 PID> 之间的映射关系,当信息通过网络传输到目标主机时,操作系统可以根据其中的 [目的 Port],直接定位到具体的进程 PID,然后进行通信

1.4 传输层协议

主流的传输层协议有两个:TCP 和 UDP

两个协议各有优缺点,可以采用不同的协议,实现截然不同的网络程序,关于 TCP 和 UDP 的详细信息将会放到后面的博客中详谈,先来看看简单这两种协议的特点

TCP 协议:传输控制协议

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

字节流就像水龙头,用户可以根据自己的需求获取水流量

UDP 协议:用户数据协议

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

数据报则是相当于包裹,用户每次获取的都是一个或多个完整的包裹

关于 可靠性
TCP 的可靠传输并不意味着它可以将数据百分百递达,而是说它在数据传输过程中,如果发生了传输失败的情况,它会通过自己独特的机制,重新发送数据,确保对端百分百能收到数据;至于 UDP 就不一样,数据发出后,如果失败了,也不会进行重传,好在 UDP 面向数据报,并且没有很多复杂的机制,所以传输速度很快

总结起来就是:TCP 用于对数据传输要求较高的领域,比如金融交易、网页请求、文件传输等,至于 UDP 可以用于短视频、直播、即时通讯等对传输速度要求较高的领域

如果不知道该使用哪种协议,优先考虑 TCP,如果对传输速度又要求,可以选择 UDP

1.5 网络字节序

在学习网络字节序相关知识前,先回顾一下大小端字节序

  • 数据拥有高权值位和低权值位,比如在 32 位操作系统中,十六进制数 0x11223344,其中的 11 称为 最高权值位44 称为 最低权值位
  • 内存有高地址和低地址之分

如果将数据的高权值存放在内存的低地址处,低权值存放在高地址处,此时就称为 大端字节序,反之则称为 小端字节序,这两种字节序没有好坏之分,只是系统设计者的使用习惯问题,比如我当前的电脑在存储数据时,采用的就是 小端字节序 方案

通过内存单元可以看到,使用 小端字节序 时数据是倒着放的,大端字节序 就是正着存放了

在网络出现之前,使用大端或小端存储都没有问题,网络出现之后,就需要考虑使用同一种存储方案了,因为网络通信时,两台主机存储方案可能不同,会出现无法解读对方数据的问题

顶层设计者采用了解决方案2,TCP/IP 协议规定:网络中传输的数据,统一采用大端存储方案,也就是网络字节序, 现在大端/小端称为 主机字节序

发送数据时,将 主机字节序 转化为 网络字节序,接收到数据后,再转回 主机字节序 就好了,完美解决不同机器中的大小端差异,可以用下面这批库函数进行转换,在发送/接收时,调用库函数进行转换即可

#include <arpa/inet.h>

// 主机字节序转网络字节序
uint32_t htonl(uint32_t hostlong); // l 表示32位长整数
uint32_t htons(uint32_t hostshort); // s 表示16位短整数

// 网络字节序转主机字节序
uint32_t ntohl(uint32_t netlong); // l 表示32位长整数
uint32_t ntohs(uint32_t netshort); // s 表示16位短整数

二、 socket套接字

2.1 socket 常见API

socket 套接字提供了下面这一批常用接口,用于实现网络通信

#include <sys/types.h>
#include <sys/socket.h>

// 创建socket文件描述符(TCP/UDP	服务器/客户端)
int socket(int domain, int type, int protocol);

// 绑定端口号(TCP/UDP	服务器)
int bind(int socket, const struct sockaddr* address, socklen_t address_len);

// 开始监听socket (TCP	服务器)
int listen(int socket, int backlog);

// 接收连接请求 (TCP	服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);

// 建立连接 (TCP	客户端)
int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);

可以看到在这一批 API 中,频繁出现了一个结构体类型 sockaddr,该结构体支持网络通信,也支持本地通信

socket 套接字就是用于描述 sockaddr 结构体的字段,复用了文件描述符的解决方案

2.2 sockaddr 结构体

        socket 这套网络通信标准隶属于 POSIX 通信标准,该标准的设计初衷就是为了实现 可移植性,程序可以直接在使用该标准的不同机器中运行,但有的机器使用的是网络通信,有的则是使用本地通信,socket 套接字为了能同时兼顾这两种通信方式,提供了 sockaddr 结构体

        由 sockaddr 结构体衍生出了两个不同的结构体:sockaddr_in 网络套接字、sockaddr_un 域间套接字,前者用于网络通信,后者用于本地通信

  • 可以根据 16 位地址类型,判断是网络通信,还是本地通信
  • 在进行网络通信时,需要提供 IP 地址、端口号 等网络通信必备项,本地通信只需要提供一个路径名,通过文件读写的方式进行通信(类似于命名管道)

socket 提供的接口参数为 sockaddr*,我们既可以传入 &sockaddr_in 进行网络通信,也可以传入 &sockaddr_un 进行本地通信,传参时将参数进行强制类型转换即可,这是使用 C语言 实现 多态 的典型做法,确保该标准的通用性

三、UDP套接字程序

该图引用于此,套接字运用流程图

3.1 简易本地通讯及bash模拟

分别实现客户端与服务器,客户端向服务器发送消息,服务器收到消息后,回响给客户端,有点类似于 echo 指令

该程序的核心在于 使用 socket 套接字接口,以 UDP 协议的方式实现简单网络通信

程序由 server.hppserver.ccclient.hppclient.cc 组成,大体框架如下

server.hpp头文件

server.cc文件

client.hpp头文件

client.cc文件

Makefile文件

四、TCP套接字程序

4.1 多进程版本地通信

#include<iostream>
#include<string>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<cstring>
#include<unistd.h>
#include<signal.h>
using namespace std;
 
void Usage(string proc)
{
    cout << "Usage: " << proc << "port" << endl;
}
void ServiceIO(int new_sock)
{
    // 提供服务
        while(true)
        {
            char buffer[1024];
            memset(buffer, 0, sizeof(buffer));
            ssize_t cnt = read(new_sock, buffer, sizeof(buffer) - 1);
            if(cnt > 0)
            {
                buffer[cnt] = 0;
                cout << "client# " << buffer << endl;
                string echo_string = "server--> ";
                echo_string += buffer;
                write(new_sock, echo_string.c_str(), echo_string.size());
            }
            else if(cnt == 0)
            {
                cout << "client quit..." << endl;
                break;
            }
            else
            {
                cerr << "read error" << endl;
                break;
            }
        }
}
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    // 1.创建监听套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0); // ipv4 , 流式套接
    if(listen_sock < 0)
    {
        cerr << "socket error: " << errno << endl;
        return 2;
    }
    // bind
    // 填充套接字信息
    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(argv[1]));
    local.sin_addr.s_addr = INADDR_ANY;
    if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        cerr << "bind error" << errno << endl;
        return 3;
    }
    // 监听
    const int back_log = 5;
    if (listen(listen_sock, back_log) < 0)
    {
        cerr << "listen error" << errno << endl;
        return 4;
    }
 
    signal(SIGCHLD, SIG_IGN); // 在linux中父进程忽略子进程的SIGCHLD信号,子进程会自动退出释放资源
 
    while(true)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len); 
        // 上面创建的监听套接字只起到监听作用
        // accept接口返回的套接字,才能为客户端提供服务
        if(new_sock < 0)
        {
            continue;
        }
        // peer: 输入输出型参数,存放客户端sock
        // 拿到客户端ip地址及端口:
        uint16_t client_port = ntohs(peer.sin_port); // 网络序列转主机序列
        string client_ip = inet_ntoa(peer.sin_addr); // inet_ntoa:将四字节ip转化成点分十进制ip
        cout << "get a new link -> : [" << client_ip << ":" << client_port << "]#" << new_sock << endl;
        pid_t id = fork();
        if(id<0)
        {
            continue;
        }
        else if(id==0) // 子进程会继承父进程的文件描述符, 为防止出现文件描述符泄露,需要关闭不用的文件描述符
        {
            // child
            close(listen_sock);
            ServiceIO(new_sock);
            close(new_sock); // 关闭文件描述符,否则会导致文件描述符泄露
            exit(0);
        }
        else
        {
            // father
            // do nothing
            close(new_sock); // 父进程关闭提供服务的文件描述符,继续接收新链接
        }
        }
    return 0;
}

4.2 多线程版

#include<iostream>
#include<string>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<cstring>
#include<unistd.h>
#include<signal.h>
#include<pthread.h>
using namespace std;
 
void Usage(string proc)
{
    cout << "Usage: " << proc << "port" << endl;
}
void ServiceIO(int new_sock)
{
    // 提供服务
        while(true)
        {
            char buffer[1024];
            memset(buffer, 0, sizeof(buffer));
            ssize_t cnt = read(new_sock, buffer, sizeof(buffer) - 1);
            if(cnt > 0)
            {
                buffer[cnt] = 0;
                cout << "client# " << buffer << endl;
                string echo_string = "server--> ";
                echo_string += buffer;
                write(new_sock, echo_string.c_str(), echo_string.size());
            }
            else if(cnt == 0)
            {
                cout << "client quit..." << endl;
                break;
            }
            else
            {
                cerr << "read error" << endl;
                break;
            }
        }
}
 
void* HandlerRequest(void*args)
{
    pthread_detach(pthread_self()); // 分离新线程
    int sock = *(int *)args;
    delete (args);
    ServiceIO(sock);
    close(sock); 
}
 
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    // 1.创建监听套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0); // ipv4 , 流式套接
    if(listen_sock < 0)
    {
        cerr << "socket error: " << errno << endl;
        return 2;
    }
    // bind
    // 填充套接字信息
    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(argv[1]));
    local.sin_addr.s_addr = INADDR_ANY;
    if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        cerr << "bind error" << errno << endl;
        return 3;
    }
    // 监听
    const int back_log = 5;
    if (listen(listen_sock, back_log) < 0)
    {
        cerr << "listen error" << errno << endl;
        return 4;
    }
 
    signal(SIGCHLD, SIG_IGN); // 在linux中父进程忽略子进程的SIGCHLD信号,子进程会自动退出释放资源
 
    while(true)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len); 
        // 上面创建的监听套接字只起到监听作用
        // accept接口返回的套接字,才能为客户端提供服务
        if(new_sock < 0)
        {
            continue;
        }
        // peer: 输入输出型参数,存放客户端sock
        // 拿到客户端ip地址及端口:
        uint16_t client_port = ntohs(peer.sin_port); // 网络序列转主机序列
        string client_ip = inet_ntoa(peer.sin_addr); // inet_ntoa:将四字节ip转化成点分十进制ip
        cout << "get a new link -> : [" << client_ip << ":" << client_port << "]#" << new_sock << endl;
        
        // 创建新线程
        pthread_t tid;
        int *pram = new int(new_sock);
        pthread_create(&tid, nullptr, HandlerRequest, pram);
        // 这里主线程不需要关闭new_sock文件描述符,因为主线程与新线程共享一个文件描述符数组,主线程关闭new_sock会导致新线程也关闭
        }
    return 0;
}

4.3 线程池版

#include<iostream>
#include<string>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<cstring>
#include<unistd.h>
#include<signal.h>
#include<pthread.h>
#include"thread_pool.hpp"
#include"task.hpp"
using namespace std;
 
void Usage(string proc)
{
    cout << "Usage: " << proc << "port" << endl;
}
 
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    // 1.创建监听套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0); // ipv4 , 流式套接
    if(listen_sock < 0)
    {
        cerr << "socket error: " << errno << endl;
        return 2;
    }
    // bind
    // 填充套接字信息
    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(argv[1]));
    local.sin_addr.s_addr = INADDR_ANY;
    if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        cerr << "bind error" << errno << endl;
        return 3;
    }
    // 监听
    const int back_log = 5;
    if (listen(listen_sock, back_log) < 0)
    {
        cerr << "listen error" << errno << endl;
        return 4;
    }
 
    signal(SIGCHLD, SIG_IGN); // 在linux中父进程忽略子进程的SIGCHLD信号,子进程会自动退出释放资源
 
    while(true)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len); 
        // 上面创建的监听套接字只起到监听作用
        // accept接口返回的套接字,才能为客户端提供服务
        if(new_sock < 0)
        {
            continue;
        }
        // peer: 输入输出型参数,存放客户端sock
        // 拿到客户端ip地址及端口:
        uint16_t client_port = ntohs(peer.sin_port); // 网络序列转主机序列
        string client_ip = inet_ntoa(peer.sin_addr); // inet_ntoa:将四字节ip转化成点分十进制ip
        cout << "get a new link -> : [" << client_ip << ":" << client_port << "]#" << new_sock << endl;
        // 1.创建任务
        Task t(new_sock);
        // 2.将任务放进线程池
        ThreadPool<Task>::GetInstance()->PushTask(t);
        }
    return 0;
}

五、总结

  • 创建socket的过程(socket()),本质是打开文件。(仅有系统相关的内容)
  • bind(),struct sockaddr_in -> ip,port,本质是ip+port和文件信息进行关联
  • listen(),本质是设置该socket文件的状态,允许别人来连接我
  • accpet(),获取新链接到应用层,是以fd为代表的;所谓的连接,在OS层面,本质其实就是一个描述连接的结构体(文件)
  • read/write,本质就是进行网络通信,对于用户来讲就相当于在进行正常的文件读写
  • close(fd),关闭文件;系统层面,释放曾经申请的文件资源,连接资源等;网络层面,通知对方,我的连接已经关闭了
  • connect(),本质是发起连接,在系统层面,就是构建一个请求报文发送过去;网络层面,发起tcp连接的三次握手
  • close(),client、server,本质在网络层面就是在进行四次挥手

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(五)关系数据库标准语言SQL

注&#xff1a;课堂讲义使用的数据库 5.1利用SQL语言建立数据库 5.1.1 create Database 5.1.2 create schema...authorization... 创建数据库和创建模式的区别&#xff1a; 数据库是架构的集合&#xff0c;架构是表的集合。但在MySQL中&#xff0c;他们使用的方式是相同的。 …

电脑工作电压是多少你要看看光驱电源上面标的输入电压范围

要确定电脑的工作电压&#xff0c;必须查看电源上标注的输入电压范围。 国内法规规定民用220V电压范围为10%-15%&#xff0c;也就是说通信220V电压正常范围为187--242V&#xff0c;供电设备一般为180V。 --250V电压范围&#xff0c;即正常情况下电脑电源电压不低于187V即可工作…

4.1k star,官方出品的redis桌面管理工具——redislnsight

导航 令人抓狂的大key加载RedisInsight 简介RedisInsight的亮点GitHub 地址安装和使用RedisInsight 下载安装 使用RedisInsight redis数据库可视化直观的CLI&#xff08;Command-Line Interface&#xff09;日志分析和命令分析 结语参考 令人抓狂的大key加载 工欲善其事必先利…

查询IP地址保障电商平台安全

随着电子商务的快速发展&#xff0c;网购已经成为人们日常生活中不可或缺的一部分。然而&#xff0c;网络交易安全一直是人们关注的焦点之一&#xff0c;尤其是在面对日益频发的网络诈骗和欺诈行为时。为了提高网购平台交易的安全性&#xff0c;一种有效的方法是通过查询IP地址…

每周一算法:A*(A Star)算法

八数码难题 题目描述 在 3 3 3\times 3 33 的棋盘上&#xff0c;摆有八个棋子&#xff0c;每个棋子上标有 1 1 1 至 8 8 8 的某一数字。棋盘中留有一个空格&#xff0c;空格用 0 0 0 来表示。空格周围的棋子可以移到空格中。要求解的问题是&#xff1a;给出一种初始布局…

算法优化 —— 注意力机制篇

文章目录 前言一、EMA 高效多尺度注意力机制(ICASSP2023)二、BiFormer: 基于动态稀疏注意力构建高效金字塔网络架构(2023CVPR)前言 提示:这里可以添加本文要记录的大概内容: 一、EMA 高效多尺度注意力机制(ICASSP2023) 原论文链接 代码如下: class EMA(nn.Module

数据结构-线段树

&#x1f4d1;前言 本文主要是【线段树】——线段树简单使用的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&…

R实现热图与网络图组合并显示显著性

大家好&#xff0c;我是带我去滑雪&#xff01; 热图和网络图分别展示了数据的不同方面。热图可用于显示变量之间的相关性或模式&#xff0c;而网络图则可用于显示节点之间的连接关系。通过将它们组合在一起&#xff0c;可以更全面地展示数据之间的关系和结构。下面开始代码实战…

STM32CubeIDE基础学习-STM32CubeIDE软件快捷键介绍

STM32CubeIDE基础学习-STM32CubeIDE软件快捷键介绍 文章目录 STM32CubeIDE基础学习-STM32CubeIDE软件快捷键介绍前言第1章 查看快捷键方法第2章 设置快捷键方法第3章 常用快捷键示例总结 前言 这个STM32CubeIDE软件使用的是Eclipse框架的开发环境&#xff0c;所以所使用的快捷…

单链表OJ题:LeetCode--141.环形链表

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下LeetCode中的第141道单链表OJ题&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; 数据结构与算法专栏&#xff1a;数据结构与算法 个 …

DHCP部署与安全

在当今快速发展的网络世界中&#xff0c;动态主机配置协议&#xff08;DHCP&#xff09;扮演着至关重要的角色。这项技术不仅简化了网络管理&#xff0c;还提高了网络资源的利用率。本文旨在深入探讨DHCP的工作原理、优势以及如何有效部署和保护DHCP服务器。 一、DHCP作用 自…

抖音商家短视频直播流量变现运营SOP地图

【干货资料持续更新&#xff0c;以防走丢】 抖音商家短视频直播流量变现运营SOP地图 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 抖音运营资料合集&#xff08;完整资料包含以下内容&#xff09; 目录 【提升短视频运营效率的专业指南】 高效运营&#xf…

Python笔记(三)—— Python循环语句

循环普遍存在于日常生活中&#xff0c;同样&#xff0c;在程序中&#xff0c;循环功能也是至关重要的基础功能。 循环在程序中同判断一样&#xff0c;也是广泛存在的&#xff0c;是非常多功能实现的基础&#xff1a; bilibili循环轮播图 循环和判断一样&#xff0c;同样是程序…

npm市场发布包步骤

1.打开npm官网npm官网 2.创建自己的账号 3.查看当前npm的镜像源&#xff0c; 如果出现淘宝的镜像源则需要切换成官方的镜像源 npm config get registry //查看镜像源 https://registry.npm.taobao.org/ //淘宝的镜像源 https://registry.npmjs.org/ //官方的镜像源 …

解决:ModuleNotFoundError: No module named ‘paddle‘

错误显示&#xff1a; 原因&#xff1a; 环境中没有‘paddle’的python模块&#xff0c;但是您在尝试导入 解决方法&#xff1a; 1.普通方式安装&#xff1a; pip install paddlepaddle #安装命令 2.镜像源安装 pip install paddlepaddle -i https://pypi.tuna.tsinghua.e…

Linux性能分析之CPU实战

本课程专注于教授学员如何利用各种工具和技术来分析Linux系统中的CPU性能问题。通过实际操作和案例研究&#xff0c;学员将深入了解CPU性能优化和故障排除&#xff0c;提升其在Linux环境下的技能水平。 课程大小&#xff1a;1.9G 课程下载&#xff1a;https://download.csdn.…

tiktok矩阵引流系统开发常用源代码!

在数字营销领域&#xff0c;TikTok已成为一个不可忽视的平台&#xff0c;随着其用户基数的不断增长&#xff0c;如何利用TikTok进行有效的引流成为了许多企业和营销人员关注的焦点。 为了实现这一目标&#xff0c;许多开发者开始构建TikTok矩阵引流系统&#xff0c;这些系统通…

MacBook2024苹果免费mac电脑清理垃圾软件CleanMyMac X

CleanMyMac X是一款专业的Mac清理软件&#xff0c;具备多种强大功能。首先&#xff0c;它能够智能清理Mac磁盘上的垃圾文件和多余语言安装包&#xff0c;从而快速释放电脑内存。其次&#xff0c;CleanMyMac X可以轻松管理和升级Mac上的应用&#xff0c;同时强力卸载恶意软件并修…

LeetCode19题:删除链表的倒数第N个结点(python3)

代码思路&#xff1a; 我们可以设想假设设定了双指针 p 和 q 的话&#xff0c;当 q 指向末尾的 NULL&#xff0c;p 与 q 之间相隔的元素个数为 n 时&#xff0c;那么删除掉 p 的下一个指针就完成了要求。 1.设置虚拟节点 dummyHead 指向 head 2.设定双指针 p 和 q&#xff0c…

定时执行专家V7.1 多国语言版本英文版发布 - TimingExecutor V7.1 English Version Release

目录 ◆ About TimingExecutor ◆ Main Frame ◆ Job Dailog ◆ Trigger Dialog ◆ Setting Dialog ◆ About Dialog ◆ Job Detail Information panel ◆ Statistics Information panel ◆ About TimingExecutor 《定时执行专家》是一款制作精良、功能强大、毫秒精度…