【Linux】网络编程 - Socket套接字/基于UDP的网络通信

news2024/11/14 18:48:38

目录

一.套接字

1.什么是套接字/Socket套接字

2.套接字的分类

3.Socket套接字的常见API

二.网络字节序

1.什么是网络字节序

2.网络字节序和主机字节序的转换接口

三.IP地址形式上的转换

四.客户端的套接字不由程序员bind

1.为什么客户端套接字不能由程序员bind

2.OS是在什么时候给客户端bind了ip和port

五.基于UDP的网络通信

1.传输层协议UDP的基本特性

2.基于UDP协议的C/S网络通信

1).demo代码1

2.demo代码2 - mini聊天室

六.补充扩展

1.本地环回ip地址

2.云服务器不能绑定指定IP地址

3.服务器不推荐绑定确定IP


一.套接字

1.什么是套接字/Socket套接字

套接字是通信的基石,是支持TCP/IP协议的路通信的基本操作单元。可以将套接字看作不同主机间的进程进行双间通信的端点,它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序),各种进程使用这个相同的域互相之间用Internet协议簇来进行通信

Socket(套接字)可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个逻辑上的概念。它是网络环境中进程间通信的API(应用程序编程接口),也是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连进程。通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的 Socket中,该 Socket通过与网络接口卡(NIC)相连的传输介质将这段信息送到另外一台主机的 Socket中,使对方能够接收到这段信息。 Socket是由IP地址和端口结合的,提供向应用层进程传送数据包的机制

                                                                                                                                                                                                --- 摘自百度

Socket套接字属于应用层和传输层协议之间的一个抽象层, Socket套接字系列的API都是传输层来提供给我们使用的接口, 用来让我们在应用层中编写网络编程代码

在linux中, 一切皆文件, 网卡也是文件; socket套接字, 本质上, 就是一个文件描述符fd, 因为网络通信的本质也是使用文件来进行通信, 创建好套接字, 之后的网络通信, 想要发送/接收信息数据本质上都是在利用这个套接字文件来进行

2.套接字的分类

域间套接字 - 用于本主机内的进程间通信 - 基于套接字式的管道通信 - 原理类似于命名管道通信

原始套接字 - 用来编写一些工具 - 可以绕过运输层或网络层或其他层直接使用底层

网络套接字 - 用于网络通信 - 例如, socket套接字就是网络套接字

以上, 理论上应该是三套接口, 而linux"封装"了套接字类型, 将所有的接口进行了统一

其统一就体现在, sockaddr结构体类型

不管是sockaddr_in还是sockaddr_un都可以以强制类型转换的方式传给sockaddr, sockaddr依靠前两字节判断该sockaddr具体是sockaddr_in还是sockaddr_un还是其他套接字..., 补充: 为何不用void*? 因为网络接口的设计比C语言中void*更早, 所以那时还没有void*

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

以上三个头文件, 有一些网络编程中常用函数和sockaddr_in类型所需

sockaddr_in网络通信结构体类型: 

 

总之, struct sockaddr_in内需要有:

哪种套接字(sin_family 类型: AF_INET or AF_UNIX or AF_LOCAL ...宏 --> int)

IP地址(sin_addr 类型: struct in_addr --> 内部只有一个成员s_addr 类型: in_addr_t --> uint32_t)

Port端口(sin_port 类型: in_port_t --> uint16_t)

剩下的均为填充字段(sin_zero不用管)

3.Socket套接字的常见API

#include <sys/types.h>

#include <sys/socket.h>

创建socket套接字, 即文件描述符(TCP/UDP)

本质: 创建一个指明运输层协议的文件供我们用来网络通信

int socket(int domain, int type, int protocol);

参数

1.domain: 表示套接字的域, 即套接字的类型, AF_UNIX域AF_LOCAL用于本地通信, AF_INET和AF_INET6用于网络通信(IPv4和IPv6), ...

