网络编程套接字——udp网络编程

news2024/11/26 5:28:14

目录

一、预备知识

1.端口        

2.TCP协议和UDP协议

3.socket编程接口

①socket 常见API

②sockaddr结构

二、网络编程

1.UDP网络程序

1.1服务器

①打印

②socket​编辑

③bind

④recvfrom ​编辑

1.2客户端

①sendto

 1.3提升通信的花样性

①将字符串返还

②注册

③消息路由


一、预备知识

1.端口        

         上篇我们讲到,就是数据与数据的交互,那数据之间的交互又是用户与用户之间的交互的体现,但本质上还是用户通过进程来进行与对面用户的进程通信

        但是每个主机上的进程成千上百哪能对应准确的进程来通信?

        需要两个数据就可以准确定位。

        IP:标识主机的唯一性;

        端口号(port):标识主机内的进程的唯一性;

        IP + port = socket ->全网唯一一个进程。

        一个进程可以和多个端口号绑定,但一个端口号只能和一个进程绑定。

        传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 "数据是谁发的, 要 发给谁"。

2.TCP协议和UDP协议

        这两个都是传输层协议。

        TCP协议:                                                  UDP协议

        ①有链接                                                     ①无连接

        ②可靠传输                                                 ②不可靠传输

        ③面向字节流                                             ③面向数据报

3.socket编程接口

①socket 常见API

// 创建 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);

②sockaddr结构

        我们看到这些接口中都有struct sockaddr*类型的参数,这个类型到底是什么?

        网络通信本质是不同主机进程之间的通信,那使用网络通信的接口让同一主机的进程之间通信也是可以的。

        那怎么实现呢?

        靠类型的强转实现。

        特定函数在使用结构之前,会先提取判断前16位数字,如果地址类型是AF_INET,就将struct sockaddr强转为struct sockadd_in类型,如果地址类型是AF_UNIX就强转为struct sockadd_un类型。

二、网络编程

1.UDP网络程序

        当然你想配合整体代码看的话,请点击此处的gitee浏览。

1.1服务器

①打印

        为了方便打印,我们先写一个打印函数。

#define DEBUG 0
#define NOTICE 1
#define WARNING 2
#define FATAL 3

const char *log_level[] = {"DEBUG", "NOTICE", "WARNING", "FATAL"};

void logMessage(int level, const char *format, ...)
{
    assert(level >= DEBUG);
    assert(level <= FATAL);
    char logInfor[1024];
    char *name = getenv("USER");
    va_list ap;
    va_start(ap, format);

    vsnprintf(logInfor, sizeof(logInfor) - 1, format, ap);

    va_end(ap);
    
    FILE * out = (level == FATAL) ? stderr : stdout;

    fprintf(out,"%s | %u | %s | %s\n",\
        log_level[level],\
        (unsigned int)time(nullptr),\
        name == nullptr ? "Unkown" : name,\
        logInfor
    );
}

②socket

        目的是创建一个套接字。

domain:本地通信还是网络通信 

一般有这几个选项:

 type:套接字类型决定了通信的时候的报文类型,一般有流式、用户数据报类型。

一般有这几个选项:

protocol:协议类型。网络通信中设置为0。

返回值:成功一个文件描述符被返回,失败返回-1,errno被设置。

        首先来使用一下socket这个函数。

int main()
{
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        logMessage(FATAL, "%s%d", strerror(errno), fd);
        exit;
    }
    logMessage(DEBUG, "socket create success : %d", fd);

    return 0;
}

结果:

        还要建一个类,作为初始的框架

class UdpServer
{
public:
    UdpServer()
    {
    }
    ~UdpServer()
    {
    }

public:
    void init()
    {
    }
    void start()
    {
    }

private:
    int sockfd;
};

        这里的创建套接字要放到init初始化中。

​​​​​​        // 1.create 套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            logMessage(FATAL, "socket: %s%d", strerror(errno), _sockfd);
            exit(1);
        }
        logMessage(DEBUG, "socket create success : %d", _sockfd);

