【计算机网络学习之路】TCP socket编程

news2025/1/17 23:05:51

文章目录

  • 前言
  • 一. 服务器
    • 1. 初始化服务器
    • 2. 启动服务器
  • 二. 客户端
  • 三. 多进程服务器
  • 结束语

前言

本系列文章是计算机网络学习的笔记,欢迎大佬们阅读,纠错,分享相关知识。希望可以与你共同进步。

本篇博客基于UDP socket基础,介绍TCP socket编程接口和细节

UDP socket编程可参看【计算机网络学习之路】UDP socket编程

本次编写的服务器和客户端依然是最简单的echo服务器

一. 服务器

服务器的基本框架:

tcp_server.hpp

#pragma once

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

namespace ns_server
{
    const uint16_t default_port = 8888;
    class TcpServer
    {
    public:
        TcpServer(uint16_t port = default_port) : _port(port){}
        void InitServer(){}//初始化服务器
        void Start(){}//启动服务器
        ~TcpServer(){}
    private:
        int _sock; // 监听套接字
        uint16_t _port;  // 端口号
    };
}

tcp_server.cc

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

using namespace std;
using namespace ns_server;

static void usage(char*argv)
{
    cout<<"Usage\n\t"<<argv<<" serverPort"<<endl;
}
int main(int argc,char*argv[])
{
    if(argc!=2)
    {
        usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port=atoi(argv[1]);

    unique_ptr<TcpServer> usvr(new TcpServer(echo,port));
    usvr->InitServer();
    usvr->Start();

    return 0;
}

1. 初始化服务器

服务器的初始化,还是一样的

  1. 创建套接字
  2. 绑定套接字
 void InitServer()
{
	// 1.创建套接字
    _sock = socket(AF_INET, SOCK_STREAM, 0);
    if (_listensock < 0)
    {
		std::cerr << "create sock error," << strerror(errno) << std::endl;
        exit(1);
    }

    std::cout << "create listensock success: " << _sock << std::endl;

    // 2.绑定套接字
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(_port);
    local.sin_addr.s_addr = INADDR_ANY;

    if (bind(_sock , (struct sockaddr *)&local, sizeof(local)) < 0)
    {
		std::cerr << "bind error," << strerror(errno) << std::endl;
        exit(2);
    }
}

需要注意的是,socket的第二个参数为SOCK_STREAM面向字节流

TCP与UDP不同的地方是,TCP是面向连接的,UDP是无连接的
所以TCP还需要listen

在这里插入图片描述

返回值:成功返回0,失败返回-1并设置错误码

backlog参数需要在后续TCP详解中学习,先定义大小为32

const int backlog = 32;
void InitServer()
{
	// 1.创建套接字
    _sock = socket(AF_INET, SOCK_STREAM, 0);
    if (_listensock < 0)
    {
		std::cerr << "create sock error," << strerror(errno) << std::endl;
        exit(1);
    }

    std::cout << "create listensock success: " << _sock << std::endl;

    // 2.绑定套接字
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(_port);
    local.sin_addr.s_addr = INADDR_ANY;

    if (bind(_sock , (struct sockaddr *)&local, sizeof(local)) < 0)
    {
		std::cerr << "bind error," << strerror(errno) << std::endl;
        exit(2);
    }
	
	// 3.监听
    if (listen(_listensock, backlog) < 0)
    {
        std::cerr << "listen error," << strerror(errno) << std::endl;
        exit(3);
    }
}

初始化到此就结束了
接下来是启动服务器

2. 启动服务器

TCP通过accept获取客户端连接

在这里插入图片描述

  • sockfd:socket返回的文件描述符
  • addr:输入输出型参数,客户端信息的结构体
  • addrlen:输入输出型参数,结构体大小。注意:需要传入addr的大小
  • 返回值是网络文件描述符

在TCP中,socket返回的网络文件可以理解为连接文件,内部保存了连接信息
而accept是从连接文件中获取连接,然后创建套接字,网络文件。
真正通信的是connect创建的网络文件

我们将私有成员的_sock改为_listensock

void Start()
{
	while (true)
	{
		struct sockaddr_in client;
        memset(&client, 0, sizeof(client));
        socklen_t len = sizeof(client);
        int sock = accept(_listensock, (struct sockaddr *)&client, &len);
        if (sock < 0)
        {
			std::cerr << "accept error" << std::endl;
			continue;
		}

        // 提取客户端信息
        std::string clientIp = inet_ntoa(client.sin_addr);
        uint16_t clientPort = ntohs(client.sin_port);

        std::string name = "[" + clientIp + ":" + std::to_string(clientPort) + "]";
        std::cout << "create sock " << sock << " from " << _listensock << std::endl;
    }
}

接下来就可以在connect返回的套接字中读写数据了。
本次使用readwrite

void Start()
{
	while (true)
	{
		struct sockaddr_in client;
        memset(&client, 0, sizeof(client));
        socklen_t len = sizeof(client);
        int sock = accept(_listensock, (struct sockaddr *)&client, &len);
        if (sock < 0)
        {
			std::cerr << "accept error" << std::endl;
			continue;
		}

        // 提取客户端信息
        std::string clientIp = inet_ntoa(client.sin_addr);
        uint16_t clientPort = ntohs(client.sin_port);

        std::string name = "[" + clientIp + ":" + std::to_string(clientPort) + "]";
        std::cout << "create sock " << sock << " from " << _listensock << std::endl;

        char buffer[1024];
        while (true)
        {
			int n = read(sock, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
				buffer[n] = '\0';
                std::cout << name << "# " << buffer << std::endl;
                std::string responce = buffer;//返回收到的数据
                int m = write(sock, responce.c_str(), responce.size());
            }
            else if (n == 0)
            {
                // 写端关闭
                std::cout << name << " quit,me to" << std::endl;
                close(sock);
                break;
            }
            else
            {
                // 读数据异常
                std::cerr << "read error" << std::endl;
                break;
            }
		}
	}
}

完整代码:
tcp_server.hpp

#pragma once

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

namespace ns_server
{
    const uint16_t default_port = 8888;
    const int backlog = 32;
    class TcpServer
    {
    public:
        TcpServer(func_t func, uint16_t port = default_port) : _port(port), _func(func)
        {
        }

        void InitServer()
        {
            // 1.创建套接字
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                std::cerr << "create sock error," << strerror(errno) << std::endl;
                exit(1);
            }

            std::cout << "create listensock success: " << _listensock << std::endl;

            // 2.绑定套接字
            struct sockaddr_in local;
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;

            if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                std::cerr << "bind error," << strerror(errno) << std::endl;
                exit(2);
            }

            // 3.监听
            if (listen(_listensock, backlog) < 0)
            {
                std::cerr << "listen error," << strerror(errno) << std::endl;
                exit(3);
            }
        }

        void Start()
        {
            while (true)
            {
                struct sockaddr_in client;
                memset(&client, 0, sizeof(client));
                socklen_t len = sizeof(client);
                int sock = accept(_listensock, (struct sockaddr *)&client, &len);
                if (sock < 0)
                {
                    std::cerr << "accept error" << std::endl;
                    continue;
                }

                // 提取客户端信息
                std::string clientIp = inet_ntoa(client.sin_addr);
                uint16_t clientPort = ntohs(client.sin_port);

                std::string name = "[" + clientIp + ":" + std::to_string(clientPort) + "]";

                std::cout << "create sock " << sock << " from " << _listensock << std::endl;

                char buffer[1024];
                while (true)
                {
                    int n = read(sock, buffer, sizeof(buffer) - 1);
                    if (n > 0)
                    {
                        buffer[n] = '\0';
                        std::cout << name << "# " << buffer << std::endl;
                        std::string responce = buffer;
                        int m = write(sock, responce.c_str(), responce.size());
                    }
                    else if (n == 0)
                    {
                        // 写端关闭
                        std::cout << name << " quit,me to" << std::endl;
                        close(sock);
                        break;
                    }
                    else
                    {
                        // 读数据异常
                        std::cerr << "read error" << std::endl;
                        break;
                    }
                }
            }
        }

        ~TcpServer()
        {
        }

    private:
        int _listensock; // 监听套接字
        uint16_t _port;  // 端口号
    };
}

