计算机网络(3) --- 网络套接字TCP

news2024/11/18 3:23:37

计算机网络(2) --- 网络套接字UDP_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131977544?spm=1001.2014.3001.5501

目录

1.TCP

1.服务端接口介绍

1.listen状态

2.accept获取链接

2.客户端接口介绍

2.TCP的服务器和客户端接口实现

1.服务端

1.成员函数

2.接口

start()实现方式

1.单一执行流

2.多进程

3.多线程

4.线程池

3.main函数

2.客户端

1.成员函数

2.接口

3.mian函数

3.守护进程化

1.守护进程

2.代码


1.TCP

回顾一下UDP套接字,实现很简单,只需要初始化然后再send即可。但是TCP更加复杂

1.服务端接口介绍

1.listen状态

listen状态适用于获取先链接的,第二个参数不能填太大的int类型数据。

2.accept获取链接

 服务器只有先accept获取新链接(客户端传来的套接字),才能接受到套接字。

1.调用成功返回一个文件描述符, 失败返回-1。这个返回值其实对应的是一个套接字。

2.第一个sockfd其实是接收用的套接字,它不参与到具体的操作内;而返回值返回的套接字才是客户端返回的套接字,这个套接字是用于处理的。

3.由于tcp是面向字节流的,所以接受到的套接字就可以使用read和write进行操作了

4.如果该套接字使用完毕,一定要释放(close)套接字。因为文件描述符的本质是数组,而数组有一定的上限,我们不能只入而不释放,如果不释放会出现文件描述符泄漏。

2.客户端接口介绍

1.connect:发起链接

链接对应服务端的ip和port,表示自己要链接哪个服务端

2.TCP的服务器和客户端接口实现

1.服务端

namespace Server
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR,
        OPEN_ERR
    };

    static const uint16_t gport = 8080;
    static const int gbacklog = 5;

    class tcpServer
    {
    public:
        tcpServer(const uint16_t &port = gport)
            : _listensock(-1), _port(port)
        {
        }

        void initServer()
        {
            // 1.创建
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                logMessage(FATAL, "create listensock error");
                exit(SOCKET_ERR);
            }
            logMessage(NORMAL, "create listensock success");

            // 2.bind
            struct sockaddr_in local;
            memset(&local, 0, sizeof 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)
            {
                logMessage(FATAL, "bind error");
                exit(BIND_ERR);
            }
            logMessage(NORMAL, "bind listensock success");

            // 3.设置socket,为监听状态
            if (listen(_listensock, gbacklog))
            {
                logMessage(FATAL, "listen error");
                exit(LISTEN_ERR);
            }
            logMessage(NORMAL, "listen listensock success");
        }

        void start()
        {
            for (;;)
            {
                // 单线程版,只能有一个执行流,其他的无法进入执行,因为serviceIO死循环
                // 1.sever获取新链接
                struct sockaddr_in peer;
                socklen_t len = sizeof peer;
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next"); // accept失败不影响整体的代码,继续访问即可
                    continue;
                }
                logMessage(NORMAL, "accept a new link success");
                std::cout << "sock: " << sock << std::endl;

                // sock是面向字节流的,后续全部都是文件操作
                serviceIO(sock);
                close(sock); //不释放会出现文件描述符泄漏
            }
        }

        void serviceIO(int sock)
        {
            char buffer[1024];
            while (true)
            {
                ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = 0;
                    std::cout << "recv message: " << buffer << std::endl;

                    std::string outbuffer = buffer;
                    outbuffer += "server[echo]";

                    write(sock, outbuffer.c_str(), outbuffer.size());
                }
                else if (n == 0)
                {
                    // 代表client退出
                    logMessage(NORMAL, "client quit,me too");
                    break;
                }
            }
        }

        ~tcpServer()
        {
        }

    private:
        uint16_t _port;
        int _listensock; // 不是用来通信的,而是用于监听链接的
    };
}

1.成员函数

uint16_t _port:端口号

int _listensock:监听套接字

2.接口

1.initServer()创建套接字,先初始化监听套接字;生成对应的监听套接字;随后将当前的IP设置为任意IP,并且设置port最后绑定(bind)起来;随后还需要listen监听套接字

