linux——网络套接字编程

news2024/11/26 18:37:34

目录

一.简单了解TCP和UDP协议

二.网络字节序

三.socket常见的编程接口

1.介绍接口

2.sockaddr结构

 四.简单的UDP网络程序

1.recvfrom和sendto

2.server.cc

3.client.cc

五.简单的TCP通信

1.client.cc

2.server.cc


一.简单了解TCP和UDP协议

此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识; 后面我们再详细讨论TCP的一些细节问题.

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

此处我们也是对UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识; 后面再详细讨论。

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

二.网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
  • 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
  • TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
  • 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
  • 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

#include <arpa/inet.h>
//  htonl() 将无符号整数,从主机字节顺序转换为网络字节顺序。
uint32_t htonl(uint32_t hostlong);
//  htons() 将无符号短整数,从主机字节顺序转换为网络字节顺序。
uint16_t htons(uint16_t hostshort);
//  ntohl() 将无符号整数,从网络字节顺序转换为主机字节顺序。
uint32_t ntohl(uint32_t netlong);
//  ntohs() 将无符号短整数,从网络字节顺序转换为主机字节顺序。
uint16_t ntohs(uint16_t netshort);
  • 这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
  • 如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

三.socket常见的编程接口

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

1.介绍接口

socket

  • 功能:创建套接字,表明我们需要的通信服务类型,和IP协议,tcp/udp通信都需要使用。
  • 参数:_1:通信类型,_2:协议类型,_3:默认给0。
  • 返回值:成功返回一个文件描述符,失败返回-1,错误码被设置。

bind

  • 功能:将端口号与该进程绑定,tcp/udp通信都需要使用。
  • 参数:_1:socket返回的文件描述符,_2:一种特定的结构,存储IP地址和端口号,通信协议类型。_3:这种特定结构的大小。
  • 返回值:成功返回0,失败返回-1,错误码被设置。

listen

  • 功能:为套接字设置backlog,并创建半连接队列和全连接队列,将TCP状态设置为TCP_LISTEN,tcp服务端通信才能使用。
  • 参数:_1:socket返回的文件描述符,_2:输出型参数,存储客户端的IP和端口信息,_3:输出型参数,第一个参数存储客户端信息结构的大小。
  • 返回值:成功返回0,失败返回-1,错误码被设置。

accept

  • 功能:接收来自客户端的连接请求,tcp服务端通信才能使用。
  • 参数:_1:socket返回的文件描述符,_2:接受客户端的最大数量。
  • 返回值:成功返回一个新的套接字专门用于和客户端进行通信,失败返回-1。

connect

  • 功能:函数会发起一个TCP连接请求。tcp客户端使用。
  • 参数:_1:socket返回的文件描述符,_2:一种特定的结构,存储IP地址和端口号,通信协议类型。_3:这种特定结构的大小。
  • 返回值:成功返回0,失败返回-1,错误码被设置。

2.sockaddr结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同.

  • IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.
  • IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,
  • 不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
  • socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;

struct sockaddr_in结构 :

  struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;			//端口
    struct in_addr sin_addr;		//ip地址

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
			   __SOCKADDR_COMMON_SIZE -
			   sizeof (in_port_t) -
			   sizeof (struct in_addr)];
  };

/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };

 四.简单的UDP网络程序

1.recvfrom和sendto

recvfrom

  • 功能:从指定的套接字上接收数据。
  • 参数:_1:文件描述符,_2:存储缓冲区,_3:标志位,一般为0,_4:数据发送者的地址和端口,_5:地址结构的大小。
  • 返回值:实际接收到的字节数,如果发生错误则返回-1。如果套接字被关闭或连接已断开,则返回0。

 sendto

  • 功能:向指定的目标地址发送数据报.
  • 参数:_1:创建好的socket的文件描述符,_2:指向需要发送数据的缓冲区的指针,_3要发送的数据的长度,_4:通常设为0,_5:这是一个指向包含数据报接收者的协议地址(如IP地址和端口号)的套接字地址结构的指针,其大小由addrlen参数指定,_6:这是一个指向sockaddr结构大小的指针。
  • 返回值:如果数据成功发送,则返回实际发送的字节数;如果出现错误,则返回-1。

2.server.cc

#include <iostream>
#include <unordered_map>
#include <cstdio>
#include <cstring>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;
const static int port = 8080;