PS:上述的服务器是单进程,所以只能同时处理一个客户端,读者可以尝试添加一下多进程,多线程或者线程池

本篇博客最后会贴出多进程的方案

二. 客户端

客户端就不作封装了

最开始也是要创建套接字
然后TCP的客户端需要connect服务器

在这里插入图片描述

  • sockfd:socket返回的文件描述符
  • addr:服务器信息的结构体
  • addrlen:结构体大小。
  • 返回值:成功返回0,失败返回-1并设置错误码

注意:connect时OS会bind客户端
UDP是在发送数据时才会bind

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

using namespace std;

static void usage(char *argv)
{
    cout << "Usage:\n\t" << argv << " serverIp serverPort" << endl;
}

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

    string serverIp = argv[1];
    uint16_t serverPort = atoi(argv[2]);

    // 1.创建套接字
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        cerr << "create sock error," << strerror(errno) << endl;
        exit(SOCKET_ERR);
    }

    cout<<"create sock sucess:"<<sock<<endl;

    // 2. 连接
    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);

    int cnt = 5; // 记录重连次数
    // connect时会bind
    while (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
    {
        cout << "正在重连...还有" << cnt-- << "次" << endl;
        if (cnt <= 0)
        {   
            cerr<<"连接失败"<<endl;
            exit(CONNECT_ERR);
        }
        sleep(1);
    }
    // 连接成功
    string name = "["+serverIp + ":" + to_string(serverPort)+"]";
    cout << "connect " << name << " sucess" << endl;


    return 0;
}