2.start()先定义新套接字,accept获取新套接字,执行套接字传输的任务。

start()实现方式

1.单一执行流
        void start()
        {
            for (;;)
            {
                // 1.sever获取新链接
                struct sockaddr_in peer;
                socklen_t len = sizeof peer;
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next"); // accept失败不影响整体的代码,继续访问即可
                    continue;
                }
                logMessage(NORMAL, "accept a new link success");
                std::cout << "sock: " << sock << std::endl;

                // sock是面向字节流的,后续全部都是文件操作
                serviceIO(sock);
                close(sock); //不释放会出现文件描述符泄漏
            }
        }

这样的缺点很明显:只有一个执行流操作服务端,也就意味着客户端只有一个能链接服务端。而且serviceIO()是一个死循环函数,那么只有客户端主动退出才能让其他客户端链接。这是串行的执行逻辑

2.多进程
1.信号版
        void start()
        {
            signal(SIGCHLD, SIG_IGN);
            for (;;)
            {
                // 1.sever获取新链接
                struct sockaddr_in peer;
                socklen_t len = sizeof peer;
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next"); // accept失败不影响整体的代码,继续访问即可
                    continue;
                }
                logMessage(NORMAL, "accept a new link success");
                std::cout << "sock: " << sock << std::endl;

                // 多进程
                pid_t id = fork();
                if (id == 0)
                {
                    // child
                    close(_listensock);
                    serviceIO(sock);
                    close(sock);
                    exit(0);
                }
                close(sock);
            }
        }

1.直接忽略子进程阻塞的状态,这样操作系统会自动回收

2.只要子进程自动回收,那么父进程就不需要等待,直接不断的按照需求新建子进程,而子进程执行结束不需要管理也不会有内存泄漏问题

3.在父进程处close(sock)的做法是为了不让文件描述符泄漏,子进程的close不影响父进程,如果父进程不弄,子进程把自己的套接字关闭不代表父进程的套接字也被关闭了

2.waitpid版
        void start()
        {
            for (;;)
            {
                // 1.sever获取新链接
                struct sockaddr_in peer;
                socklen_t len = sizeof peer;
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next"); // accept失败不影响整体的代码,继续访问即可
                    continue;
                }
                logMessage(NORMAL, "accept a new link success");
                std::cout << "sock: " << sock << std::endl;

                // 多进程
                pid_t id = fork();
                if (id == 0)
                {
                    // child
                    close(_listensock);
                    if (fork() > 0)
                        exit;

                    // 孙子进程变为孤儿进程
                    serviceIO(sock);
                    close(sock);
                    exit(0);
                }
                close(sock);
                // father
                pid_t ret = waitpid(id, nullptr, 0); //子进程进去直接死亡回收,不需要管理
                if (ret > 0)
                    std::cout << "wait success: " << ret << std::endl;
            }
        }

1.父进程先非阻塞等待

2.子进程中创建孙子进程,孙子进程执行接收的文件,子进程直接退出,让父进程回收。此时孙子进程变成孤儿进程托付给操作系统,操作系统管理孙子进程的结束。

多进程展现的问题就是消费太多资源,本来一个执行流能解决的事情却创造了调度执行流的基本单位进程来实现,而且拷贝需要时间,成本过大。

3.多线程
    class ThreadData
    {
    public:
        ThreadData(tcpServer *self, int sock)
            : _self(self), _sock(sock)
        {
        }

    public:
        tcpServer *_self;
        int _sock;
    };

        void start()
        {
            for (;;)
            {
                // 1.sever获取新链接
                struct sockaddr_in peer;
                socklen_t len = sizeof peer;
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next"); // accept失败不影响整体的代码,继续访问即可
                    continue;
                }
                logMessage(NORMAL, "accept a new link success");
                std::cout << "sock: " << sock << std::endl;

                // 多线程
                pthread_t tid;
                ThreadData *td = new ThreadData(this, sock);
                pthread_create(&tid, nullptr, threadRoutine, td);
            }
        }

        static void *threadRoutine(void *args)
        {
            pthread_detach(pthread_self());
            ThreadData *td = static_cast<ThreadData *>(args);

            td->_self->serviceIO(td->_sock);
            delete td;
            close(td->_sock);
            return nullptr;
        }