class Udp_server
{
    enum
    {
        scket_err = 1,
        bind_err
    };

public:
    Udp_server(int po = port)
        : _port(po)
    {
    }

    void Init()
    {
        // 一.创建套接字,返回文件描述符,
        // 1.AF_INET:表示支持IPv4网络通信
        // 2.SOCK_DGRAM:支持无连接的不可靠的面向数据报的传输方式,即udp传输
        // 3.0:只支持一个协议来给定套接字
        _fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_fd == -1)
        {
            cout << "create socket error:" << strerror(errno) << endl;
            exit(scket_err);
        }
        else
        {
            cout << "create socket successful" << endl;
        }

        struct sockaddr_in local;
        // 清空
        bzero(&local, sizeof(local));
        // 写入端口号,htons:主机序列转网络序列
        local.sin_port = htons(_port);
        // 写入AF_INET,表示网络通信。
        local.sin_family = AF_INET;
        // 写入IP地址,inet_addr:
        // 1.将点分十进制ip,转化为整形,
        // 2.并且将主机序列转换为网络序列
        // 3.最为一款云服务器,或者服务器,一般不止一个网卡,或者IP,在设置IP时不能直接将ip设置死
        local.sin_addr.s_addr = INADDR_ANY;
        // 二.绑定
        if (bind(_fd, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            cerr << "bind error:" << strerror(errno) << endl;
            exit(bind_err);
        }
        else
        {
            cout << "bind successful" << endl;
        }
    }

    void Start()
    {
        // 1.接受信息
        char buff[1024];
        struct sockaddr_in client;
        socklen_t client_len = sizeof(client);
        while (1)
        {
            int read_size = recvfrom(_fd, buff, sizeof(buff), 0, (struct sockaddr *)&client, &client_len);
            if (read_size > 0)
            {
                buff[read_size] = 0;
            }
            cout << "client:" << buff << endl;

            // 提取客户端信息
            string clientip = inet_ntoa(client.sin_addr); // ip
            int clientport = ntohs(client.sin_port);

            // 发送信息
            char send[1024];
            sprintf(send, "(-_-) %s (-_-)", buff);
            sendto(_fd, send, sizeof(send), 0, (const struct sockaddr *)&client, client_len);
        }
    }

    ~Udp_server()
    {
    }

private:
    int _port;
    int _fd;
};

int main()
{
    cout << "00000000" << endl;

    Udp_server server(8081);

    server.Init();
    server.Start();

    return 0;
}

3.client.cc

#include <iostream>
#include <cstdio>
#include <cstring>

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

using namespace std;
//./client ip port

int main(int argc, char *args[])
{
    // 1.创建按套接字
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    // 2.获取ip和端口
    string server_ip = args[1];
    int server_prot = atoi(args[2]);
    // 3.明确连接的服务器
    struct sockaddr_in server;
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());
    server.sin_port = htons(server_prot);

    while (1)
    {
        string str;
        str.clear();
        getline(cin, str);

        // 发送信息
        sendto(fd, str.c_str(), str.length(), 0, (struct sockaddr *)&server, sizeof(server));
        // 接受信息
        char buff[1024];
        struct sockaddr_in sersock;
        socklen_t sersock_len = sizeof(sersock);
        //
        int n = recvfrom(fd, buff, sizeof(buff), 0, (struct sockaddr *)&sersock, &sersock_len);
        if (n > 0)
            buff[n] = 0;
        else
            continue;
        cout << "server:" << buff << endl;
    }
    return 0;
}

代码测试:

五.简单的TCP通信

1.client.cc

#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
using namespace std;

enum err
{
    SOCKER_ERR = 1,
    BIND_ERR,
    LISTEN_ERR,
    ACCEPT_ERR,
    CONNECT_ERR
};