然后也可以开始读写数据了

完整代码:

tcp_client.cc

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

using namespace std;

static void usage(char *argv)
{
    cout << "Usage:\n\t" << argv << " serverIp serverPort" << endl;
}

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

    string serverIp = argv[1];
    uint16_t serverPort = atoi(argv[2]);

    // 1.创建套接字
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        cerr << "create sock error," << strerror(errno) << endl;
        exit(SOCKET_ERR);
    }

    cout<<"create sock sucess:"<<sock<<endl;

    // 2. 连接
    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);

    int cnt = 5; // 记录重连次数
    // connect时会bind
    while (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
    {
        cout << "正在重连...还有" << cnt-- << "次" << endl;
        if (cnt <= 0)
        {   
            cerr<<"连接失败"<<endl;
            exit(CONNECT_ERR);
        }
        sleep(1);
    }

    // 连接成功
    string name = "["+serverIp + ":" + to_string(serverPort)+"]";
    cout << "connect " << name << " sucess" << endl;

    // 发送消息
    while (true)
    {
        cout << "please enter your message# ";
        string message;
        getline(cin, message);

        int n = write(sock, message.c_str(), message.size());
        if (n < 0)
        {
            cerr << "write error," << strerror(errno) << endl;
            break;
        }
        else if (n == 0)
        {
            cout << "读端关闭,停止写" << endl;
            break;
        }

        char buffer[1024];
        int m = read(sock, buffer, sizeof(buffer) - 1);
        if (m > 0)
        {
            buffer[n] = '\0';
            cout<<name<<" echo "<<buffer<<endl;
        }
        else if (m == 0)
        {
            // 写端关闭
            std::cout << name << " quit,me to" << std::endl;
            close(sock);
            break;
        }
        else
        {
            // 读数据异常
            std::cerr << "read error" << std::endl;
            break;
        }
    }

    return 0;
}

三. 多进程服务器

tcp_server.hpp

#pragma once

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


namespace ns_server
{
    const uint16_t default_port = 8888;
    const int backlog = 32;
   
    class TcpServer
    {
    public:
        TcpServer(uint16_t port = default_port) : _port(port)
        {
        }

        void InitServer()
        {
            // 1.创建套接字
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                std::cerr << "create sock error," << strerror(errno) << std::endl;
                exit(1);
            }

            std::cout << "create listensock success: " << _listensock << std::endl;

            // 2.绑定套接字
            struct sockaddr_in local;
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;

            if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                std::cerr << "bind error," << strerror(errno) << std::endl;
                exit(2);
            }

            // 3.监听
            if (listen(_listensock, backlog) < 0)
            {
                std::cerr << "listen error," << strerror(errno) << std::endl;
                exit(3);
            }
        }

        

        void Start()
        {
        	//忽略子进程的信号,不需要等待子进程退出(推荐!!!)
            signal(SIGCHLD,SIG_IGN);
            while (true)
            {
                struct sockaddr_in client;
                memset(&client, 0, sizeof(client));
                socklen_t len = sizeof(client);
                int sock = accept(_listensock, (struct sockaddr *)&client, &len);
                if (sock < 0)
                {
                    std::cerr << "accept error" << std::endl;
                    continue;
                }
                std::cout << "create sock " << sock << " from " << _listensock << std::endl;

                // 多进程
                pid_t id = fork();
                if (id < 0)
                {
                    close(sock);
                    continue;
                }
                else if (id == 0)
                {
                    //子进程
                    close(_listensock);//建议关掉不需要的fd
                    if(fork()>0)
                        exit(0);//子进程退掉,后续为孙子进程
                    // 提取客户端信息
                    std::string clientIp = inet_ntoa(client.sin_addr);
                    uint16_t clientPort = ntohs(client.sin_port);
                    service(sock, clientIp, clientPort);
                    exit(0);
                }
                //父进程
                //一定关掉不需要的fd,防止fd泄露
                close(sock);

                //pid_t ret=waitpid(id,nullptr,0);//默认为阻塞等待
                //pid_t ret=waitpid(id,nullptr,WNOHANG);//非阻塞
                //if(ret==id) std::cout<<"wait "<<id<<" sucess"<<std::endl;
            }
        }

		void service(int sock, std::string &clientIp, uint16_t&clientPort)
        {
            std::string name = "[" + clientIp + ":" + std::to_string(clientPort) + "]";
            char buffer[1024];
            while (true)
            {
                int n = read(sock, buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = '\0';
                    std::cout << name << "# " << buffer << std::endl;
                    std::string responce = buffer;
                    int m = write(sock, responce.c_str(), responce.size());
                }
                else if (n == 0)
                {
                    // 写端关闭
                    std::cout << name << " quit,me to" << std::endl;
                    close(sock);
                    break;
                }
                else
                {
                    // 读数据异常
                    std::cerr << "read error" << std::endl;
                    break;
                }
            }
        }

        ~TcpServer()
        {
        }

    private:
        int _listensock; // 监听套接字
        uint16_t _port;  // 端口号
    };
}