③bind

        给一个套接字绑定上iP地址与端口号。

        // 2 bind
        // 2.1 填入基本信息到struct sockaddr_in 中
        struct sockaddr_in local;
        // 初始化
        bzero(&local, sizeof(local));
        // 填充域 AF_INET 网络通信 AF_UNIX 本地通信
        local.sin_family = AF_INET;
        // 填充对应的端口号 htons的作用是将本地序列转换为网络序列这样才能发送给对方
        local.sin_port = htons(_port);
        // 服务器的IP地址 xx.yy.aa.ccc 每个都是0-255的数字,有四个8比特位 正好放在uint36_t中
        // sin_addr也是一个结构体其中的元素是s._addr是被typedef过的uint32_t
        // INADDR_ANY就是0,一般不关注服务器绑定哪一个IP地址,服务器会自动bind,一般所有服务器都是这样做的
        // inet_addr将char *转换为s_addr,还会将主机序列转换为网络序列
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        // 2.2 bind网络信息
        if (bind(_sockfd, (const struct sockaddr *)&local, sizeof(local)) == -1)
        {
            logMessage(FATAL, "bind: %s%d", strerror(errno), _sockfd);
            exit(2);
        }
        logMessage(DEBUG, "socket success: %d", _sockfd);

       其中sockaddr_in的成员为:      

        其中包含端口号和IP地址。

        这时大致udpserver已经成型,我们来测试下。

static void Usage(const string proc)
{
    cout << "Usage:\n\t" << proc << " port [ip]" << endl;
}
    void start()
    {
        while (1)
        {
            logMessage(NOTICE, "udpserver runing");
            sleep(1);
        }
    }