2.type: 类型, 你想创建这个套接字的通信种类是什么, 面向流SOCK_STREAM?or面向数据报SOCK_DGRAM?or other?

3.protocol: 协议, 你想使用哪一种协议, 通常第一二个参数填好, 就代表第三个参数填好了, 如用AF_INET且SOCK_DGRAM也就代表使用传输层的UDP协议, 或AF_INET且SOCK_STREAM也就代表使用传输层的TCP协议, 通常如果采用默认传0即可

返回值: 文件描述符(在网络中就是套接字)

绑定端口号(TCP/UDP)

本质: 将用户设置的IP和port在内核中和我们当前进程及创建的socket套接字强关联

int bind(int socket, const struct sockaddr *addr, socklen_t address_len);

参数

1.socket: 我们创建的套接字

2.addr: 填充ip和port的结构体, 需要将struct sockaddr_in*强转成struct sockaddr*

3.address_len: addr结构体的长度(本质: uint32_t)

返回值: 成功返回0, 否则返回-1, 设置错误码errno

开始监听socket(TCP)

int listen(int socket, int backlog);

接收请求(TCP)

int accept(int socket, struct sockaddr *addr, socklen_t *address_len);

建立连接(TCP)

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

ssize_t recv(int sockfd, void* buf, size_t len, int flags);

ssize_t recvfrom(int sockfd, void* buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)

本质: 从源ip主机, 即网络中, 接收数据

参数

sockfd: 创建并且绑定的套接字

buf: 读到buf中

len: buf大小

flags: 读取方式, 0代表阻塞式读取

src_addr: 源IP和port, 输出型参数

addrlen: 源IP和port的长度, 输入输出型参数, 输入: struct sockaddr_in的大小 输出: 实际读到的src_addr大小

注:!! src_addr与addrlen本质是要获取, 发送数据一端的ip&&port信息

返回值: 实际读取到的字节个数

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

本质: 向目的ip主机, 即网络中, 发送数据

参数

sockfd: 创建并且绑定的套接字

buf: 发送数据的内容

len: 发送数据的长度

flags: 读取方式, 0代表阻塞式读取

dest_addr: 目的端的信息, ip port family...

addrlen: dest_addr的长度

二.网络字节序

1.什么是网络字节序

在计算机中, 内存存储字节有两种方式: 大端或小端

一台主机发送给另一台主机数据, 这两台机器在网络通信时并不知道对方是大端机还是小端机, 为了能够统一字节序, 便有了网络字节序这一概念

在TCP/IP协议族中, 网络字节序采用大端, 也就是不管发送什么数据, 都会按照大端(网络字节序)来发送/接收数据, 如果当前发送机是小端, 就要先将数据转成大端; 否则就忽略, 直接发送即可; 从网络中接收数据时也是同理

2.网络字节序和主机字节序的转换接口

包含头文件 <arpa/inet.h>

主机转网络

uint32_t htonl(uint32_t hostlong);

uint16_t htons(uint16_t hostshort);

网络转主机

uint32_t ntohl(uint16_t netlong);

uint16_t ntohs(uint16_t netshort);

注: h - host代表主机; n - network代表网络; l - long代表长整型; s - short代表短整型

通常如果是port, 用short - uint16_t; 如果是IP, 用long - uint32_t

三.IP地址形式上的转换

为了让人更方便的看到ip地址, 通常输出/输入"点分十进制" 的ip地址, 每个.之间的数取值范围[0, 255]因为一个数只占1字节, 实际上计算机存储ip地址只需要4Byte就够了, 要是通过网络的传输的话还要把4字节ip转换为网络字节序, 这样一共有两步转换

1."点分十进制"字符串式ip --> 主机序列的4字节ip

2.主机序列的4字节ip --> 网络序列的4字节ip

以上两步可以通过同一接口进行转换