结束语

本篇博客到此结束,感谢看到此处。
欢迎大家纠错和补充
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

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

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

相关文章

windows系统玩游戏找不到d3dx9_35.dll缺失的解决方法

分享一个我们在打开游戏或许软件过程中遇到的问题——“由于找不到d3dx9_35.dll,无法继续执行代码”的五个修复方案。这个问题可能会影响到我们的工作和娱乐效率&#xff0c;甚至可能导致工作的延期。因此&#xff0c;我希望通过今天的文章&#xff0c;能够帮助大家更好地解决这…

宽压12-90V转5V3A降压IC,AH8691芯片

## 宽压12-90V转5V3A降压IC&#xff0c;多重保护功能全面升级 1. **宽压输入范围**&#xff1a;8V-100V&#xff0c;支持输出电压低至3.3V 2. **高效转换**&#xff1a;5A典型峰值开关电流&#xff0c;高达95%的转换效率 3. **多重保护**&#xff1a;包括过流、过热、输出短路…

Git本地库操作

对本地库的操作很少&#xff0c;我们学习1~6节即可&#xff0c;其他了解下。我们可以在idea中完成对本地库还有远程库的操作&#xff0c;可视化界面用起来更加舒适而且也不会混淆。 1. Git概述 Git 是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小…

ASM字节码操作类库(打开java语言世界通往字节码世界的大门) | 京东云技术团队

前言&#xff1a;授人以鱼不如授人以渔&#xff0c;应用asm的文章有很多&#xff0c;简单demo的也很多&#xff0c;那么ASM都具备哪些能力呢&#xff1f;如何去学习编写ASM代码呢&#xff1f;什么样的情景需要用到ASM呢&#xff1f;让我们带着这些问题阅读这篇文章吧。 这里由…

笔记本只使用Linux是什么体验?

笔记本只使用Linux是什么体验&#xff1f; 之后安了Windows双系统之后也不怎么想再进Windows了。 开发环境就不用说了&#xff0c;Linux下配各种开发环境都方便的多&#xff0c;当然你要用 vs 那还是乖乖回 Windows 吧。 最近很多小伙伴找我&#xff0c;说想要一些Linux的资…

Sleuth

Sleuth 一 引言 随着服务的越来越多&#xff0c;对调⽤链的分析会越来越复杂。它们之间的调⽤关系也许如下图&#xff1a; 问题&#xff1a; 1&#xff1a;微服务之间的调⽤错综复杂&#xff0c;⽤户发送的请求经历那些服务&#xff0c;调⽤链不清楚&#xff0c;没有⼀ 个⾃…

CSDN最新最全pytest系列——pytest-base-url插件之配置可选的项目系统UR

前言 ①当我们的自动化代码完成之后&#xff0c;通常期望可以在不同的环境进行测试&#xff0c;此时可以将项目系统的URL单独拿出来&#xff0c;并且可以通过pytest.ini配置文件和支持pytest命令行方式执行。 ② pytest-base-url 是一个简单的pytest插件&#xff0c;它通过命…

哈夫曼树你需要了解一下

哈夫曼树介绍哈夫曼数特点哈夫曼应用场景哈夫曼构建过程哈夫曼树示例拓展 哈夫曼树介绍 哈夫曼树&#xff08;Huffman Tree&#xff09;是一种特殊的二叉树&#xff0c;也被称为最优二叉树。在计算机科学中&#xff0c;它是由权值作为叶子节点构造出来的一种二叉树。哈夫曼树的…