int main(int argc, char *argv[])
{
    if (argc != 2 && argc != 3)
    {
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port = atoi(argv[1]);
    string ip;
    if (argc == 3)
    {
        ip = argv[2];
    }

    UdpServer svr(port, ip);
    svr.init();
    svr.start();

    return 0;
}

         这里的端口号最好不要绑定0-1023的端口号,这些端口号是服务器自己使用的对应特定服务的。         这时我们可以通过netstat -lnup来查看当前的网络服务。

④recvfrom 

        从特定socke中读取到buf里,长度为len,默认设置flags为0,阻塞式读取,src_addr用来接收发送方的参数,addrlen为src_addr的大小。返回值为读到的字节大小。

    void start()
    {
        char inbuffer[1024];  // 输入进来的数据放到inbuffer中
        char outbuffer[1024]; // 输出的数据放outbuffer中
        while (1)
        {
            struct sockaddr_in peer;      // 输出形参数
            socklen_t len = sizeof(peer); // 输入输出型参数
            ssize_t size = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (size > 0)
            {
                // 这里将读的数据看为字符串
                inbuffer[size] = 0;
            }
            else if (size == -1)
            {
                logMessage(WARNING, "recevfrom : %s %d", strerror(errno), _sockfd);
            }
            // 拿到发送方的IP地址 peer.sin_addr的类型是四字节uint36_t 要转换为string
            // peer.sin_port是从网络中接收到的是网络序列,ntohs目的是将网络序列转换为本地序列
            string peerip = inet_ntoa(peer.sin_addr);
            uint16_t peerport = ntohs(peer.sin_port);
            // 打印客户端IP与port 和信息
            logMessage(NOTICE, "[%s %d]# %s", peerip.c_str(), peerport, inbuffer);
        }
    }

1.2客户端

        以上写的都是服务端,现在完善一下客户端。

        上面提到过作为服务器不用bind特定的IP地址和port端口号,但是作为客户端必须知道服务器的IP地址和port端口号。

static void Usage(const string proc)
{
    cout << "Usage:\n\t"
         << "server IP ,server port" << endl;
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    // 1. 获取服务端
    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);

    // 2. 创建客户端
    // 2.1 创建socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sockfd > 0);

    // 2.2 client 需不需要bind 可以不用bind OS会自动帮我们bind 不推荐自己bind
    // 2.3 填写对应服务器信息
    struct sockaddr_in server;
    bzero(&server, sizeof(server));
    // 都需要转换为网络序列
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(serverip.c_str());
    server.sin_port = htons(serverport);
    ...

①sendto

        既然到了客户端,服务端来接收,客户端就要发送。

        通过指定的套接字,从缓冲区buf中读取len的长度的内容,默认flags为0阻塞式,dest_addr为目的地。

    // 3. 发送消息
    string output;
    while (1)
    {

        cout << "Please entry | ";
        getline(cin, output);

        // 发送
        sendto(sockfd, output.c_str(), output.size(), 0, (const struct sockaddr *)&server, sizeof(server));
    }

        写了这么多,我们来测试下。

        这里的IP地址127.0.0.1,是本地环回,就指的是本主机。

        当然这是本地之间的测试,你如果想远程测试可以,将udpclient发给他,然后将自己的IP地址告诉对方,自己打开udpserver并确定端口号,对方使用udpclient通过IP地址和端口号就可以远程通信了。当然记得使用g++进行编译时要加-static,库变成静态连接,这样对面没有对应的库也没有关系。具体的可以看我这篇博客。

        那要是没有两台linux呢?我们可以用Visual Studio做出windows版本的客户端。

代码:

#pragma warning(disable:4996)// 使warning去掉
#pragma comment(lib,"ws2_32.lib")// 所需要包含的连接库

#include <iostream>
#include <cstdio>
#include <cassert>
#include <string>
#include <WinSock2.h>

using namespace std;

int serverport = 8888;
string serverip ="121.4.139.131"; // 此处要填自己主机的IP地址

int main()
{
	// 用作初始化套接字
	WSADATA data;
	// 初始套接字
	WSAStartup(MAKEWORD(2, 2), &data);
	(void)data;

     SOCKET sockfd = socket(AF_INET, SOCK_DGRAM, 0);
     assert(sockfd > 0);

    struct sockaddr_in server;
    memset(&server, 0 ,sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(serverip.c_str());
    server.sin_port = htons(serverport);

    string output;
    while (1)
    {
        cout << "Please entry | ";
        getline(cin, output);

        sendto(sockfd, output.c_str(), output.size(), 0, (const struct sockaddr*)&server, sizeof(server));
    }
    closesocket(sockfd);
    WSACleanup();
    return 0;
}

结果:


         注意:有些云服务器没有开放对应的端口,是不能使用这种Udp方法通信的。我们要在自己云服务器的官网找到你的云服务器,在你的实例中点击防火墙,然后点击添加规则,我们这次测试为Udp所以点击Udp,再将你自定义的端口输入,点击确定,这样你就可以和远处的人通信了。虽然这次实验只是单方面的通信。


 1.3提升通信的花样性

①将字符串返还

        我们服务端只接受信息太单一了,我们将发送过来的字符串,转换成大写转换回去。这些代码分别写在上一步server打印信息和client发送信息之后。

代码:

UdpServer.cc:start():            
            // 转换字符串小写-大写
            for (int i = 0; i < strlen(inbuffer); i++)
            {
                if (isalpha(inbuffer[i]) && islower(inbuffer[i]))
                {
                    outbuffer[i] = toupper(inbuffer[i]);
                }
                else
                {
                    outbuffer[i] = inbuffer[i];
                }
            }
            sendto(_sockfd, outbuffer, strlen(outbuffer), 0, (struct sockaddr *)&peer, len);
            memset(outbuffer,0,sizeof(outbuffer));
UdpClient.cc:main():
        // 接收
        char buffer[1024];
        struct sockaddr temp;
        socklen_t len = sizeof(temp);
        ssize_t s = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&server, &len);
        if (s > 0)
        {
            buffer[s] = 0;
            cout << "Server output| " << buffer << endl;
        }

结果:               

②注册

        使用unordered_map来简单存储用户信息,来区分新老用户。

        客户端没有任何变化,服务端新增一个成员变量,将转换字符注释掉,向客户端发送字符也注释掉。注意服务端的代码都是写在类中的。

代码:

 void start()
    {
        char inbuffer[1024];  // 输入进来的数据放到inbuffer中
        char outbuffer[1024]; // 输出的数据放outbuffer中
        int i = 1;
        while (1)
        {
            struct sockaddr_in peer;      // 输出形参数
            socklen_t len = sizeof(peer); // 输入输出型参数
            ssize_t size = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (size > 0)
            {
                // 这里将读的数据看为字符串
                inbuffer[size] = 0;
            }
            else if (size == -1)
            {
                logMessage(WARNING, "recevfrom : %s %d", strerror(errno), _sockfd);
            }
            // 拿到发送方的IP地址 peer.sin_addr的类型是四字节uint36_t 要转换为string
            // peer.sin_port是从网络中接收到的是网络序列,ntohs目的是将网络序列转换为本地序列
            string peerip = inet_ntoa(peer.sin_addr);
            uint16_t peerport = ntohs(peer.sin_port);
            // 打印客户端IP与port 和信息
            logMessage(NOTICE, "[%s %d]# %s", peerip.c_str(), peerport, inbuffer);

            checkOnlineUser(i,peerip, peerport, peer);
        }
    }  
    bool checkOnlineUser(int &i,string &ip, uint16_t port, struct sockaddr_in &peer)
    {
        string userInfor = ip;
        userInfor += " ";
        userInfor += to_string(port);

        auto iter = user.find(userInfor);
        if (iter == user.end())
        {
            // 没找到
            user.insert({userInfor, peer});
            i = 1;
            if (i == 1)
            {
                cout << "新用户登录" << endl;
            }
        }
        else
        {
            // 找到了
            if (i == 1)
            {
                i = 0;
                cout << "老用户登录" << endl;
            }
        }
    }
private:
    unordered_map<string, struct sockaddr_in> user;

③消息路由

        将输出与命名管道结合起来。

        先在当前路径建立命名管道,修改代码,服务端中将客户端发回的消息使用管道保存起来。代码:

    void start()    
    {
        ......
        logMessage(NOTICE, "[%s %d]# %s", peerip.c_str(), peerport, inbuffer);
        checkOnlineUser(i, peerip, peerport, peer);
        messageRoute(inbuffer);
    }
    void messageRoute(string message)
    {
        for (auto &ch : user)
        {
            sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&(ch.second),sizeof(ch.second));
        }
    }