"点分十进制"字符串式ip --> 网络序列的4字节ip: in_addr_t inet_addr(const char* cp);

网络序列的4字节ip --> "点分十进制"字符串式ip: char *inet_ntoa(struct in_addr in);

四.客户端的套接字不由程序员bind

1.为什么客户端套接字不能由程序员bind

客户端client一定需要绑定ip和端口, 但是由用户的OS自动绑定的, 不能由程序员手动绑定, 因为程序员写的客户端client在未来是要交给用户, 并且让用户下载的, 程序员并不知道在用户的机器上的端口的使用情况, 由于1个端口只能绑定一个客户端, 所以这个绑定的过程要交给用户的机器OS自动进行

2.OS是在什么时候给客户端bind了ip和port

当客户端client首次发送信息给服务器的时候, 用户的OS会自动给用户客户端client绑定它的ip和port

五.基于UDP的网络通信

1.传输层协议UDP的基本特性

有连接

可靠传输

面向字节流

2.基于UDP协议的C/S网络通信

1).demo代码1

udp_server.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <strings.h>
#include <unistd.h>
#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

class udpServer
{
public:
    udpServer(const uint16_t port, const std::string ip = "") : _port(port), _ip(ip)
    {
    }
    void Init()
    {
        // 1.创建Socket套接字
        _socket = socket(AF_INET, SOCK_DGRAM, 0);
        assert(_socket != -1);
        std::cout << "创建Socket套接字成功" << std::endl;

        // 2.绑定Socket套接字 -- ip和port
        struct sockaddr_in sv;
        bzero(&sv, sizeof(sv));
        sv.sin_family = AF_INET;
        sv.sin_port = htons(_port);
        sv.sin_addr.s_addr = _ip == "" ? INADDR_ANY : inet_addr(_ip.c_str());
        socklen_t len = sizeof(sv);

        int ret = bind(_socket, (struct sockaddr *)&sv, len);
        assert(ret != -1);
        std::cout << "绑定Socket套接字成功" << std::endl;
    }
    void Start()
    {
        while (true)
        {
            // 3.开始通信
            char buffer[1024];
            bzero(buffer, sizeof(buffer));
            // 3.1接收数据
            struct sockaddr_in cli;
            bzero(&cli, sizeof(cli));
            socklen_t len = sizeof(cli);

            ssize_t n = recvfrom(_socket, buffer, sizeof(buffer), 0, (struct sockaddr *)&cli, &len);
            if (n > 0)
            {
                buffer[n] = '\0';
                // 3.2输出读取数据
                std::string cli_ip = inet_ntoa(cli.sin_addr);
                uint16_t cli_port = ntohs(cli.sin_port);
                printf("[%s:%d]# %s\n", cli_ip.c_str(), cli_port, buffer);

                // 4.原路发回
                sendto(_socket, buffer, strlen(buffer), 0, (struct sockaddr*)&cli, len);
            }
            else
            {
                std::cout << "本次未读到数据" << std::endl;
            }
        }
    }
    ~udpServer()
    {
        close(_socket);
    }

private:
    uint16_t _port;   // 端口
    std::string _ip;  // ip
    int _socket = -1; // Socket套接字
};

udp_server.cc

#include "udp_server.hpp"
#include <memory>


static inline void Usage(char *&proc)
{
    std::cout << "correct way to use: " << proc << " port" << std::endl;
}

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(-1);
    }

    uint16_t server_port = atoi(argv[1]); 

    std::unique_ptr<udpServer> server(new udpServer(server_port));

    // 初始化服务器
    server->Init();

    // 启动服务器
    server->Start();

    return 0;
}

client.cc

#include "udp_server.hpp"
#include <memory>


static inline void Usage(char *&proc)
{
    std::cout << "correct way to use: " << proc << " port" << std::endl;
}

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(-1);
    }

    uint16_t server_port = atoi(argv[1]); 

    std::unique_ptr<udpServer> server(new udpServer(server_port));

    // 初始化服务器
    server->Init();

    // 启动服务器
    server->Start();

    return 0;
}

