【网络】网络聊天室udp

news2024/12/23 17:42:06

网络聊天室udp

  • 一、低耦合度代码
    • 1、代码
    • 2、测试结果
  • 二、高耦合度代码
    • 1、服务端小改
      • (1)维护一个unordered_map用户列表
      • (2)服务端代码
      • (3)客户端不改的情况下结果展示
    • 2、大改客户端(udp全双工用多线程)
        • i、结果
        • ii、代码分析
        • iii、源代码
    • 3、ls /dev/pts/
    • 4、重定向输出dup2后在终端打印效果
    • 5、利用终端创建一个简易的聊天室
      • (1)代码
      • (2)结果展示
      • (3)也可以在终端用一个命令
    • 6、代码汇总


一、低耦合度代码

1、代码

在这里插入图片描述

2、测试结果

在这里插入图片描述

二、高耦合度代码

1、服务端小改

(1)维护一个unordered_map用户列表

在这里插入图片描述

(2)服务端代码

#pragma once 

#include <iostream>
#include <string>
#include <cstring>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unordered_map>
#include "Log.hpp"

using func_t = std::function<std::string(const std::string&, const std::string&, uint16_t)>; // 将返回值为string,参数为const string&的函数包装起来

extern Log log;

uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";

enum 
{
    SOCKET_ERR=1,
    BIND_ERR
};

class UdpServer
{
public:
    // 构造函数
    UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip)
        : _socketfd(0)
        , _port(port)
        , _ip(ip)
        , _isrunning(false)
    {}
    void Init()
    {
        // 1.创建udp套接字socket
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        // 创建失败
        if (_socketfd < 0)
        {
            log(Fatal, "socket create error,socketfd:%d", _socketfd);
            exit(SOCKET_ERR);
        }
        // 创建成功
        log(Info, "socket create sucess,socketfd:%d", _socketfd);

        // 2.绑定端口号bind socket
        struct sockaddr_in local; // 网络套接字结构体
        bzero(&local, sizeof(local)); // 将该套接字结构体对象全部清零
        local.sin_family = AF_INET; // 类型:ipv4
        local.sin_port = htons(_port); // 端口号:是在网络中来回发送的,我发过去要让对面知道我发的端口号是什么,所以必须是网络字节序列
        local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1.string->unit_32 2.来回通信对方要知道发送的ip,所以ip的unit_32必须是网络序列的
        int n = bind(_socketfd, (const struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            log(Fatal, "bind error, erron:%d, errno string:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind sucess");
    }

    void CheckUser(const struct sockaddr_in &client, const std::string& clientip, uint16_t clientport)
    {
        auto it = _online_user.find(clientip);
        if (it == _online_user.end())
        {
            // 添加
            _online_user.insert({clientip, client});
            std::cout << "{ " << clientip << " }" << "[ " << clientport << "]" << "#addr onlineuser sucess" << std::endl;
        }
    }
    void Broadcast(const std::string& info, const std::string& clientip, uint16_t clientport)
    {
        for (const auto& user : _online_user)
        {
            std::string message = "{ ";
            message += clientip;
            message += " }";
            message += "[ ";
            message += std::to_string(clientport);
            message += " ]#";
            message += info;
            socklen_t len = sizeof(user.second);
            sendto(_socketfd, message.c_str(), message.size(), 0, (struct sockaddr*)&(user.second), len);
        }
    }

    void Run(/*func_t func*/) // 对代码进行分层
    {
        _isrunning = true;
        char inbuffer[1024];
        while (_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_socketfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);
            if (n < 0)
            {
                log(Warning, "recvfrom error");
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port); // 网络序列转主机序列,拿到port
            std::string clientip = inet_ntoa(client.sin_addr); // 将网络序列的ip地址转化成为主机的序列的ip地址,风格是字符串类型的
            
            // 判断是不是新用户,新用户则添加到onlineuser
            CheckUser(client, clientip, clientport);
            
            std::string info = inbuffer;
            // 转发给所有人
            Broadcast(info, clientip, clientport);
        }
    }
    // 析构函数
    ~UdpServer()
    {
        if (_socketfd > 0) 
        {
            close(_socketfd);
        }
    }
private:
    int _socketfd; // 网络文件描述符,表示socket返回的文件描述符
    uint16_t _port; // 表明服务器进程的端口号
    std::string _ip; // ip地址,任意地址绑定为0
    bool _isrunning; // 判断是否运行
    std::unordered_map<std::string, struct sockaddr_in> _online_user; // client列表 主机序列的ip地址,网络序列的套接字信息
};