结果:

         我们呢,还可以使用线程来改造下。让主线程不断在发消息,另一个线程去接收发回来的消息。 

代码:

struct sockaddr_in server;

static void Usage(const string proc)
{
    cout << "Usage:\n\t"
         << "server IP ,server port" << endl;
}

void *fuc(void *argc)
{
    while (1)
    {
        int sockfd = *(int *)argc;
        // 接收
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));
        struct sockaddr temp;
        socklen_t len = sizeof(temp);
        ssize_t s = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&server, &len);
        if (s > 0)
        {
            buffer[s] = 0;
            cout << "Server output| " << buffer << endl;
        }
    }
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    // 1. 获取服务端
    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);

    // 2. 创建客户端
    // 2.1 创建socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sockfd > 0);

    // 2.2 client 需不需要bind 可以不用bind OS会自动帮我们bind 不推荐自己bind
    // 2.3 填写对应信息

    bzero(&server, sizeof(server));
    // 都需要转换为网络序列
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(serverip.c_str());
    server.sin_port = htons(serverport);

    pthread_t t;
    pthread_create(&t, nullptr, fuc, (void *)&sockfd);

    // 3. 发送消息
    string output;
    while (1)
    {

        cerr << "Please entry | ";
        getline(cin, output);

        // 发送
        sendto(sockfd, output.c_str(), output.size(), 0, (const struct sockaddr *)&server, sizeof(server));
    }
    close(sockfd);
    return 0;
}

         运行过程与上文的结果一样,这里就不演示了。

        这里的fifo好像没什么用啊?为什么要管道呢?

        我们启动服务器,在客户端输入消息,服务器再将消息返回,使用fifo来展示返回的消息。一个人这样使用其实没有fifo用处,但如果是多个人使用,无论谁向客户端输入消息,服务器都会返还所有它接受的消息到fifo中,意味着我们可以通过fifo来查看别人发的消息,这样不就可以双端聊天了嘛。至于fifo中不显示谁发送的消息,我们将server中的sendto所使用的buffer中填写从recevfrom中获取到的IP地址与端口,就可以辨识是谁发送的消息。

        到这里我们Udp网络程序的编写暂时告一段落基本的要求我们都已经实现,Udp的全部代码我已上传gitee,有兴趣的可以看一下。  

        限于篇幅,Tcp网络编程就移至下一节去讲,感谢观看,如有错误请指出,我们下次再见。      

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

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