int main(int argc, char *argv[])
{
    string port = argv[2];
    string ip = argv[1];

    // 创建TCP套接字
    int sock = socket(AF_INET, SOCK_STREAM, 0);

    // 向服务端建立连接
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(port.c_str()));
    // net_aton()将网络主机地址cp从IPv4数字和点符号转换为二进制形式(以网络字节为单位)
    // 并将其存储在inp指向的结构中
    inet_aton(ip.c_str(), &server.sin_addr);
    int count = 1;
    while (connect(sock, (struct sockaddr *)&server, sizeof(server)) != 0)
    {
        sleep(1);
        cout << "正在尝试重连..."
             << "[" << count++ << "/5]" << endl;
        if (count == 6)
            exit(CONNECT_ERR);
    }
    // 连接成功
    while (1)
    {
        string message;
        cout << "Enter>>> ";
        getline(cin, message);
        write(sock, message.c_str(), message.length());

        char buff[1024];
        int n = read(sock, buff, sizeof(buff));
        if (n > 0) // 读取成功
        {
            buff[n] = 0;
            cout << "server:" << buff << endl;
        }
        else if (n == 0) // 服务器端关闭了
        {
            cout << "server quit,me too" << endl;
            break;
        }
        else // 读取出错
        {
            cout << "read err" << endl;
            break;
        }
    }
    close(sock);

    return 0;
}

2.server.cc

#include <iostream>
#include <string>
#include <functional>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>

using namespace std;
const int static backlog = 32;

enum err
{
    SOCKER_ERR = 1,
    BIND_ERR,
    LISTEN_ERR,
    ACCEPT_ERR,
    CONNECT_ERR
};
class tcp_server;
struct Args
{
    Args(int sock, uint16_t port, string ip, tcp_server *server)
        : _sock(sock), _port(port), _ip(ip), _server(server)
    {
    }
    int _sock;
    uint16_t _port;
    string _ip;
    tcp_server *_server;
};

class tcp_server
{
public:
    tcp_server(uint16_t port, function<string(string)> func)
        : _port(port), _func(func)
    {
    }

    void Init()
    {
        // 1.创建套接字
        // AF_INET:使用IPv4通信。
        // SOCK_STREAM:面向字节流,可靠的,有序,双向的,tcp通信。
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock == -1)
        {
            cerr << "socket err:" << strerror(errno) << endl;
            exit(SOCKER_ERR);
        }
        else
        {
            cerr << "socket successful" << endl;
        }

        // 2.绑定
        struct sockaddr_in host;
        memset(&host, 0, sizeof(host));
        host.sin_family = AF_INET; // #define AF_INET 2
        host.sin_port = htons(_port);
        host.sin_addr.s_addr = htonl(INADDR_ANY); // #define INADDR_ANY 0
        if (bind(_listensock, (struct sockaddr *)&host, sizeof(host)) == -1)
        {
            cerr << "bind err:" << strerror(errno) << endl;
            exit(BIND_ERR);
        }
        else
        {
            cerr << "bind successful" << endl;
        }

        // 3.监听
        if (listen(_listensock, backlog) == -1)
        {
            cerr << "listen err:" << strerror(errno) << endl;
            exit(LISTEN_ERR);
        }
        else
        {
            cerr << "listen successful" << endl;
        }
    }

    void Start()
    {
        // v2.1子进程回收
        signal(SIGCHLD, SIG_IGN);

        while (1)
        {
            // 4.接受请求
            // 表明接受的客户端主机
            struct sockaddr_in client_host;
            socklen_t client_len = sizeof(client_host);
            int sock = accept(_listensock, (struct sockaddr *)&client_host, &client_len);
            if (sock == -1)
            {
                cerr << "accpet err:" << strerror(errno) << endl;
                exit(ACCEPT_ERR);
            }
            else
            {
                cerr << "socket successful,get a new client"
                     << "fd:" << sock << endl;
            }

            // 提取客户端信息
            uint16_t client_port = ntohs(client_host.sin_port);
            // inet_ntoa: 将网络主机地址(以网络字节序)转换为IPv4点分十进制的字符串.
            string client_ip = inet_ntoa(client_host.sin_addr);

            // 多线程处理接受的客户端
            pthread_t t;
            pthread_create(&t, nullptr, threadRun, new Args(sock, client_port, client_ip, this));
        }
    }

    static void *threadRun(void *args)
    {
        pthread_detach(pthread_self());
        Args *ts = (Args *)(args);
        ts->_server->service(ts->_sock, ts->_port, ts->_ip);
        return nullptr;
    }

    void service(int sock, uint16_t client_port, string client_ip)
    {
        string name = to_string(client_port) + "-" + client_ip;
        char buff[1024];
        while (1)
        {
            int n = read(sock, buff, sizeof(buff) - 1);
            if (n > 0) // 读取成功
            {
                buff[n] = 0;
                // 读取成功之后,处理也业务
                string recvmess = _func(name + ":" + buff);
                // 将信息返回
                write(sock, recvmess.c_str(), recvmess.size());
            }
            else if (n == 0)
            {
                // 对方将连接关闭了
                close(sock);
                std::cout << name << " quit, me too" << std::endl;
                break;
            }
            else
            {
                close(sock);
                cout << "read err" << strerror(errno) << endl;
                break;
            }
        }
    }