(3)客户端不改的情况下结果展示

在这里插入图片描述
出现一个比较奇葩的问题,我上面linux客户端是先链接的,发出一个消息halo,发现能够显示出来,我下面的vs是后面链接的,发了个haha,发现是能够显示的,但是我上面linux客户端发现halo被haha覆盖住了,而我们知道udp是个全双工的,就是接收消息和发送消息是可以分开的,可以套在多线程中的,但是上面消息却会被覆盖,因为在客户端的getline那边会阻塞住,会让优先级发送消息,我客户端要一直按回车才能收到别人发的消息,而不能实现消息的自动弹出,客户端只能一条一条收到消息,所以我们在客户端需要使用多线程版本的。我想实现一个我不发消息也能看到别人群聊发的消息。

2、大改客户端(udp全双工用多线程)

我们因为上面客户端不改变的情况下发现的是我要一直摁回车才能收到别人发的消息,其本质原因就是因为getline会阻塞住消息,即一条消息一发一条消息一收,所以我们下面客户端改成全双工,即接消息和发消息分开,这样能够实现我不发消息依旧能够收到别人发的消息,不需要按回车,我们看一下现象,再分析代码:

i、结果

在这里插入图片描述

ii、代码分析

在这里插入图片描述

iii、源代码
#include <iostream>
#include <unistd.h>
#include <string>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

void Usage(std::string proc)
{
    std::cout << "\n\rUsages: " << proc << "serverip serverport\n" << std::endl;
}

struct ThreadData
{
    struct sockaddr_in server;
    int socketfd;
};

void* recv_message(void* args)
{
    ThreadData* td = static_cast<ThreadData*>(args);
    char buffer[1024];
    while (true)
    {
        // 收数据 -- 从socket文件中的数据拿出来到buffer中,并将收到的对方的个人信息进行保存到temp中
        struct sockaddr_in temp;
        socklen_t len2 = sizeof(temp);
        ssize_t n = recvfrom(td->socketfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len2);
        if (n > 0)
        {
            buffer[n] = 0;
            // 打印数据
            std::cout << buffer << std::endl;
        }
    }
}

void* send_message(void* args)
{
    ThreadData* td = static_cast<ThreadData*>(args);
    std::string message;
    socklen_t len = sizeof(td->server);
    while (true)
    {
        // 数据
        std::cout << "Please Enter# ";
        getline(std::cin, message);
        
        // 发送数据 -- 把数据发送到socketfd文件中,并将server信息提炼出来发送给server,可以理解成唤醒server
        sendto(td->socketfd, message.c_str(), message.size(), 0, (struct sockaddr *)&(td->server), len);
    }
}

// ./udpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(0);
    }
    std::string serverip = argv[1]; // serverip
    uint16_t serverport = std::stoi(argv[2]); // serverport

    struct ThreadData td;

    // 给谁发
    bzero(&td.server, sizeof(td.server));
    td.server.sin_family = AF_INET;
    td.server.sin_port = htons(serverport);
    td.server.sin_addr.s_addr = inet_addr(serverip.c_str());

    td.socketfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建套接字
    if (td.socketfd < 0)
    {
        std::cout << "socket create error" << std::endl;
        return 1;
    }

    pthread_t recver;
    pthread_t sender;
    pthread_create(&recver, nullptr, recv_message, &td);
    pthread_create(&sender, nullptr, send_message, &td);

    pthread_join(recver, nullptr);
    pthread_join(sender, nullptr);

    close(td.socketfd);
    return 0;
}

3、ls /dev/pts/

我们发现的是/dev/pts/的3号文件是我们的1号终端,我们发送消息给3号文件就是发送给了1号终端了。
在这里插入图片描述

4、重定向输出dup2后在终端打印效果

test.cc:
在这里插入图片描述