相关文章

Individual Tree Segmentation Method Based on Mobile Backpack LiDAR Point Clouds

Abstract 单棵树 (IT) 分割对于森林管理、支持森林清查、生物量监测或树木竞争分析至关重要。光探测和测距 (LiDAR) 是这方面的一项突出技术&#xff0c;优于竞争技术。航空激光扫描 (ALS) 经常用于森林记录&#xff0c;在树顶表面显示良好的点密度。尽管使用多回波 ALS 可以收…

【虹科云展厅专题】虹科赋能汽车智能化云展厅——车载以太网/TSN专题

虹科2023年开年福利来了&#xff01; 聚焦前沿技术&#xff0c;【虹科赋能汽车智能化云展厅】正式上线&#xff0c;本次云展厅围绕“汽车以太网/TSN、汽车总线、智能网联、电子测试与验证、自动驾驶”等核心话题&#xff0c;为您带来如临展会现场般的讲演与介绍&#xff0c;更…

Unity入门基础语法

物体的坐标 transform.position 世界坐标 transform.localPosition 相对坐标 设置物体的坐标&#xff1a; this.transform.localPosition new Vector3(1.5f, 0, 2.0f); 帧更新 Update()&#xff0c;称为帧更新 此方法会被游戏引擎定时调用&#xff0c;已更新游戏的状态 …

基于Java+SpringBoot+vue+element实现物流管理系统

基于JavaSpringBootvueelement实现物流管理系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系…

SQL Studio:一款纯Web化SQL开发工具,关键是免安装还免费!

经常使用SQL工具的开发者对Navicat一定都不陌生。这款软件作为一款全球化的多数据库管理工具&#xff0c;这些年逐步得到全国各地SQLer&#xff08;SQL开发者&#xff09;的关注。 与其他很多外来的软件产品一样&#xff0c;由于价格原因&#xff0c;很多SQLer感觉不太适合适应…

聊聊微服务架构中的用户认证方案

传统的用户认证方案 我们直奔主题&#xff0c;什么是用户认证呢&#xff1f;对于大多数与用户相关的操作&#xff0c;软件系统首先要确认用户的身份&#xff0c;因此会提供一个用户登录功能。用户输入用户名、密码等信息&#xff0c;后台系统对其进行校验的操作就是用户认证。…

S7-1200与三菱FX5U系列PLC通过简单CPU通信功能实现以太网通信的具体方法

S7-1200与三菱FX5U系列PLC通过简单CPU通信功能实现以太网通信的具体方法 前提条件: 西门子S7-1200一侧需要在防护安全中选择连接机制,选择连接机制后在将这里面的“ 允许来自远程对象的PUT/GET通讯访问”这个选项勾选即可。 另外要注意,被访问的DB块要设置为非优化的块访问…

Go第 9 章:map

Go第 9 章&#xff1a;map 9.1 map 的基本介绍 map 是 key-value 数据结构&#xff0c;又称为字段或者关联数组。类似其它编程语言的集合&#xff0c; 在编程中是经常使用到 9.2 map 的声明 9.2.1基本语法 var map 变量名 map[keytype]valuetype 9.2.2map 声明的举例 m…

如果这都不是爱!谷歌承包广告牌喊话苹果;亚马逊裁员的业内分析;李玟VR演唱会明日上线;AMD发布会全程高能;GitHub今日热榜 | ShowMeAI资讯日报