2.demo代码2 - mini聊天室

udp_server.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <strings.h>
#include <unistd.h>
#include <cstring>
#include <unordered_map>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

class udpServer
{
public:
    udpServer(const uint16_t port, const std::string ip = "") : _port(port), _ip(ip)
    {
    }
    void Init()
    {
        // 1.创建Socket套接字
        _socket = socket(AF_INET, SOCK_DGRAM, 0);
        assert(_socket != -1);
        std::cout << "创建Socket套接字成功" << std::endl;

        // 2.绑定Socket套接字 -- ip和port
        struct sockaddr_in sv;
        bzero(&sv, sizeof(sv));
        sv.sin_family = AF_INET;
        sv.sin_port = htons(_port);
        sv.sin_addr.s_addr = _ip == "" ? INADDR_ANY : inet_addr(_ip.c_str());
        socklen_t len = sizeof(sv);

        int ret = bind(_socket, (struct sockaddr *)&sv, len);
        assert(ret != -1);
        std::cout << "绑定Socket套接字成功" << std::endl;
    }
    void Start()
    {
        while (true)
        {
            // 3.开始通信
            char buffer[1024];
            bzero(buffer, sizeof(buffer));
            // 3.1接收数据
            struct sockaddr_in cli;
            bzero(&cli, sizeof(cli));
            socklen_t len = sizeof(cli);

            ssize_t n = recvfrom(_socket, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&cli, &len);

            char key[64];
            bzero(key, sizeof(key));
            if (n > 0)
            {
                buffer[n] = '\0';

                // 3.2输出读取数据

                std::string cli_ip = inet_ntoa(cli.sin_addr);
                uint16_t cli_port = ntohs(cli.sin_port);

                printf("[%s:%d]# %s\n", cli_ip.c_str(), cli_port, buffer); // 给服务器打印

                // 注册发送数据的用户信息
                snprintf(key, sizeof(key), "%s-%u", cli_ip.c_str(), cli_port);

                _usrmessage[key] = cli;
            }

            // 向所有注册过的用户广播信息
            std::string message;
            message += key;
            message += "#";
            message += buffer;
            message += "\n";
            
            for (auto &e : _usrmessage)
            {
                std::cout << "广播............" << std::endl;
                // 4.原路发回
                sendto(_socket, message.c_str(), message.size(), 0, (struct sockaddr *)&(e.second), sizeof(e.second));
            }
        }
    }
    ~udpServer()
    {
        close(_socket);
    }

private:
    uint16_t _port;   // 端口
    std::string _ip;  // ip
    int _socket = -1; // Socket套接字

    std::unordered_map<std::string, struct sockaddr_in> _usrmessage;
};

udp_server.cc

#include "udp_server.hpp"
#include <memory>


static inline void Usage(char *&proc)
{
    std::cout << "correct way to use: " << proc << " port" << std::endl;
}

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(-1);
    }

    uint16_t server_port = atoi(argv[1]); 

    std::unique_ptr<udpServer> server(new udpServer(server_port));

    // 初始化服务器
    server->Init();

    // 启动服务器
    server->Start();

    return 0;
}

udp_client.hpp

#pragma once

#include <iostream>
#include <cstdlib>
#include <strings.h>
#include <memory>
#include <unistd.h>
#include <thread>
#include <cstdio>
#include <sys/stat.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

class udpClient
{
private:
    static void Send(struct sockaddr_in sendUsr, int sock)
    {
        while (true)
        {
            // 2.发送数据
            std::string sendmessage;
            std::cout << "请输入: ";
            std::getline(std::cin, sendmessage);

            sendto(sock, sendmessage.c_str(), sendmessage.size(), 0, (struct sockaddr *)&sendUsr, sizeof(sendUsr));

            if (sendmessage == "quit")
            {
                std::cout << "the clientSend is closed" << std::endl;
                break;
            }
        }
    }