private:
    uint16_t _port;                 // 端口
    int _listensock;                // 监听套接字
    function<string(string)> _func; // 服务端处理的业务
};

string func(string message)
{
    return message + "(-_-)";
}

int main(int argc, char *argv[])
{
    int port = atoi(argv[1]);
    tcp_server server(port, func);
    server.Init();
    server.Start();

    return 0;
}

代码测试:

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

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

相关文章

Unity在Project右键点击物体之后获取到点击物体的名称

Unity在Project右键点击物体之后获取到点击物体的名称 描述&#xff1a; 在Unity的Project右键点击物体之后选择对应的菜单选项点击之后打印出物体的名称 注意事项 如果获取到文件或者预制体需要传递objcet类型&#xff0c;然后使用 GameObject.Instantiate((GameObject)se…

人工智能-深度学习计算:层和块

我们关注的是具有单一输出的线性模型。 在这里&#xff0c;整个模型只有一个输出。 注意&#xff0c;单个神经网络 &#xff08;1&#xff09;接受一些输入&#xff1b; &#xff08;2&#xff09;生成相应的标量输出&#xff1b; &#xff08;3&#xff09;具有一组相关 参数…

BUUCTF RSA4

BUUCTF RSA4 下载题目&#xff0c;可见文件给出了3组n和c N 331310324212000030020214312244232222400142410423413104441140203003243002104333214202031202212403400220031202142322434104143104244241214204444443323000244130122022422310201104411044030113302323014101…

基于仿真的飞机ICD工具测试

机载电子系统是飞机完成飞行任务的核心保障之一。从1949年新中国建立至今70余年的发展过程中&#xff0c;随着我国在航空航天领域的投资逐年增多&#xff0c;机载电子系统大致经历了四个发展过程阶段&#xff0c;按照出现的先后顺序进行排序&#xff0c;分别为&#xff1a; 1、…

MyBatis实现多表映射、分页显示、逆向工程

目录 一、MyBatis实现多表映射 1.1 实体类设计 1.2 一对一关系实现案例 1.3 对多配置实现案例 1.4 设置自动映射与n张表关联映射 二、MyBatis实现分页功能 2.1 mybatis插件工作原理 2.2 引入插件与插件的使用 三、逆向工程插件 3.1 什么是逆向工程 3.2 MyBat…

vue3路由配置

npm下载安装路由 npm install vue-router4 --save新建router目录 新建routes路由表可以分离&#xff0c;也可以写在路由文件下的index.js 想用方式引入&#xff0c;需要新建vite.config.ts文件配置

【Redis】使用Java操作Redis

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Redis》。&#x1f3af;&#x1f3af; &#x1f4…

文本内容转换成语音播放的工具:Speech Mac

Speech Mac版是一款适用于Mac电脑的语音合成工具。它将macOS语音合成器的所有功能整合到一个易于使用的界面中。通过Speech Mac版&#xff0c;用户可以选择40多种声音和语言&#xff0c;方便地将文本转换为语音。用户可以将文本拖放或粘贴到Speech中&#xff0c;并随时更改语音…

JPA编程使用注解@LastModifiedDate,自动更新修改时间字段

一、背景 jpa编程中&#xff0c;我们一般会设计以下五个字段&#xff1a; 在新增记录的时候&#xff0c;无需对创建时间和修改时间进行赋值。 此外&#xff0c;你还得在类上&#xff0c;增加注解EntityListeners(AuditingEntityListener.class)。 EntityListeners(Auditing…

用中文编程工具给澳大利亚客户定制开发的英文版服装进销存软件应用实例

用中文编程工具给澳大利亚客户定制开发的英文版服装进销存软件应用实例 软件从2016年一直用到现在&#xff0c;而且开的分店也是安装的这个软件&#xff0c;上图是定制打印的格式。 该编程工具不但可以连接硬件&#xff0c;而且可以开发大型的软件。 编程系统化课程总目录及明…

Linux下的IMX6ULL——构建bootloader、内核、文件系统(四)