g++ test.cc

在这里插入图片描述

5、利用终端创建一个简易的聊天室

(1)代码

在这里插入图片描述

(2)结果展示

在这里插入图片描述

(3)也可以在终端用一个命令

这里的3是你需要ls 命令去看哪个终端能用的文件信息的,因为linux下一切皆文件,那么终端也是文件。
在这里插入图片描述

6、代码汇总

udpserver.hpp:

#pragma once 

#include <iostream>
#include <string>
#include <cstring>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unordered_map>
#include "Log.hpp"

using func_t = std::function<std::string(const std::string&, const std::string&, uint16_t)>; // 将返回值为string,参数为const string&的函数包装起来

extern Log log;

uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";

enum 
{
    SOCKET_ERR=1,
    BIND_ERR
};

class UdpServer
{
public:
    // 构造函数
    UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip)
        : _socketfd(0)
        , _port(port)
        , _ip(ip)
        , _isrunning(false)
    {}
    void Init()
    {
        // 1.创建udp套接字socket
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        // 创建失败
        if (_socketfd < 0)
        {
            log(Fatal, "socket create error,socketfd:%d", _socketfd);
            exit(SOCKET_ERR);
        }
        // 创建成功
        log(Info, "socket create sucess,socketfd:%d", _socketfd);

        // 2.绑定端口号bind socket
        struct sockaddr_in local; // 网络套接字结构体
        bzero(&local, sizeof(local)); // 将该套接字结构体对象全部清零
        local.sin_family = AF_INET; // 类型:ipv4
        local.sin_port = htons(_port); // 端口号:是在网络中来回发送的,我发过去要让对面知道我发的端口号是什么,所以必须是网络字节序列
        local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1.string->unit_32 2.来回通信对方要知道发送的ip,所以ip的unit_32必须是网络序列的
        int n = bind(_socketfd, (const struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            log(Fatal, "bind error, erron:%d, errno string:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind sucess");
    }

    void CheckUser(const struct sockaddr_in &client, const std::string& clientip, uint16_t clientport)
    {
        auto it = _online_user.find(clientip);
        if (it == _online_user.end())
        {
            // 添加
            _online_user.insert({clientip, client});
            std::cout << "{ " << clientip << " }" << "[ " << clientport << "]" << "#addr onlineuser sucess" << std::endl;
        }
    }
    void Broadcast(const std::string& info, const std::string& clientip, uint16_t clientport)
    {
        for (const auto& user : _online_user)
        {
            std::string message = "{ ";
            message += clientip;
            message += " }";
            message += "[ ";
            message += std::to_string(clientport);
            message += " ]#";
            message += info;
            socklen_t len = sizeof(user.second);
            sendto(_socketfd, message.c_str(), message.size(), 0, (struct sockaddr*)&(user.second), len);
        }
    }

    void Run(/*func_t func*/) // 对代码进行分层
    {
        _isrunning = true;
        char inbuffer[1024];
        while (_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_socketfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);
            if (n < 0)
            {
                log(Warning, "recvfrom error");
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port); // 网络序列转主机序列,拿到port
            std::string clientip = inet_ntoa(client.sin_addr); // 将网络序列的ip地址转化成为主机的序列的ip地址,风格是字符串类型的
            
            // 判断是不是新用户,新用户则添加到onlineuser
            CheckUser(client, clientip, clientport);
            
            std::string info = inbuffer;
            // 转发给所有人
            Broadcast(info, clientip, clientport);
        }
    }
    // 析构函数
    ~UdpServer()
    {
        if (_socketfd > 0) 
        {
            close(_socketfd);
        }
    }
private:
    int _socketfd; // 网络文件描述符,表示socket返回的文件描述符
    uint16_t _port; // 表明服务器进程的端口号
    std::string _ip; // ip地址,任意地址绑定为0
    bool _isrunning; // 判断是否运行
    std::unordered_map<std::string, struct sockaddr_in> _online_user; // client列表 主机序列的ip地址,网络序列的套接字信息
};

udpclient.cc:

#include <iostream>
#include <unistd.h>
#include <string>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "terminal.hpp"