计算机组成原理(万字爆肝整理)

第一章 计算机系统概述 “较简单&#xff0c;不做过多赘述&#xff0c;后面会详细学到” 第一节 计算机系统层次结构 1.计算机系统的基本组成&#xff1a;硬件软件 2.计算机硬件的基本组成&#xff1a;运算器存储器控制器输入设备输出设备 3.系统软件和应用软件 系统软件…

企业数字化转型所需的数据在哪里找?企业数据运营有什么用?

现阶段&#xff0c;越来越多企业考虑数字化转型。特别是中小型企业&#xff0c;他们察觉到&#xff1a;数字化转型的关键在于数据的运营。只有通过数据的有效管理和不断挖掘&#xff0c;企业才可以更好地了解市场需求&#xff0c;优化业务流程&#xff0c;提高决策效率&#xf…

浅谈Python装饰器原理与用法分析

前言 本文实例讲述了Python装饰器原理与用法。分享给大家供大家参考&#xff0c;具体如下&#xff1a; 1、装饰器的本质是函数&#xff0c;主要用来装饰其他函数&#xff0c;也就是为其他函数添加附加功能 2、装饰器的原则: (1) 装饰器不能修改被装饰的函数的源代码 (2) 装…

磐舟CI使用说明及案例

整体介绍 磐舟作为一个devops产品&#xff0c;它具备基础的CI流水线功能。同时磐舟的流水线是完全基于云原生架构设计的&#xff0c;在使用时会有一些注意事项。这里首先我们要了解磐舟整体的流水线打包逻辑。 文档结构说明 一般来说&#xff0c;磐舟推荐单个业务的标准git库…

面试官:什么是三色标记

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

sqlserver==索引解析,执行计划,索引大小

1创建测试表 -- 创建大型表 CREATE TABLE LargeTableWithIndex (ID int IDENTITY(1,1) PRIMARY KEY,IndexedColumn int,NonIndexedColumn nvarchar(255),OtherData nvarchar(255) );2插入测试数据 -- 使用 T-SQL 插入大量数据 DECLARE @i int = 1; WHILE @i <= 100000 -- …

优先经验回放(prioritized experience replay)

prioritized experience replay 思路 优先经验回放出自ICLR 2016的论文《prioritized experience replay》。 prioritized experience replay的作者们认为&#xff0c;按照一定的优先级来对经验回放池中的样本采样&#xff0c;相比于随机均匀的从经验回放池中采样的效率更高&…

第一百七十六回 如何创建渐变色边角

文章目录 1. 概念介绍2. 实现方法3. 代码与细节3.1 示例代码3.2 代码细节 4. 内容总结 我们在上一章回中介绍了"如何创建放射形状渐变背景"相关的内容&#xff0c;本章回中将介绍"如何创建渐变色边角".闲话休提&#xff0c;让我们一起Talk Flutter吧。 1.…

2023年【广东省安全员B证第四批(项目负责人)】报名考试及广东省安全员B证第四批(项目负责人)复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 广东省安全员B证第四批&#xff08;项目负责人&#xff09;报名考试是安全生产模拟考试一点通总题库中生成的一套广东省安全员B证第四批&#xff08;项目负责人&#xff09;复审考试&#xff0c;安全生产模拟考试一点…

智能安全帽作业记录仪赋能智慧工地人脸识别劳务实名制

需求背景 建筑工地是一个安全事故多发的场所。目前&#xff0c;工程建设规模不断扩大&#xff0c;工艺流程纷繁复杂&#xff0c;如何完善现场施工现场管理&#xff0c;控制事故发生频率&#xff0c;保障文明施工一直是施工企业、政府管理部门关注的焦点。尤其随着社会的不断进…

18.天气小案例

1►新增带Layout组件的页面 直接在views文件夹下面新增weather.vue。然后随便写一个123&#xff0c;现在先让我们页面能跳过去先。 让页面能跳过去&#xff0c;有好几种方法&#xff1a; 1、在菜单管理自己添加一个菜单&#xff0c;然后把菜单分配给某个角色&#xff0c;再把…

【Python进阶】近200页md文档14大体系第4篇:Python进程使用详解(图文演示)

本文从14大模块展示了python高级用的应用。分别有Linux命令&#xff0c;多任务编程、网络编程、Http协议和静态Web编程、htmlcss、JavaScript、jQuery、MySql数据库的各种用法、python的闭包和装饰器、mini-web框架、正则表达式等相关文章的详细讲述。 Python全套笔记直接地址…