前言&#xff1a; Linux 平台上有许多开源的嵌入式linux系统构建框架(框架的意思就是工 具)&#xff0c;这些框架极大的方便了开发者进行嵌入式系统的定制化构建&#xff0c;目前比较常 见的有OpenWrt, Buildroot, Yocto,等等。其中Buildroot功能强大&#xff0c;使用 简单&…

CY3-N3的荧光特性Cyanine3 azide星戈瑞

CY3-N3是一种荧光染料&#xff0c;其荧光特性通常取决于其分子结构和环境条件。CY3-N3的激发波长通常位于500到550纳米之间。这表示当暴露在具有适当激发光的条件下时&#xff0c;CY3-N3会吸收光并进入激发态。 CY3-N3的发射波长通常位于550到650纳米之间。这意味着在激发后&a…

Mac连接linux的办法(自带终端和iterm2)

1. 使用Mac自带终端Terminal 1.1 点击右上角的聚焦搜索&#xff0c;再输入终端 1.2 查找linux系统的ip地址 在虚拟机里输入如下命令&#xff0c;找到蓝色区域的就是ip地址 ip addr 如果没有显示ip地址&#xff0c;可以重新安装一下虚拟机&#xff0c;之后确保以太网的连接是打…

竹胶板木板片:多功能建筑模板的理想选择

竹胶板木板片作为一种多功能的建筑模板材料&#xff0c;在各类建筑项目中扮演着重要的角色。其防水、耐磨的特性使其成为庭院、阳台等室外空间制模的理想选择。本文将重点介绍竹胶板木板片的特点以及其在建筑模板领域的应用。 竹胶板木板片由优质的竹材制成&#xff0c;经过胶合…

Java中Deque栈对象的增删查(所有方法详解)

1、Deque栈的增删查方法总结 2、方法增删查 栈顶添加&#xff1a;push、offFirst栈尾添加&#xff1a;add、offer、offerLast栈顶删除&#xff1a;remove、pop、poll、pollFirst栈尾删除&#xff1a;pollLast栈顶查看&#xff1a;peek、peekFirst栈尾查看&#xff1a;peekLast…

Miniconda 使用进阶,把它添加到右键菜单中

因为最近有写 python 的需求&#xff0c;且我是用 Miniconda 来管理本地 python 环境的&#xff0c;所以就涉及到需要频繁的进入到环境的命令行中&#xff0c;每次都需要通过官方提供的快捷方式&#xff0c;然后还要一路设置环境和路径&#xff0c;好烦呀。因为之前添加过 Wind…

OceanMind海睿思加入江苏省勘察设计行业协会,数字化能力受勘察设计行业认可

近日&#xff0c;中新赛克海睿思 通过江苏省勘察设计行业协会八届五次常务理事会议审议批准&#xff0c;正式成为江苏省勘察设计行业协会会员单位。 江苏省勘察设计行业协会 江苏省勘察设计行业协会成立于1986年4月&#xff0c;是我国勘察设计行业成立协会较早的省份之一&…

HarmonyOS 数据管理与应用数据持久化(二)

通过键值型数据库实现数据持久化 场景介绍 键值型数据库存储键值对形式的数据&#xff0c;当需要存储的数据没有复杂的关系模型&#xff0c;比如存储商品名称及对应价格、员工工号及今日是否已出勤等&#xff0c;由于数据复杂度低&#xff0c;更容易兼容不同数据库版本和设备…

FreeRTOS_软件定时器

目录 1. 软件定时器简介 2. 定时器服务/Daemon 任务 2.1 定时器服务任务与队列 2.2 定时器相关配置 2.2.1 configUSE_TIMERS 2.2.2 configTIMER_TASK_PRIORITY 2.2.3 configTIMER_QUEUE_LENGTH 2.2.4 configTIMER_TASK_STACK_DEPTH 2.3 单次定时器和周期定时器 2.4 复…

在公共安全场景下,智能监控如何做到保障安全的同时又最大化地提供便利?

智能监控系统应用的场景十分广泛&#xff0c;其中&#xff0c;公共安全场所的需求尤为重要&#xff0c;为保障公共区域的安全&#xff0c;提升人民群众的归属感&#xff0c;增强公共场所的安全性&#xff0c;智慧安防EasyCVR智能视频监控系统做出了极大努力。具体细节如下&…