1.创造线程,让子线程进行接收执行。当然为了不需要串行执行,在threadRoutine()中,优先将当前线程进行线程分离。这样主线程就不需要回收子线程,子线程结束操作系统就会自动回收。

2.执行后需要将当前的文件描述符回收,由于函数是线程的公共资源,所以threadRoutine内就能影响到。

4.线程池
//线程池
using namespace ThreadNs;

const int gnum = 10;

template <class T>
class ThreadPool;

template <class T>
class ThreadData
{
public:
    ThreadData(ThreadPool<T> *tp, const std::string &n)
        : threadpool(tp), name(n)
    {
    }

public:
    ThreadPool<T> *threadpool;
    std::string name;
};

template <class T>
class ThreadPool
{
private:
    static void *handlerTask(void *args)
    {
        ThreadData<T> *td = static_cast<ThreadData<T> *>(args);
        while (true)
        {
            T t;
            {
                // td->threadpool->lockQueue();
                LockGuard lockguard(td->threadpool->mutex());
                while (td->threadpool->isQueueEmpty())
                {
                    td->threadpool->threadWait();
                }
                t = td->threadpool->pop();
            }
            // td->threadpool->unlockQueue();
            std::cout << td->name << "处理完了任务: " << t.toTaskString() << "并处理完成,结果是: " << t();
        }
        return nullptr;
    }

    ThreadPool(const int &_num = gnum)
        : _num(gnum)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i < _num; i++)
        {
            _threads.push_back(new Thread());
        }
    }

    void operator=(const ThreadPool &) = delete;

    ThreadPool(const ThreadPool &) = delete;

public:
    static ThreadPool<T> *getInstance()
    {
        if (_tp == nullptr)
        {
            _singleton_lock.lock();
            if (_tp == nullptr)
            {
                _tp = new ThreadPool<Task>();
            }
            _singleton_lock.unlock();
        }
        return _tp;
    }

    // void lockQueue()
    // {
    //     pthread_mutex_lock(&_mutex);
    // }

    // void unlockQueue()
    // {
    //     pthread_mutex_unlock(&_mutex);
    // }

    bool isQueueEmpty()
    {
        return _task_queue.empty();
    }

    void threadWait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    T pop()
    {
        T t = _task_queue.front();
        _task_queue.pop();
        return t;
    }

    pthread_mutex_t *mutex()
    {
        return &_mutex;
    }

public:
    void run()
    {
        for (const auto &t : _threads)
        {
            ThreadData<T> *td = new ThreadData<T>(this, t->threadname());

            t->start(handlerTask, td);
            std::cout << t->threadname() << " start ..." << std::endl;
        }
    }

    void push(const T &in)
    {
        // pthread_mutex_lock(&_mutex);
        LockGuard lockguard(&_mutex);
        _task_queue.push(in);
        pthread_cond_signal(&_cond);
        // pthread_mutex_unlock(&_mutex);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        for (const auto &t : _threads)
            delete t;
    }

private:
    int _num;
    std::vector<Thread *> _threads;
    std::queue<T> _task_queue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static ThreadPool<T> *_tp;
    static std::mutex _singleton_lock;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;
static std::mutex _singleton_lock;
/

        void start()
        {
            // 线程池初始化
            ThreadPool<Task>::getInstance()->run();
            for (;;)
            {
                // 1.sever获取新链接
                struct sockaddr_in peer;
                socklen_t len = sizeof peer;
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next"); // accept失败不影响整体的代码,继续访问即可
                    continue;
                }
                logMessage(NORMAL, "accept a new link success");
                std::cout << "sock: " << sock << std::endl;

                // 4.线程池
                ThreadPool<Task>::getInstance()->push(Task(sock, serviceIO));
            }
        }

3.main函数

using namespace std;
using namespace Server;

static void Usage(string proc)
{
    cout << "Usage:\n\t" << proc << " local_ip local_port\n\n";
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    std::unique_ptr<tcpServer> tsvr(new tcpServer(port));
    tsvr->initServer();
    tsvr->start();
    return 0;
}

1. 一旦启动,就会进入LISTEN状态

2.客户端