void Usage(std::string proc)
{
    std::cout << "\n\rUsages: " << proc << "serverip serverport\n" << std::endl;
}

struct ThreadData
{
    struct sockaddr_in server;
    int socketfd;
    std::string serverip;
};

void* recv_message(void* args)
{
    openterminal();
    ThreadData* td = static_cast<ThreadData*>(args);
    char buffer[1024];
    while (true)
    {
        // 清空
        memset(buffer, 0, sizeof(buffer));
        // 收数据 -- 从socket文件中的数据拿出来到buffer中,并将收到的对方的个人信息进行保存到temp中
        struct sockaddr_in temp;
        socklen_t len2 = sizeof(temp);
        ssize_t n = recvfrom(td->socketfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len2);
        if (n > 0)
        {
            buffer[n] = 0;
            // 打印数据
            std::cerr << buffer << std::endl;
        }
    }
}

void* send_message(void* args)
{
    ThreadData* td = static_cast<ThreadData*>(args);
    std::string message;
    socklen_t len = sizeof(td->server);

    std::string welcome = td->serverip;
    welcome += "coming...";
    sendto(td->socketfd, message.c_str(), message.size(), 0, (struct sockaddr *)&(td->server), len);

    while (true)
    {
        // 数据
        std::cout << "Please Enter# ";
        getline(std::cin, message);
        
        // 发送数据 -- 把数据发送到socketfd文件中,并将server信息提炼出来发送给server,可以理解成唤醒server
        sendto(td->socketfd, message.c_str(), message.size(), 0, (struct sockaddr *)&(td->server), len);
    }
}

// ./udpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(0);
    }
    std::string serverip = argv[1]; // serverip
    uint16_t serverport = std::stoi(argv[2]); // serverport

    struct ThreadData td;

    // 给谁发
    bzero(&td.server, sizeof(td.server));
    td.server.sin_family = AF_INET;
    td.server.sin_port = htons(serverport);
    td.server.sin_addr.s_addr = inet_addr(serverip.c_str());

    td.socketfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建套接字
    if (td.socketfd < 0)
    {
        std::cout << "socket create error" << std::endl;
        return 1;
    }

    td.serverip = serverip;

    pthread_t recver;
    pthread_t sender;
    pthread_create(&recver, nullptr, recv_message, &td);
    pthread_create(&sender, nullptr, send_message, &td);

    pthread_join(recver, nullptr);
    pthread_join(sender, nullptr);

    close(td.socketfd);
    return 0;
}

main.cc:

#include "udp.hpp"
#include "Log.hpp"
#include <memory>
#include <cstdio>
#include <vector>

Log log;

void Usage(std::string proc)
{
    std::cout << "\n\rUsages: " << proc << "port[1024+]\n" << std::endl;
}

std::string Handler(const std::string& info, const std::string& ip, uint16_t port)
{
    std::cout << "{ " << ip << " }" << "[ " << port << "]" << "# " << info << std::endl;
    std::string res = "recv a message# ";
    res += info;
    std::cout << res << std::endl;
    return res;
}

bool SafeCheck(const std::string& cmd)
{
    std::vector<std::string> word_key = {
        "rm",
        "top",
        "cp",
        "yum",
        "while",
        "kill",
        "unlink"
        "uninstall",
        "top"
    };
    for (auto &word : word_key)
    {
        auto pos = cmd.find(word);
        if (pos != std::string::npos)
        {
            return false;
        }
    }
    return true;
}

std::string ExcuteCommand(const std::string& cmd)
{
    std::cout << "get a massage:" << cmd << std::endl;
    // 做一个保护
    if (!SafeCheck(cmd)) return "bad man";
    
    FILE* fp = popen(cmd.c_str(), "r"); // 管道创建好,子进程创建好,子进程通过管道放到父进程
    if (nullptr == fp)
    {
        perror("popen failed");
        return "error";
    }
    std::string result;
    char buffer[4096];
    while (true)
    {
        char* ok = fgets(buffer, sizeof(buffer), fp); // 写到buffer缓冲区中
        if (ok == nullptr)
        {
            break;
        }
        result += buffer;
    }
    pclose(fp);
    return result;
}

