【网络编程】UDP简单实现翻译软件与网络聊天室

news2024/11/27 1:26:03

文章目录

  • 一、引入
  • 二、翻译软件实现
    • 2.1 加载字典
    • 2.2 处理数据并传递给用户端
    • 2.3 客户端获取结果
    • 2.4 结果
    • 2.5 执行命名功能
  • 三、网络聊天室实现
    • 3.1 管理用户
    • 3.2 发送消息
    • 3.3 多线程处理
    • 3.4 结果
  • 四、源码

一、引入

在上一章【网络编程】demo版UDP网络服务器实现实现了客户端和服务端之间的数据的发送与接收,上一章我们是直接让服务端把接收到的数据打印出来。在这里插入图片描述
但是服务端并不是只接收到数据就完了,它还要处理任务
所以我们可以在服务端设置一个回调函数:

typedef std::function<void(std::string, uint16_t, std::string)> func_t;

用来处理接收到的信息。

二、翻译软件实现

2.1 加载字典

要实现客户端发送一个单词,接收到服务端翻译的结果。
那么首先需要把英语和汉语加载进unordered_map词典中。

static std::unordered_map<std::string, std::string> Dict;

从文件中把数据加载进词典中:
在这里插入图片描述

const std::string path = "./Dict.txt";

static std::unordered_map<std::string, std::string> Dict;

// 分割字符串
static bool cutstring(std::string s, std::string *key, std::string *val)
{
    size_t pos = s.find(":");
    if(pos == std::string::npos)
    {
        return false;
    }
    *key = s.substr(0, pos);
    *val = s.substr(pos + 1);
    return true;
}

static void InitDict()
{
    std::ifstream ifs(path);
    if(!ifs.is_open())// 打开失败
    {
        std::cout << "ifstream open fail" << std::endl;
        exit(1);
    }
    std::string sline;
    std::string key, val;
    while(getline(ifs, sline))// 按行读取
    {
        if(cutstring(sline, &key, &val))
        {
            Dict.insert({key, val});
        }
        else
        {
            std::cout << "cutstring fail" << std::endl;
            exit(1);
        }
    }
    ifs.close();
    std::cout << "Dict Load Success\n";
}

2.2 处理数据并传递给用户端

接下来处理数据并且要把处理的结果反馈给用户端。

处理数据比较简单:

std::string res;
auto it = Dict.find(msg);
if(it == Dict.end())
{
    res = "not find";
}
else
{
    res = it->second;
}

接下来要把res发送给客户端,经过前面的学习sendto需要构建一个结构体表示要传给谁。
构建完结构体使用sendto的时候发现要有文件描述符,所以调用回调函数的时候要把_sockfd传递进去。

// 回调方法
void handler(int _sockfd, std::string ip, uint16_t port, std::string msg)
{
    std::string res;
    auto it = Dict.find(msg);
    if(it == Dict.end())
    {
        res = "not find";
    }
    else
    {
        res = it->second;
    }
    struct sockaddr_in si;
    bzero(&si, sizeof si);
    si.sin_family = AF_INET;
    si.sin_port = htons(port);
    si.sin_addr.s_addr = inet_addr(ip.c_str());
    sendto(_sockfd, res.c_str(), res.size(), 0, (struct sockaddr*)&si, sizeof si);
}

2.3 客户端获取结果

// 获取结果
char buf[1024];
sockaddr_in tmp;
socklen_t tmplen = sizeof tmp;
size_t n = recvfrom(_sockfd, buf, sizeof buf - 1, 0, (struct sockaddr*)&tmp, &tmplen);
if(n > 0) buf[n] = 0;
std::cout << "翻译结果:" << buf << "\n";

2.4 结果

客户端:
在这里插入图片描述
服务端:
在这里插入图片描述

2.5 执行命名功能

现在我们可以做一个小小的改动,不让服务端做翻译了,而是处理任务,例如我们发送pwd,他要返回服务端此时的路径。
这里其他的逻辑都不用变,只需要修改回调函数即可。

介绍一个命令行解析接口
popen

#include <stdio.h>

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

先回忆一下之前手写的建议shell【linux】进程控制详述,我们首先要命令行分割,把每个选项都提取出来,然后fork创建子进程再程序替换。
而这个函数包含了上述的所有功能,并且还多了一个创建管道的功能。
参数介绍:

command:传递进来的字符串,比如 ls -a -l
type:以什么方式打开文件(r/w/a)
比方说现在以r只读方式打开,就直接从管道中提取出结果。