&#x1f3a1; 『Google』再次买下大幅电子广告牌&#xff0c;喊话苹果推动 RCS 发展 一线消息&#xff0c;Google 在拉斯维加斯 Harmon Corner 投放了大型新年主题广告&#xff0c;喊话说服苹果采用 RCS 消息协议&#xff0c;不要在修复像素化的照片和视频上掉链子。视频显示…

YOLOv5-C3模块实现

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章地址&#xff1a; 365天深度学习训练营-第P8周&#xff1a;YOLOv5-C3模块实现&#x1f356; 作者&#xff1a;K同学啊一、前期准备1.设置GPUimport torch from torch import nn i…

数据库,计算机网络、操作系统刷题笔记26

数据库&#xff0c;计算机网络、操作系统刷题笔记26 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle…

消息队列如何保证消息幂等性消费

1 介绍 我们实际系统中有很多操作&#xff0c;不管你执行多少次&#xff0c;都应该产生一样的效果或返回一样的结果。 例如&#xff1a; 前端页面重复提交选中的数据&#xff0c;服务端只产生对应这个数据的一个反应结果&#xff0c;只保存一次数据。我们发起一笔付款请求&am…

裸露土堆智能识别检测系统 yolo

裸露土堆智能识别检测系统基于pythonyolo计算机视觉深度学习技术&#xff0c;对现场画面中土堆裸露情况进行实时分析检测&#xff0c;若发现画面中的土堆有超过40%部分裸露&#xff0c;则判定为裸露进行抓拍预警。我们选择当下YOLO最新的卷积神经网络YOLOv5来进行裸露土堆识别检…

商用密码安全性评估

商用密码应用安全性评估&#xff08;简称“密评”&#xff09;指在采用商用密码技术、产品和服务集成建设的网络和信息系统中&#xff0c;对其密码应用的合规性、正确性和有效性等进行评估。01办理依据 GM/T0054-2018《信息系统密码应用基本要求》 《信息系统密码测评要求&…

Linux内核内存分配函数kmalloc、kzalloc和vmalloc

在内核环境中&#xff0c;常用的内存分配函数主要有kmalloc、kzalloc和vmalloc这三个。既然这三函数都能在内核申请空间&#xff0c;那么这三个函数有什么区别呢&#xff1f;如何选用呢&#xff1f; kmalloc 首先是kmalloc&#xff0c;其函数原型为 // /include/linux/slab.…

acwing基础课——质数

由数据范围反推算法复杂度以及算法内容 - AcWing 常用代码模板4——数学知识 - AcWing 基本思想&#xff1a; 首先&#xff0c;我们给出质数的定义&#xff0c;指在大于1的自然数中&#xff0c;除了1和该数自身外&#xff0c;无法被其他自然数整除的数。这里考虑三个问题&…

笔记-鼠标悬浮展示图标

鼠标悬浮展示图标 .primaryLink {color: primary-color-dark;}.primaryLink:hover {cursor: pointer;color: link-hover-color-dark;}.itemAction {display: none; }.itemMenu:hover .itemAction {display: block; }

【数据结构进阶】并查集

并查集 正如它的名字一样&#xff0c;并查集&#xff08;Union-Find&#xff09;就是用来对集合进行 合并&#xff08;Union&#xff09; 与 查询&#xff08;Find&#xff09; 操作的一种数据结构。 合并 就是将两个不相交的集合合并成一个集合。 查询 就是查询两个元素是否属…

链表常见OJ题汇总(持续更新)

目录前言一、移除链表中的元素&#xff08;多指针法&#xff09;二、反转链表&#xff08;多指针法&头插法&#xff09;三、链表的中间结点&#xff08;算数法和双指针法&#xff09;四、链表中的第K个结点&#xff08;算数法&双指针法&#xff09;五、合并两个有序链表…

vue 父子组件设置 scoped, 如何导致滚动条失效的

vue父组件的页面结构 // 调用子组件 <process-time-line :nodeArr"nodeArr"></process-time-line> 父组件的样式 <style lang"scss" scoped> ::-webkit-scrollbar {width: 0px;height: 0px;} </style>子组件的页面结构 <div …