    static void Recv(int sock)
    {
        std::string namedPath = "./JOKER.txt";
        umask(0);
        mkfifo(namedPath.c_str(), 0666);

        int fd = open(namedPath.c_str(), O_WRONLY | O_APPEND);

        while (true)
        {
            // 3.接收server echo
            char buffer[1024];
            bzero(buffer, sizeof(buffer));
            ssize_t n = recv(sock, buffer, sizeof(buffer), 0);
            if (n > 0)
            {
                buffer[n] = '\0';

                //std::cout << "Server Echo# " << buffer << std::endl;
                write(fd, buffer, sizeof(buffer));

                if (buffer == "quit")
                {
                    std::cout << "the clientRecv is closed" << std::endl;
                    break;
                }
            }
        }
        close(fd);
    }

public:
    udpClient(std::string sip, uint16_t sport) : _sv_ip(sip), _sv_port(sport), _socket(-1)
    {
    }

    void Init()
    {
        // 1.创建套接字
        _socket = socket(AF_INET, SOCK_DGRAM, 0);

        // 客户端由客户机OS自动绑定
    }

    void Start()
    {
        struct sockaddr_in sendUsr;
        bzero(&sendUsr, sizeof(sendUsr));

        sendUsr.sin_family = AF_INET;
        sendUsr.sin_addr.s_addr = inet_addr(_sv_ip.c_str());
        sendUsr.sin_port = htons(_sv_port);

        std::thread t_send(Send, sendUsr, _socket);
        std::thread t_recv(Recv, _socket);

        t_send.join();
        t_recv.join();
    }

    ~udpClient()
    {
        close(_socket);
    }

private:
    std::string _sv_ip;
    uint16_t _sv_port;

    int _socket;
};

udp_client.cc 

#include "udp_client.hpp"

static inline void Usage(char *&proc)
{
    std::cout << "correct way to use: " << proc << " ip port" << std::endl;
}

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(-1);
    }

    std::string sv_ip = argv[1];
    uint16_t sv_port = atoi(argv[2]);

    std::unique_ptr<udpClient> client(new udpClient(sv_ip, sv_port));

    client->Init();

    client->Start();

    return 0;
}

六.补充扩展

1.本地环回ip地址

ip地址: 127.0.0.1 --> 本地环回

client和server发送数据指在本地协议栈中进行数据流动, 不会把我们的数据发送到网络中

只是把TCP/IP五层协议栈走一遍, 通常用来做本地网络服务器的测试

2.云服务器不能绑定指定IP地址

云服务器无法绑定公网IP地址

在云服务器中, 比如腾讯云, 他给我们提供的IP地址全部都是虚拟出来的, 不允许绑定这个确定的ip地址, 实际使用的不是这个IP地址, 所以我们无法在服务器进程上直接绑定这个地址

但其他人是可以通过这个虚拟出来的ip向云服务器互相网络通信的, 因为可以通过服务器进程绑定任意ip地址的方式来达到

3.服务器不推荐绑定确定IP

推荐服务器绑定"任意IP" -- 使用INADDR_ANY宏

INADDR_ANY宏, 本质是(in_addr_t)0x0000 0000 -- 让服务器在工作过程中可以从任意IP中获取数据 -- 即可以理解为有多少张网卡有多少IP都可以绑定上

因为服务器也许不止一张网卡, 也许有很多张网卡, 如果绑定了具体IP地址, 则服务器只能收到发送到这个IP地址的消息, 而如果使用任意IP的绑定方法, 则服务器就能收到发送到很多个IP地址的消息, 比如该服务器有3张网卡, 则客户端有可能分别向这三张网卡的IP发送数据, 服务器就都能收到了

当在机器上查看一个服务器进程或客户端进程的IP地址为0.0.0.0就说明为任意IP地址绑定

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

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