// 以后用的是./udpserver + port
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<UdpServer> svr(new UdpServer(port)); // new一个对象

    svr->Init(); // 初始化
    svr->Run(/*Handler*/);  // 跑起来
}

terminal.hpp:

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string>

std::string terminal = "/dev/pts/3";

int openterminal()
{
    int fd = open(terminal.c_str(), O_WRONLY);
    if (fd < 0)
    {
        std::cerr << "open terminal err" << std::endl;
        return 1;
    }
    dup2(fd, 2); // 重定向到标准错误
    return 0;
}

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

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

相关文章

通过QT进行服务器和客户端之间的网络通信

客户端 client.pro #------------------------------------------------- # # Project created by QtCreator 2024-07-02T14:11:20 # #-------------------------------------------------QT core gui network #网络通信greaterThan(QT_MAJOR_VERSION, 4): QT widg…

饥荒dst联机服务器搭建基于Ubuntu

目录 一、服务器配置选择 二、项目 1、下载到服务器 2、解压 3、环境 4、启动面板 一、服务器配置选择 首先服务器配置需要2核心4G&#xff0c;4G内存森林加洞穴大概就占75% 之后进行服务器端口的开放&#xff1a; tcp:8082 tcp:8080 UDP:10888 UDP:10998 UDP:10999 共…

套接字编程一(简单的UDP网络程序)

文章目录 一、 理解源IP地址和目的IP地址二、 认识端口号1. 理解 "端口号" 和 "进程ID"2. 理解源端口号和目的端口号 三、 认识协议1. 认识TCP协议2. 认识UDP协议 四、 网络字节序五、 socket编程接口1. socket 常见API2. sockaddr结构&#xff08;1&#…

输入设备应用编程-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

输入设备应用编程 输入类设备编程介绍 什么是输入设备 输入设备&#xff08;input 设备&#xff09;&#xff0c;如鼠标、键盘、触摸屏等&#xff0c;允许用户与系统交互 input 子系统 Linux系统通过input子系统管理多种输入设备 Input子系统提供统一的框架和接口&#xff…

网络编程之LINUX信号

注意发送信号是给进程&#xff0c;不是线程&#xff0c;调用的是KILL函数&#xff0c;SIG是信号种类。pid0是本进程的其他的进程。 可以通过设置ERRNO来查看返回的错误&#xff0c;如下&#xff1a; 当目标进程收到信号后&#xff0c;要对信号进行一些执行操作&#xff1a; 定义…

[每周一更]-(第106期):DNS和SSL协作模式

文章目录 什么是DNS&#xff1f;DNS解析过程DNS解析的底层逻辑 什么是SSL&#xff1f;SSL证书SSL握手过程SSL的底层逻辑 DNS与SSL的协同工作过程 什么是DNS&#xff1f; DNS&#xff08;Domain Name System&#xff0c;域名系统&#xff09;是互联网的重要组成部分&#xff0c…

黑马程序员MySQL基础学习,精细点复习【持续更新】

文章目录 数据库Mysql基础一、数据库1.数据库2.数据库管理系统3.SQL4.Mysql目录结构5.关系型数据库6.SQL基础概念 mysql高级一、数据库备份和还原1.图形化界面备份与还原 二、约束1.分类&#xff1a;2.主键约束3.唯一约束4.非空约束5.默认值约束6.外键约束 三、表关系1.概述2.一…

《Windows API每日一练》13.1 打印基础

在Windows中使用打印机时&#xff0c;在调用一系列与打印相关的GDI绘图函数的背后&#xff0c;实际上启动了一系列模块之间复杂的交互过程&#xff0c;包括 GDI32库模块、打印机设备驱动程序库模块&#xff08;带.DRV后缀的文件&#xff09;、Windows后台打印处理程序&#xff…

5. harbor镜像仓库

harbor镜像仓库 一、镜像仓库1、类型2、构建私有仓库的方案 二、部署harbor仓库(单机版)1、安装docker(略)2、安装docker-compose工具3、安装harbor4、生成harbor需要的证书、密钥(V3版本证书)4.1 创建CA4.2 创建harbor仓库需要的证书 5、编辑harbor配置文件6、启动harbor 三、…

【C++】17.AVL树

一、AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种…

Linux实用操作二