void execcommand(int _sockfd, std::string ip, uint16_t port, std::string msg)
{
    std::string res;
    FILE* fp = popen(msg.c_str(), "r");
    if(fp == nullptr) res = msg + " execute fail";
    else
    {
        char line[1024];
        while(fgets(line, sizeof line, fp))
        {
            res += line;
        }
    }
    pclose(fp);
    struct sockaddr_in si;
    bzero(&si, sizeof si);
    si.sin_family = AF_INET;
    si.sin_port = htons(port);
    si.sin_addr.s_addr = inet_addr(ip.c_str());
    sendto(_sockfd, res.c_str(), res.size(), 0, (struct sockaddr*)&si, sizeof si);
}

客户端:
在这里插入图片描述
服务端:
在这里插入图片描述

三、网络聊天室实现

聊天室是什么远离呢?我们发送消息会进过服务端的转发,让每个在线的客户端都能看到发送的消息,这样就实现了群聊。

每个客户端发送一个online表示上线了,服务端就会把所有的数据都发送给上线的客户端。

所以首先我们先要把所有的用户管理起来。

3.1 管理用户

对于每个用户我们用IP和port来标识唯一性。

class User 
{
public:
    User(const std::string& ip, const uint16_t& port)
        : _ip(ip)
        , _port(port)
    {}
public:
    std::string _ip;
    uint16_t _port;
};

对于每个用户我们可以用哈希表来管理:

class OnlineUsers
{
public:
    void adduser(const std::string& ip, const uint16_t& port)
    {
        std::string id = ip + "@" + std::to_string(port);
        users.insert({id, User(ip, port)});
    }

    void deluser(const std::string& ip, const uint16_t& port)
    {
        std::string id = ip + "@" + std::to_string(port);
        users.erase(id);
    }

    bool isonline(const std::string& ip, const uint16_t& port)
    {
        std::string id = ip + "@" + std::to_string(port);
        return users.find(id) != users.end();
    }
private:
    std::unordered_map<std::string, User> users;
};

在回调函数中,如果收到的消息是online,就把用户添加进哈希表。如果是offline,就从哈希表中删除。

if(msg == "online") onlinemap.adduser(ip, port);
if(msg == "offline") onlinemap.deluser(ip, port);

3.2 发送消息

我们要发送消息其实就是给哈希表中所有的用户都sendto消息。在这之前要先判断是否在线

void groupmsg(int _sockfd, std::string ip, uint16_t port, std::string msg)
{
    if(msg == "online") onlinemap.adduser(ip, port);
    if(!onlinemap.isonline(ip, port))// 不在线
    {
        struct sockaddr_in si;
        bzero(&si, sizeof si);
        si.sin_family = AF_INET;
        si.sin_port = htons(port);
        si.sin_addr.s_addr = inet_addr(ip.c_str());
        std::string res = "你还没有上线,请先上线";
        sendto(_sockfd, res.c_str(), res.size(), 0, (struct sockaddr*)&si, sizeof si);
        return;
    }
    // 广播消息
}

现在要广播消息,那么就在OnlineUsers类中增加一个广播消息的成员函数。
它的参数要包含_sockfd, msg,还要有是谁发送的(ip, port)。

void broadmsg(int _sockfd, const std::string& msg, std::string ip, uint16_t port)
{
    for(auto& e : users)
    {
        struct sockaddr_in si;
        bzero(&si, sizeof si);
        si.sin_family = AF_INET;
        si.sin_port = htons(e.second._port);
        si.sin_addr.s_addr = inet_addr(e.second._ip.c_str());
        std::string res = ip + "@" + std::to_string(port) + "# ";
        res += msg;
        sendto(_sockfd, res.c_str(), res.size(), 0, (struct sockaddr*)&si, sizeof si);
    }
}

3.3 多线程处理

因为客户端不能立即收到消息打印出来(阻塞停留在接收消息),为了解决这个问题我们可以使用多线程一个线程专门接收消息,一个线程专门发送消息。

那么我们可以让主线程负责发送消息,子线程负责接收消息。

static void* getmsg(void *args)
{
    pthread_detach(pthread_self());
    int sockfd = *(static_cast<int*>(args));
    while(1)
    {
        char buf[1024];
        sockaddr_in tmp;
        socklen_t tmplen = sizeof tmp;
        size_t n = recvfrom(sockfd, buf, sizeof buf - 1, 0, (struct sockaddr*)&tmp, &tmplen);
        if(n > 0) buf[n] = 0;
        std::cout << buf << "\n";
    }
}