相关文章

【题外话】如何拯救小米11Pro这款工业垃圾

1 背景媳妇用小米11Pro手机&#xff0c;某日不慎摔落&#xff0c;幸好屏幕未碎&#xff0c;然而WiFi却怎样都无法打开&#xff0c;初以为是系统死机&#xff0c;几天依旧故障无法使用。现在的手机没有WiFi功能&#xff0c;就无法刷抖音、看视频&#xff0c;就是鸡肋了。后抽空去…

全栈之路-前端篇 | 第三讲.基础前置知识【前端标准与研发工具】学习笔记

欢迎关注「全栈工程师修炼指南」公众号点击 &#x1f447; 下方卡片 即可关注我哟!设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习&#xff01;涉及 企业运维、网络安全、应用开发、物联网、人工智能、大数据 学习知识“ 花开堪折直须折&#xff0c;莫待无花空折…

【2.19】算法题2:贪心算法、动态规划、分治

题目&#xff1a;给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。子数组 是数组中的一个连续部分。方法一&#xff1a;贪心算法原理&#xff1a;若当前指针所指元素之前的和小…

学生和老师-课后程序(JAVA基础案例教程-黑马程序员编著-第四章-课后作业)

【案例4-4】学生和老师 【案例介绍】 1.案例描述 在班级中上课时&#xff0c;老师在讲台上讲课&#xff0c;偶有提问&#xff0c;会点名学生回答问题。虽然老师和学生都在讲话&#xff0c;但讲话的具体内容却不相同。本案例要求使用抽象类的知识编写一个程序实现老师上课的情…

Allegro如何快速清除多余的规则设置操作指导

Allegro如何快速清除多余的规则设置操作指导 在用Allegro做PCB设计的时候,会给PCB设置一些规则,在PCB设计完成之后,可能会有一些没有使用到的规则,如下图 Physical规则中的45OHM的规则是多余的 单独某个规则可以直接在规则管理器中删除,如果比较多可以用下面方法批量删除…

真实景观渲染技巧【Three.js】

受到一些很棒的 three.js 演示、与 covid 相关的旅行禁令以及可能在 pinterest 上花太多时间看美丽的旅行照片的启发——我开始看看我是否可以使用 three.js 和r3f在浏览器中渲染一个令人信服的风景场景。 推荐&#xff1a;将 NSDT场景编辑器 加入你的3D开发工具链。 在过去一个…

AnLogicFPGA设计的时序约束及时序收敛

本篇博文讲了三个内容&#xff1a;时序约束基本概念、时序约束命令、时序收敛技巧 时序约束基本概念 时序设计的实质就是满足每一个触发器的建立&#xff08;setup&#xff09;时间和保持&#xff08;hold&#xff09;时间。 建立时间(Tsu) 触发器的时钟信号沿到来以前&…

零信任-新华三H3C零信任介绍(12)

​目录 ​新华三零信任是什么&#xff1f; 新华三零信任架构特点 新华三零信任架构 新华三零信任架构适用场景 新华三零信任的未来发展展望 新华三零信任是什么&#xff1f; 建立新边界 全面身份化。新华三贯彻“永不信任&#xff0c;始终验证”的原则&#xff0c;通过对…

JavaWeb7-线程状态(生命周期)及转换过程

目录 1.所有线程状态&#xff08;共6种&#xff09; ①NEW-新建状态 ②RUNNABLE-运行状态 RUNNABLE&#xff08;得到时间片运行中状态&#xff09; READY&#xff08;已经保存了上下文&#xff0c;但还未得到时间片的就绪状态&#xff09;。 ③BLOCKED-阻塞状态 ④WAITI…

使用NutUI创建小程序和H5界面