#define NUM 1024

namespace Client
{
    class tcpClient
    {
    public:
        tcpClient(const std::string &serverip, const uint16_t &serverport)
            : _serverip(serverip), _serverport(serverport), _sock(-1)
        {
        }

        void initClient()
        {
            // 1.创建socket
            _sock = socket(AF_INET, SOCK_STREAM, 0);
            if (_sock < 0)
            {
                std::cerr << "socket error: " << errno << " : " << strerror(errno) << std::endl;
                exit(2);
            }
            // 2.client要不要bind[必须要的],client要不要显示的bind,不需要
            // 3.客户端不需要listen
            // 4.也不需要accept
        }

        void start()
        {
            // 5.要发起链接
            struct sockaddr_in server;
            memset(&server, 0, sizeof server);
            server.sin_family = AF_INET;
            server.sin_port = htons(_serverport);
            server.sin_addr.s_addr = inet_addr(_serverip.c_str());

            if (connect(_sock, (struct sockaddr *)&server, sizeof server) != 0)
            {
                std::cerr << "connect error: " << errno << " : " << strerror(errno) << std::endl;
            }
            else
            {
                std::string msg;
                while (true)
                {
                    std::cout << "Enter# ";
                    std::getline(cin, msg);
                    write(_sock, msg.c_str(), msg.size());

                    char buffer[NUM];
                    int n = read(_sock, buffer, sizeof(buffer) - 1);
                    if (n > 0)
                    {
                        buffer[n] = 0;
                        std::cout << "server回显的信息: " << buffer << std::endl;
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }

        ~tcpClient()
        {
            if (_sock >= 0)
                close(_sock);
        }

    private:
        int _sock;
        std::string _serverip;
        uint16_t _serverport;
    };
}

1.成员函数

int _sock:套接字
std::string _serverip:server的IP地址
uint16_t _serverport:server发PORT

2.接口

3.mian函数

static void Usage(string proc)
{
    cout << "Usage:\n\t" << proc << " server_ip server_port\n\n";
}

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

    uint16_t serverport = atoi(argv[2]);
    std::string serverip = argv[1];
    std::unique_ptr<tcpClient> tcli(new tcpClient(serverip, serverport));
    tcli->initClient();
    tcli->start();

    return 0;
}

在同一个服务器运行客户端和服务端

1.ESTABLISHED:指的是客户端被服务端所接收了

2.tcp的Server查到的是服务器的链接

3.tcp的Client查到的是客户端的链接

4.两端都是全双工的

3.守护进程化

1.守护进程

1.xshell在操作系统下会生成一个会话,此时bash充当前台任务,其他都是后台任务

2.前台任务有且只能有一个,而后台任务能允许有多个

3.所有的作业可以前后台转换。先fg转换,再ctrl+Z切换后台暂停,最后bg使得暂停任务重新开始

4.这些任务可能受到用户的登录和注销的影响的,我们需要将作业自称独立会话,和终端设备无关,这样的进程为守护进程

1.setsid:将非组员的进程独立成一个会话,并且该进程变成此会话的组长

2.不能是原先就是组长的进程

/dev/null:黑洞文件,信息重定向到该文件默认数据全部清空丢弃。

2.代码

#define DEV "/dev/null"

void daemonself(const char *currPath = nullptr)
{
    // 1.让调用进程忽略异常信号
    signal(SIGPIPE, SIG_IGN);

    // 2.如何让自己不是组长,setsid
    if (fork() > 0)
        exit(0);
    // 守护进程 -- 精灵进程,孤儿进程的一种
    pid_t n = setsid();
    assert(n != -1);

    // 3.守护进程脱离终端,关闭或者重定向以前进程默认打开文件 012
    int fd = open(DEV, O_RDWR);
    if (fd >= 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);

        close(fd);
    }
    else
    {
        close(0);
        close(1);
        close(2);
    }