void start()
{
    pthread_create(&_get, nullptr, getmsg, (void*)&_sockfd);
    struct sockaddr_in si;
    bzero(&si, sizeof(si));
    si.sin_family = AF_INET;
    si.sin_addr.s_addr = inet_addr(_serverip.c_str());
    si.sin_port = htons(_serverport);
    std::string msg;
    while(1)
    {
        std::cout << "Please input: ";
        std::cin >> msg;
        sendto(_sockfd, msg.c_str(), msg.size(), 0, (struct sockaddr*)&si, sizeof si);
    }
}

3.4 结果

服务端:
在这里插入图片描述
客户端:
在这里插入图片描述
这里因为只有一个终端所以打印的比较混乱,但是能看到现象就行,如果想要优化就可以把输出的数据重定向到管道文件中,再打开一个终端读取管道,这样就可以实现发送和获取两个窗口。

四、源码

//UDPClient.hpp
#pragma once 
#include <iostream>
#include <string>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <strings.h>
#include <netinet/in.h>
#include <string.h>
#include <cassert>
#include <pthread.h>

class UDPClient
{
public:
    UDPClient(const std::string& serverip, const uint16_t& port)
        : _serverip(serverip)
        , _serverport(port)
        , _sockfd(-1)
    {}

    void InitClient()
    {
        // 创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd == -1)
        {
            std::cerr << "socket error" << errno << " : " << strerror(errno) << std::endl;
            exit(1);
        }
    }

    static void* getmsg(void *args)
    {
        pthread_detach(pthread_self());
        int sockfd = *(static_cast<int*>(args));
        while(1)
        {
            char buf[1024];
            sockaddr_in tmp;
            socklen_t tmplen = sizeof tmp;
            size_t n = recvfrom(sockfd, buf, sizeof buf - 1, 0, (struct sockaddr*)&tmp, &tmplen);
            if(n > 0) buf[n] = 0;
            std::cout << buf << "\n";
        }
    }

    void start()
    {
        pthread_create(&_get, nullptr, getmsg, (void*)&_sockfd);
        struct sockaddr_in si;
        bzero(&si, sizeof(si));
        si.sin_family = AF_INET;
        si.sin_addr.s_addr = inet_addr(_serverip.c_str());
        si.sin_port = htons(_serverport);
        std::string msg;
        while(1)
        {
            std::cout << "Please input: ";
            std::cin >> msg;
            sendto(_sockfd, msg.c_str(), msg.size(), 0, (struct sockaddr*)&si, sizeof si);
        }
    }
private:
    uint16_t _serverport;
    std::string _serverip;
    int _sockfd;
    pthread_t _get;
};

// UDPClient.cc
#include "UDPClient.hpp"
#include <memory>

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        std::cout << "incorrect number of parameters" << std::endl;
        exit(1);
    }
    std::string ip = argv[1];
    uint16_t port = atoi(argv[2]);
    std::unique_ptr<UDPClient> ptr(new UDPClient(ip, port));
    ptr->InitClient();
    ptr->start();
    return 0;
}

// UDPServer.hpp
#pragma once 
#include <iostream>
#include <string>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <strings.h>
#include <netinet/in.h>
#include <string.h>
#include <cassert>
#include <functional>

static const std::string defaultip = "0.0.0.0";// 默认IP

typedef std::function<void(int, std::string, uint16_t, std::string)> func_t;

class UDPServer
{
public:
    UDPServer(const func_t& func, const uint16_t& port, const std::string ip = defaultip)
        : _port(port)
        , _ip(ip)
        , _sockfd(-1)
        , _func(func)
    {}

    void InitServer()
    {
        // 创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd == -1)
        {
            std::cerr << "socket error" << errno << " : " << strerror(errno) << std::endl;
            exit(1);
        }
        // 绑定IP与port
        struct sockaddr_in si;
        bzero(&si, sizeof si);
        si.sin_family = AF_INET;// 协议家族
        si.sin_port = htons(_port);// 端口号,注意大小端问题
        // si.sin_addr.s_addr = inet_addr(_ip.c_str());// ip
        si.sin_addr.s_addr = INADDR_ANY;
        // 绑定
        int n = bind(_sockfd, (struct sockaddr*)&si, sizeof si);
        assert(n != -1);
    }