做开发的时间长了&#xff0c;技术都是通用的&#xff0c;创建小程序和H5界面有很多的UI&#xff0c;本章节演示使用NutUI来创建&#xff0c;官网&#xff0c;NutUI - 移动端 Vue3 小程序组件库 1.使用HBuilder X创建一个uni-app的程序&#xff0c;如图所示 2. 安装UniNutUI …

nvm的使用与坑

1、nvm 介绍 Node Version Manager - 符合 POSIX 标准的 bash 脚本&#xff0c;用于管理多个活动的 node.js 版本 官网为 nvm-windows&#xff0c;点击这里进行下载 2、使用场景 比如有几个项目&#xff0c;这些项目的需求都不太一样&#xff0c;导致了这些个项目需要依赖的…

论文投稿指南——中文核心期刊推荐(管理学)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

多彩的声音-课后程序(JAVA基础案例教程-黑马程序员编著-第四章-课后作业)

【案例4-3】多彩的声音 记得 关注&#xff0c;收藏&#xff0c;评论哦&#xff0c;作者将持续更新。。。。 【案例介绍】 案例描述 设计和实现一个Soundable发声接口&#xff0c;该接口具有发声功能&#xff0c;同时还能调节声音大小。 Soundable接口的这些功能将由有3种声音…

【C++的OpenCV】第一课-opencv的介绍和安装(Linux环境下)

第一课-目录一、基本介绍1.1 官网1.2 git源码1.3 介绍二、OpenCV的相关部署工作2.1 Linux平台下部署OpenCV一、基本介绍 1.1 官网 opencv官网 注意&#xff1a;官网为英文版本&#xff0c;可以使用浏览器自带的翻译插件进行翻译&#xff0c;真心不推荐大家去看别人翻译的&am…

vue实现xml在线编辑功能

先看效果 避免误会 这是一个在线编辑器 我们可以在这上面随意的编写xml代码格式 我们修改上面的内容之后 就可以在控制台输出内容 如果这正是您想要的东西 那就可以先创建一个vue项目 我们先引入依赖 npm install brace -S npm install element-ui -S npm install vue-cli…

原型链污染

目录 前置知识 原型对象 prototype和__proto__的区别 原型链概念 原型链的继承 原型 链污染 原型链污染原理 javascript中可能会存在原型链污染的危险函数 原型链污染的实际应用 JavaScript中可以触发弹窗的函数 前置知识 原型对象 在JavaScript中&#xff0c;每个函…

9-静态链表及其有关操作

链表可以用malloc/new和结构体加指针的方式来实现&#xff0c;那种实现方式实现的链表又被称为动态链表。但是我们还可以利用数组的方式来实现一个链表&#xff0c;这种实现方式称为静态链表。 静态单链表 我们知道&#xff0c;一个链表节点主要由两大部分组成&#xff1a;数…

黑马Spring学习笔记(一)——IOC/DI核心概念、入门案例

目录 一、Spring系统架构和学习路线 二、Spring核心概念 2.1 IOC、IOC容器、Bean、DI 2.1.1 IOC&#xff08;Inversion of Control&#xff09;控制反转 2.1.2 DI&#xff08;Dependency Injection&#xff09;依赖注入 2.2 核心概念小结 三、入门案例 3.1 IOC入门…

【C++入门】引用、内联函数、auto关键字、基于范围的for循环(C++11)、指针空值nullptr(C++11)

文章目录引用引用概念引用特性引用使用场景常引用内联函数宏的优缺点&#xff1f;C有哪些技术替代宏&#xff1f;auto关键字auto不能推导的场景基于范围的for循环(C11)指针空值nullptr(C11)引用 引用概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&…

一篇搞懂UnitTest

unittest单元测试框架是受到 JUnit 的启发&#xff0c;与其他语言中的主流单元测试框架有着相似的风格。其支持测试自动化、支持将测试样例聚合到测试集中&#xff0c;并将测试与报告框架独立开来。unittest模块是Python标准库中的模块。在此之前需要先了解几个概念&#xff1a…