    // 4.可选:进程执行路径发生变化
    if (currPath)
        chdir(currPath);
}

1.让调用进程忽略异常信号:不出现进程错误的问题使得守护过程终止

2.让自己不是组长(setsid):守护化的条件就是进程不能是会话的组长,要想不是,上面的处理方式是fork一个子进程,当前进程直接释放,使得fork的子进程变成孤儿进程,孤儿进程托付给操作系统,此时的组长是bash,那么就可以进行setsid了

3.守护进程脱离终端,关闭或者重定向以前进程默认打开文件 012:不直接关闭,将/dev/null这个黑洞文件重定向到012中。

4.可选:进程执行路径发生变化。将当前进程执行的路径换掉。

5.此时服务端的main函数逻辑为:

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    std::unique_ptr<tcpServer> tsvr(new tcpServer(port));
    tsvr->initServer();

    // 守护进程化
    daemonself();
    tsvr->start();
    return 0;
}

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

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

相关文章

力扣:48. 旋转图像(Python3)

题目&#xff1a; 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&…

基于ANACONDA安装用于Python编程的Spyder集成开发环境的方法步骤详解

基于ANACONDA安装用于Python编程的Spyder集成开发环境的方法步骤详解 Python作为一种当下流行的编程语言&#xff0c;其编辑器有很多种&#xff0c;本文介绍基于ANACONDA的安装Spyder编辑器的方法步骤。Spyder集成开发环境&#xff0c;和其他的Python开发环境相比&#xff0c;…

django bootstrap html实现左右布局,带折叠按钮,左侧可折叠隐藏

一、实现的效果 在django项目中,需要使用bootstrap 实现一个左右分布的布局,左侧区域可以折叠隐藏起来,使得右侧的显示区域变大。(为了区分区域,左右加了配色,不好看的修改颜色即可) 点击折叠按钮,左侧区域隐藏,右侧区域铺满: 二、实现思路 1、使用col-md属性,让左…

JavaEE初阶之文件操作 —— IO

目录 一、认识文件 1.1认识文件 1.2树型结构组织 和 目录 1.3文件路径(Path) 1.4其他知识 二、Java 中操作文件 2.1File 概述 2.2代码示例 三、文件内容的读写 —— 数据流 3.1InputStream 概述 ​3.2FileInputStream 概述 3.3代码示例 3.4利用 Scanner 进行字…

procreate怎么插入图片?如何将图片插入到procreate图层里?

Procreate是一款运行在iPadOS上的强大的绘画应用软件&#xff0c;让创意人士随时把握灵感&#xff0c;通过简易的操作系统&#xff0c;专业的功能集合进行素描、填色、设计等艺术创作。procreate怎么插入图片&#xff1f;如何将图片插入到procreate图层里&#xff1f; 打开平板…

【二分+滑动窗口优化DP】CF883 I

Problem - 883I - Codeforces 题意&#xff1a; 思路&#xff1a; 首先&#xff0c;要让最大值最小&#xff0c;很显然要二分 那么就相当于有了一个极差的限制&#xff0c;看能不能分组&#xff0c;每组至少m个元素 那么就是考虑分段DP&#xff0c;直接n^2很容易写 但是n …

偶发odbc.ini文件被清空

现象&#xff1a;某个现场 odbc.ini 莫名其妙被清空&#xff0c;偶发情况&#xff0c;大概几周出现一次。 unixodbc官网地址–> https://www.unixodbc.org/ //2.3.7 BOOL _odbcinst_SystemINI( char *pszFileName, BOOL bVerify ) {FILE *hFile;char …

这个Python项目让古诗变得更易读,看完《长安三万里》惊艳了!

大家好&#xff0c;这里是程序员晚枫&#xff0c;最近在小红薯更新生活VLOG&#xff0c;被骂惨了&#xff01; 周末和小明去看了一场电影&#xff1a;《长安三万里》。 看电影的过程中发生了一件尴尬的事情&#xff1a;很多诗词里的字我都不认识。 回家以后&#xff0c;我赶…

机器学习深度学习——非NVIDIA显卡怎么做深度学习(坑点排查)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——数值稳定性和模型化参数&#xff08;详细数学推导&#xff09; &#x1f4da;订阅专栏&#xff1a;机器…

systemverilog中fork..join, join_any, join_none的用法和解析

1、fork.. join, join_any以及join_none的用法进行总结 1.1、 fork..join fork..join: 必须等到statement1&#xff0c;statement2&#xff0c;statement3全部执行完之后&#xff0c;statement4才可以执行。fork…join内的所有语句都是并发执行&#xff08;对于begin…end内…