    void start()
    {
        char buf[1024];
        while(1)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof peer;
            ssize_t s = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&peer, &len);
            if(s > 0)
            {
                buf[s] = 0;// 结尾
                std::string cip = inet_ntoa(peer.sin_addr);
                uint16_t cport = ntohs(peer.sin_port);
                std::string msg = buf;
                //std::cout << "[" << cip << "@" << cport << "]# " << msg << std::endl;
                _func(_sockfd, cip, cport, msg);
            }
        }
    }
private:
    uint16_t _port;
    std::string _ip;
    int _sockfd;
    func_t _func;
};


// UDPServer.cc
#include "UDPServer.hpp"
#include "users.hpp"
#include <memory>
#include <cstdio>
#include <unordered_map>
#include <fstream>

const std::string path = "./Dict.txt";

static std::unordered_map<std::string, std::string> Dict;

// 分割字符串
static bool cutstring(std::string s, std::string *key, std::string *val)
{
    size_t pos = s.find(":");
    if(pos == std::string::npos)
    {
        return false;
    }
    *key = s.substr(0, pos);
    *val = s.substr(pos + 1);
    return true;
}

static void InitDict()
{
    std::ifstream ifs(path);
    if(!ifs.is_open())// 打开失败
    {
        std::cout << "ifstream open fail" << std::endl;
        exit(1);
    }
    std::string sline;
    std::string key, val;
    while(getline(ifs, sline))// 按行读取
    {
        if(cutstring(sline, &key, &val))
        {
            Dict.insert({key, val});
        }
        else
        {
            std::cout << "cutstring fail" << std::endl;
            exit(1);
        }
    }
    ifs.close();
    std::cout << "Dict Load Success\n";
}

// 回调方法
void handler(int _sockfd, std::string ip, uint16_t port, std::string msg)
{
    std::string res;
    auto it = Dict.find(msg);
    if(it == Dict.end())
    {
        res = "not find";
    }
    else
    {
        res = it->second;
    }
    struct sockaddr_in si;
    bzero(&si, sizeof si);
    si.sin_family = AF_INET;
    si.sin_port = htons(port);
    si.sin_addr.s_addr = inet_addr(ip.c_str());
    sendto(_sockfd, res.c_str(), res.size(), 0, (struct sockaddr*)&si, sizeof si);
}

void execcommand(int _sockfd, std::string ip, uint16_t port, std::string msg)
{
    std::string res;
    FILE* fp = popen(msg.c_str(), "r");
    if(fp == nullptr) res = msg + " execute fail";
    else
    {
        char line[1024];
        while(fgets(line, sizeof line, fp))
        {
            res += line;
        }
    }
    pclose(fp);
    struct sockaddr_in si;
    bzero(&si, sizeof si);
    si.sin_family = AF_INET;
    si.sin_port = htons(port);
    si.sin_addr.s_addr = inet_addr(ip.c_str());
    sendto(_sockfd, res.c_str(), res.size(), 0, (struct sockaddr*)&si, sizeof si);
}

OnlineUsers onlinemap;

void groupmsg(int _sockfd, std::string ip, uint16_t port, std::string msg)
{
    if(msg == "online") onlinemap.adduser(ip, port);
    if(msg == "offline") onlinemap.deluser(ip, port);
    if(!onlinemap.isonline(ip, port))// 不在线
    {
        struct sockaddr_in si;
        bzero(&si, sizeof si);
        si.sin_family = AF_INET;
        si.sin_port = htons(port);
        si.sin_addr.s_addr = inet_addr(ip.c_str());
        std::string res = "你还没有上线,请先上线";
        sendto(_sockfd, res.c_str(), res.size(), 0, (struct sockaddr*)&si, sizeof si);
        return;
    }
    // 广播消息
    onlinemap.broadmsg(_sockfd, msg, ip, port);
}


int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        std::cout << "incorrect number of parameters" << std::endl;
        exit(1);
    }
    //InitDict();
    uint16_t port = atoi(argv[1]);
    std::unique_ptr<UDPServer> ptr(new UDPServer(groupmsg, port));
    ptr->InitServer();
    ptr->start();
    return 0;
}

// users.hpp
#pragma once

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

class User 
{
public:
    User(const std::string& ip, const uint16_t& port)
        : _ip(ip)
        , _port(port)
    {}
public:
    std::string _ip;
    uint16_t _port;
};

class OnlineUsers
{
public:
    void adduser(const std::string& ip, const uint16_t& port)
    {
        std::string id = ip + "@" + std::to_string(port);
        users.insert({id, User(ip, port)});
    }