文章目录 Linux实用操作二日期、时区&#xff1a;date命令查看日期时间作用&#xff1a;语法&#xff1a;字段解释&#xff1a;操作&#xff1a; 修改Linux系统时区作用&#xff1a;操作&#xff1a; 使用ntp进行时间同步和校准作用&#xff1a;操作&#xff1a; IP地址、主机名…

buuctf web 第五到八题

[ACTF2020 新生赛]Exec 这里属实有点没想到了&#xff0c;以为要弹shell&#xff0c;结果不用 127.0.0.1;ls /PING 127.0.0.1 (127.0.0.1): 56 data bytes bin dev etc flag home lib media mnt opt proc root run sbin srv sys tmp usr var127.0.0.1;tac /f*[GXYCTF2019]Pin…

【数学建模】多波束测线问题(持续更新)

多波束测线问题 问题 1建立模型覆盖宽度海水深度重叠长度重叠率 问题二问题三问题四 问题 1 与测线方向垂直的平面和海底坡面的交线构成一条与水平面夹角为 α \alpha α的斜线&#xff08;如下图&#xff09;&#xff0c;称 α \alpha α为坡度。请建立多波束测深的覆盖宽度及…

世界华人国学泰斗级人物颜廷利:人类史上全球公认最伟大的思想家

世界华人国学泰斗级人物颜廷利:人类史上全球公认最伟大的思想家 颜廷利教授,一位在21世纪东方哲学、科学界具有深远影响力的人物,同时也是当代中国教育界的知名教授、周易起名与易经姓名学的专家,以及现代国学的杰出代表。他在其著作《升命学说》中提出了一系列独到的理论,包括…

Mybatis--分页查询

一、分页查询 分页查询则是在页面上将本来很多的数据分段显示&#xff0c;每页显示用户自定义的行数。可提高用户体验度&#xff0c;同时减少一次性加载&#xff0c;内存溢出风险。 1、真假分页 分页分为&#xff1a;真分页和假分页。 假分页&#xff1a;一次性查询所有数据存入…

笔记小结:卷积神经网络之多输入多输出通道

本文为李沐老师《动手学深度学习》笔记小结&#xff0c;用于个人复习并记录学习历程&#xff0c;适用于初学者 彩色图像具有标准的RGB通道来代表红、绿和蓝&#xff0c;需要三个通道表示&#xff0c;故而只有单输入单输出是不够的。 对于单个输入和单个输出通道的简化例子&…

Yolo-World网络模型结构及原理分析(一)——YOLO检测器

文章目录 概要一、整体架构分析二、详细结构分析YOLO检测器1. Backbone2. Head3.各模块的过程和作用Conv卷积模块C2F模块BottleNeck模块SPPF模块Upsampling模块Concat模块 概要 尽管YOLO&#xff08;You Only Look Once&#xff09;系列的对象检测器在效率和实用性方面表现出色…

【引领未来智造新纪元:量化机器人的革命性应用】

在日新月异的科技浪潮中&#xff0c;量化机器人正以其超凡的智慧与精准的操作&#xff0c;悄然改变着各行各业的生产面貌&#xff0c;成为推动产业升级、提升竞争力的关键力量。今天&#xff0c;让我们一同探索量化机器人在不同领域的广泛应用价值&#xff0c;见证它如何以科技…

CSA笔记4-包/源管理命令以及本地光盘仓库搭建

包/源管理命令 1.rpm是最基础的rmp包的安装命令&#xff0c;需要提前下载相关安装包和依赖包 2.yum/dnf是基于rpm包的自动安装命令&#xff0c;可以自动在仓库中匹配安装软件和依赖包 注意:以上是安装命令&#xff0c;以下是安装源 3.光盘源&#xff1a;是指安装系统时后的…

Air780EP- AT开发-阿里云应用指南

简介 使用AT方式连接阿里云分为一机一密和一型一密两种方式&#xff0c;其中一机一密又包括HTTP认证二次连接和MQTT直连两种方式 关联文档和使用工具&#xff1a; AT固件获取在线加/解密工具阿里云平台 准备工作 Air780EP_全IO开发板一套&#xff0c;包括天线SIM卡&#xff0…