浪涌保护器10/350us和8/20us是什么意思,两者有何区别?

在选择浪涌保护器的时候&#xff0c;浪涌保护器的防雷能力放电电流后面的单位一般是KA&#xff0c;KA后面还有个参数是10/350us或者8/20us&#xff0c;大家可能对这2个参数不太熟悉。 那么&#xff0c;浪涌保护器10/350us和8/20us是什么意思&#xff0c;两者有何区别呢&#x…

025 - max()函数

MAX() 函数: MAX 函数返回一列中的最大值。NULL 值不包括在计算中。 SQL MAX() 语法: SELECT MAX(column_name) FROM table_name; 注释&#xff1a;MAX 也可用于文本列&#xff0c;以获得按字母顺序排列的最高或最低值。 -- 实际操作&#xff08;查询salary的最大值&#x…

【十万个等保小知识】等保测评报告是在等保整改之后发吗?

等保2.0政策已经严格落地执行了一段时间&#xff0c;但大家对于等保2.0政策还存在很多疑问&#xff0c;例如等保测评报告是在等保整改之后发吗&#xff1f;今天我们小编就来为大家简单回答一下&#xff0c;仅供参考哦&#xff01; 等保测评报告是在等保整改之后发吗&#xff1f…

uniapp echarts 点击失效

这个问题网上搜了一堆&#xff0c;有的让你降版本&#xff0c;有的让你改源码。。。都不太符合预期&#xff0c;目前我的方法可以用最新的echarts。 这个方法就是由npm安装转为CDN&#xff0c;当然你可能会质疑用CDN这样会不稳定&#xff0c;那如果CDN的地址是本地呢&#xff1…

【数据库】将excel数据导入mysql数据库

环境&#xff1a;Windows10 mysql8以上 将你要导入的excel表另存为txt格式 打开txt格式文件&#xff0c;删除表头行并另存为并更改编码方式&#xff08;由于与数据库的编码不同&#xff0c;会导致导入报错&#xff09; 通过命令行登录数据库 winr cmd进入 进入装mysql的目录位…

css中的bfc是什么?

什么bfc&#xff1f; BFC&#xff08;Block Formatting Context&#xff09;块级 格式化 上下文。 BFC就是页面上的一个隔离的独立盒子&#xff0c;容器里面的子元素和外面的元素不会相互影响。 为什么要bfc? bfc是我们去主动触发的,并不是自动就存在的,它是帮助我们解决cs…

Zabbix报警机制、配置钉钉机器人、自动发现、主动监控概述、配置主动监控、zabbix拓扑图、nginx监控实例

day02 day02配置告警用户数超过50&#xff0c;发送告警邮件实施验证告警配置配置钉钉机器人告警创建钉钉机器人编写脚本并测试添加报警媒介类型为用户添加报警媒介创建触发器创建动作验证自动发现配置自动发现主动监控配置web2使用主动监控修改配置文件&#xff0c;只使用主动…

修改el-select或者el-input样式失效

下午改el-input和el-select这两个的样式真的烦&#xff0c;&#xff0c;&#xff0c;还不如写原生标签了。。 样式使用的是sass 我已经在样式器中挨着挨着去找了&#xff0c;把层级的类都写下来了 .select-wraper{//下拉框.el-select{.el-input .el-input__wrapper{backgrou…

复习之vsftp服务

一、vsftp服务简介 文件传输协议&#xff08;File Transfer Protocol&#xff0c;FTP&#xff09;是用于在网络上进行文件传输的一套标准协议&#xff0c;它工作在 OSI 模型的第七层 即应用层&#xff0c; 使用 TCP 传输而不是 UDP&#xff0c; 客户在和服务器建立连接前要经过…

浅说React-Dnd的使用

前言(学习原因) 近期在工作中遇到一个新的需求&#xff1a;具体大概效果看下面.gif 当时看到这个需求经师兄提醒知道了React-Dnd&#xff0c;想到了表格 Table - Ant Design里有一个可拖拽表格的效果&#xff0c;照搬到了项目里发现不太能满足我们的需求&#xff0c;就去搜了一…