    void deluser(const std::string& ip, const uint16_t& port)
    {
        std::string id = ip + "@" + std::to_string(port);
        users.erase(id);
    }

    bool isonline(const std::string& ip, const uint16_t& port)
    {
        std::string id = ip + "@" + std::to_string(port);
        return users.find(id) != users.end();
    }

    void broadmsg(int _sockfd, const std::string& msg, std::string ip, uint16_t port)
    {
        for(auto& e : users)
        {
            struct sockaddr_in si;
            bzero(&si, sizeof si);
            si.sin_family = AF_INET;
            si.sin_port = htons(e.second._port);
            si.sin_addr.s_addr = inet_addr(e.second._ip.c_str());
            std::string res = ip + "@" + std::to_string(port) + "# ";
            res += msg;
            sendto(_sockfd, res.c_str(), res.size(), 0, (struct sockaddr*)&si, sizeof si);
        }
    }
private:
    std::unordered_map<std::string, User> users;
};


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

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

相关文章

(1分钟了解)视觉惯性导航初始化方法综述

视觉惯性导航初始化方法综述 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 初始化相关的简介&#xff0c;在这里知道初始化方法可以分为联合初始化、非联合初始化和半联合初始化三种方法即可。 ​ 编辑切换为居中 添加图片注释&…

VIM学习笔记 正则表达式-(vimgrep/grep)

在UNIX问世的前一年&#xff0c;1969年&#xff0c;Ken Thompson将正则表达式整合入QED文本编辑器。在Linux文本编辑器ed中&#xff0c;如果你希望显示包含字母“re”的行时&#xff0c;需要使用命令g/re/p&#xff0c;而grep也因此得名。可以看作此操作的缩写&#xff1a;g (g…

ARM板上的蓝牙对讲功能

1&#xff09;ARMRTL8723 或RTL8821 RTL8723是USB接口的邮票芯片&#xff0c;集成了wifi和BT。前面已经完成了wifi的处理&#xff0c;这次主要说一下蓝牙语音方面。 蓝牙功能&#xff0c;我们主要是使用Bluez5协议栈.结合alsa使用&#xff08;pulseaudio也是可以的&#xff0c…

JVM学习(四)

1. JAVA IO/NIO 1.1. 阻塞 IO 模型 最传统的一种 IO 模型&#xff0c;即在读写数据过程中会发生阻塞现象。当用户线程发出 IO 请求之后&#xff0c;内 核会去查看数据是否就绪&#xff0c;如果没有就绪就会等待数据就绪&#xff0c;而用户线程就会处于阻塞状态&#xff0c…

Class 02 - R语言Rstudio的安装

Class 02 - R语言&Rstudio的安装 下载和安装R安装前准备下载R语言安装R语言开始使用R语言 下载和安装RStudio安装前准备下载RStudio安装RStudio开始使用RStudio如何编写代码 下载和安装R 在这个部分中&#xff0c;你将完成在计算机上下载和安装R语言程序。当安装完成后&am…

嫌视频背景杂乱或单调?如何去除视频杂乱背景

录制视频时&#xff0c;有时候视频背景会十分杂乱或单调&#xff0c;这会对用户的视觉体验和注意力产生负面影响。 背景杂乱或单调的场景可能会影响您的观感。这种情况通常发生在自然光线不足或拍摄环境不理想的情况下。如果您想改变视频的背景或者是去除视频杂乱的背景&#…

硬件I2C读写MPU6050代码

1、接线图 SDA接在B11,SCL接在B10 &#xff0c;软件IIC的两个引脚可以任意更改的&#xff0c;因为都是开漏输出&#xff0c;硬件接在哪个引脚上&#xff0c;程序中就对应操作哪个引脚 但是硬件IIC&#xff0c;通信引脚是不可以任意指定的&#xff0c;查表&#xff0c;由于PB6、…

2023年Android开发者路线-第4部分

2023年Android开发者路线-第1部分 2023年Android开发者路线-第2部分 2023年Android开发者路线-第3部分 2023年Android开发者路线-第4部分 2023年Android开发者路线-第4部分 在上一篇博文中&#xff0c;我们讨论了现代 Android 开发的基本要素&#xff0c;包括 Fragments、…

2023年Android开发者路线-第3部分

2023年Android开发者路线-第1部分 2023年Android开发者路线-第2部分 2023年Android开发者路线-第3部分 2023年Android开发者路线-第4部分 2023年Android开发者路线-第3部分 在上一篇文章中&#xff0c;我们讨论了 Android 主要组件的重要元素&#xff0c;包括 Intents 和 …

【C++初阶】类与对象(中)之你必须掌握的三个重要默认成员函数 --- 构造函数 +析构函数 + 拷贝构造函数

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

chatgptH5源码开发

hatGPTH5源码开发需要以下步骤&#xff1a; 确定需求&#xff1a;在开发ChatGPTH5应用之前&#xff0c;需要明确用户的需求和目标&#xff0c;以便进行合理的设计和开发。 技术选型&#xff1a;选择适合的前端技术框架和工具&#xff0c;如React、Vue、Angular等&#…

Flink从入门到精通之-10容错机制

Flink从入门到精通之-10容错机制 流式数据连续不断地到来&#xff0c;无休无止&#xff1b;所以流处理程序也是持续运行的&#xff0c;并没有一个明确的结束退出时间。机器运行程序&#xff0c;996 起来当然比人要容易得多&#xff0c;不过希望“永远运行”也是不切实际的。因…

WritingGPT: 基于ChatGPT和AutoGPT打造个人写作团队

本文介绍了如何通过定义多个GPT角色&#xff0c;以自动化流水线的方式打造个人写作团队&#xff0c;让AI帮助我们完成文章写作、修改、SEO等所有流程。原文: How I Built WritingGPT, a Fully Automated AI Writing Team ChatGPT是AI内容创造领域的巨大飞跃&#xff0c;但当我们…

8 种「Python 程序」定时执行方式

在日常工作中&#xff0c;我们常常会用到需要周期性执行的任务&#xff0c;一种方式是采用 Linux 系统自带的 crond 结合命令行实现&#xff0c;另外一种方式是直接使用Python。 最近我整理了一下 Python 定时任务的实现方式&#xff0c;建议收藏后学习。 利用while True: sle…

pro如何添加定时任务

Pro v2.4版本开始后台可以开关控制定时任务&#xff0c;那如何添加新的定时任务呢&#xff1f; 第一步&#xff1a;设置定时任务名称及标识&#xff1b; 文件app\controller\admin\v1\system\SystemTimer中task_name()方法 /**定时任务名称及标识 * return mixed */ public fu…

25岁,本科学历,待业,如何成为优秀的数据分析师,值得关注!

25岁&#xff0c;本科学历&#xff0c;待业&#xff0c;如何成为优秀的数据分析师&#xff0c;值得关注&#xff01; 你是在工作几年后确定自己的职业方向的呢&#xff1f;还是一直都是处于迷茫&#xff0c;随波逐流的状态&#xff1f;都说谁的青春不迷茫&#xff0c;但时间是最…

服务限流方案总结

流控作用 一般的做后台服务的&#xff0c;都会接触到流控&#xff0c;一般的场景就是在流量异常&#xff0c;比如遭受攻击的时候&#xff0c;保障服务不过载&#xff0c;在可支持的范围内提供稳定的服务。比如我们的服务支持100QPS&#xff0c;当一下子来了1000个请求的时候&a…

全景 I 0基础学习VR全景制作,平台篇第22篇 热点功能—作品功能操作

本期为大家带来蛙色VR平台&#xff0c;热点功能—作品功能操作。 功能位置示意 热点&#xff0c;指在全景作品中添加各种类型图标的按钮&#xff0c;引导用户通过按钮产生更多的交互&#xff0c;增加用户的多元化体验。 作品热点&#xff0c;即全景项目跳转热点&#xff0c;可与…

您使用的ChatGPT错了!以下是如何领先于 99% 的 ChatGPT 用户

我们大多数人都错误地使用了 ChatGPT&#xff1a; 错误1&#xff1a;不在提示中包含示例。 错误2&#xff1a;忽略通过角色控制 ChatGPT 的行为。 错误3&#xff1a;让 ChatGPT 猜测内容&#xff0c;而不是向它提供一些信息。 通过3类有用的prompt工程掌握 ChatGPT的使用。 …

leetcode 1259. 不相交的握手

1259. 不相交的握手 提示 困难 33 company 字节跳动 company 苹果 Apple company 亚马逊 偶数 个人站成一个圆&#xff0c;总人数为 num_people 。每个人与除自己外的一个人握手&#xff0c;所以总共会有 num_people / 2 次握手。 将握手的人之间连线&#xff0c;请你返回连线…