Linux网络——UDP的运用

Linux网络——UDP的运用


文章目录

  • Linux网络——UDP的运用
    • 一、引入
    • 二、服务端实现
      • 2.1 创建socket套接字
      • 2.2 绑定bind
      • 2.3 启动服务器
      • 2.4 IP的绑定的细节
      • 2.5 读取数据recvfrom
    • 三、用户端实现
      • 3.1 绑定问题
      • 3.2 发送数据sendto
    • 四、代码
    • 五、UDP实现网络聊天室(简易版)


一、引入

在上一篇Linux网络——网络套接字中我们简述了TCP/UDP协议

其实网络通信的本质就是进程间通信,而进程间通信无非就是读和写(IO)

这篇文章我们借助UDP实现服务端(server)和客户端(client)进行通信
将所学理论运用到实践中

二、服务端实现

由于服务端是面向广大用户的,如果我们将IP地址写死,那么服务器将只能从我们写的哪个IP地址中接收信息
所以启动服务器是不需要IP地址的,它默认接收所以IP地址发来的信息

//static const std::string default_ip = "0.0.0.0";
static const uint16_t default_port = 8888;

2.1 创建socket套接字

调用系统接口socket函数,帮助我们创建套接字,本质是把文件和网卡关联起来
在这里插入图片描述

参数介绍:

domain:一个域,标识了这个套接字的通信类型(网络或者本地)
在这里插入图片描述

只用关注上面三个类,第一个与第二个AF_UNIX/AF_LOCAL表示本地通信,而AF_INET表示网络通信
type:套接字提供服务的类型
在这里插入图片描述
我们用UDP实现,所以使用SOCK_DGRAM
protocol:想使用的协议,默认为0即可,因为前面的两个参数决定了,就已经决定了是TCP还是UDP协议了

返回值:

成功则返回打开的文件描述符(指向网卡文件,其实就是文件描述符),失败返回-1

创建套接字的本质其实就是创建了一个文件描述符,并返回该文件描述符的值
只是该文件描述符是用于对应服务的网路数据传输

	_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (_sockfd < 0)
	{
 		lg.LogMessgae(Fatal, "socket error, sockfd : %d\n", _sockfd);
    	exit(1);
	}

	lg.LogMessgae(Info, "socket success, sockfd : %d\n", _sockfd);

2.2 绑定bind

在这里插入图片描述

参数介绍:

socket:创建套接字的返回值
address:通用结构体(上一章Linux网络——网络套接字有详细介绍)
address_len:传入结构体的长度

我们要先定义一个sockaddr_in结构体,将结构体内对应的字段填充好,再将结构体作为参数传递
在这里插入图片描述

struct sockaddr_in {
    short int sin_family;           // 地址族,一般为AF_INET或PF_INET
    unsigned short int sin_port;    // 端口号,网络字节序
    struct in_addr sin_addr;        // IP地址
    unsigned char sin_zero[8];      // 用于填充,使sizeof(sockaddr_in)等于16
};

创建结构体后要先清空数据(初始化),我们可以用memset,也可以用系统接口:

#include <strings.h>

void bzero(void *s, size_t n);

填充端口号的时候要注意端口号是两个字节的数据,涉及到大小端问题
在计算机中的普遍规定:在网络中传输的数据都是大端的
所以为了统一,无论我们机器是大端还是小端,在调用接口的时候,都将IP与端口号从主机序列转化为网路序列

端口号的接口

#include <arpa/inet.h>
// 主机序列转网络序列
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
// 网络序列转主机序列
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

IP的接口
对于IP,其实有两步:首先将字符串转换为整型,再解决大小端问题
系统给了直接能解决这两个问题的接口

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

int inet_aton(const char *cp, struct in_addr *inp);

in_addr_t inet_addr(const char *cp);// 点分十进制字符串

in_addr_t inet_network(const char *cp);

char *inet_ntoa(struct in_addr in);

struct in_addr inet_makeaddr(int net, int host);

in_addr_t inet_lnaof(struct in_addr in);

in_addr_t inet_netof(struct in_addr in);

这里的inet_addr就是把一个点分十进制的字符串转化成整数再进行大小端处理

整体代码:

#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include "nocopy.hpp"
#include "Log.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cerrno>
#include <strings.h>
#include <string.h>
#include "InetAddr.hpp"
#include "stdlib.h"
#include <functional>

//static const std::string default_ip = "0.0.0.0";
static const uint16_t default_port = 8888;
static const int default_fd = -1;
static const int default_size = 1024;

using func_t = function<std::string(std::string)>;


class UdpServer : public nocopy
{
public:
    UdpServer(func_t BackMessage,const uint16_t port = default_port)
        : _port(port), _sockfd(default_fd),_BackMessage(BackMessage)
    {
    }

    void Init()
    { // 1. 创建socket
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            lg.LogMessgae(Fatal, "socket error, sockfd : %d\n", _sockfd);
            exit(1);
        }

        lg.LogMessgae(Info, "socket success, sockfd : %d\n", _sockfd);
        // 2. 指定网络接口

        // 初始化结构体
        struct sockaddr_in local;
        bzero(&local, sizeof(local)); // memset

        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n != 0)
        {
            lg.LogMessgae(Fatal, "bind error\n");
            exit(2);
        }
    }


    ~UdpServer()
    {
    }

private:
    std::string _ip;
    uint16_t _port;
    int _sockfd;

    func_t _BackMessage;
};

2.3 启动服务器

服务器一般都是死循环的,基本上永远不退出,除非用户删除
站在操作系统的角度,服务器是常驻内存中的进程

我们在启动服务器的时候只需要传入端口号即可,IP默认为INADDR_ANY 即接收所以的IP

void Usage(const std::string s)
{
    std::cout << "Usagr: " << s << " local_port" << std::endl;
}

std::string BackMessage(const string s)
{
    return "Back Message# " + s;
}



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

    std::unique_ptr<UdpServer> usver(new UdpServer(BackMessage,std::stoi(argv[1])));

    usver->Init();
    usver->Start();
    return 0;
}

2.4 IP的绑定的细节

如果我们将IP绑定为127.0.0.1,这个IP地址叫做本地环回地址

它的作用就是用来做服务器代码测试的
意思就是如果我们绑定的IP是127.0.0.1的话,在应用层发送的消息不会进入物理层,也就不会发送出去
在这里插入图片描述

当我们运行起来后想要查看网络情况就可以用指令netstat
后边也可以附带参数:

-a:显示所有连线中的Socket;
-e:显示网络其他相关信息;
-i:显示网络界面信息表单;
-l:显示监控中的服务器的Socket;
-n:直接使用ip地址(数字),而不通过域名服务器;
-p:显示正在使用Socket的程序识别码和程序名称;
-t:显示TCP传输协议的连线状况;
-u:显示UDP传输协议的连线状况;

在这里插入图片描述

在这里插入图片描述

2.5 读取数据recvfrom

服务端要获取到用户端发送过来的数据

在这里插入图片描述

参数说明:

sockfd:从哪个套接字读
buf:数据放入的缓冲区
len:缓冲区长度
flags:读取方式, 0代表阻塞式读取
src_addraddrlen:输出型参数,返回对应的消息内容是从哪一个客户端发出的。第一个是自己定义的结构体,第二个是结构体长度

    void Start()
    {
        char buffer[1024];
        for (;;)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);

            if (n > 0)
            {
                InetAddr addr(peer);
                buffer[n] = 0;
                std::cout << "client[" << addr.PrintDebug() << "]# " << buffer << std::endl;

                std::string task = _BackMessage(buffer);
                sendto(_sockfd, task.c_str(), task.size(), 0, (struct sockaddr *)&peer, len);
            }
        }
    }

现在我们想要知道是谁发送过来的消息,信息都被保存到了peer结构体中,我们知道IP信息在peer.sin_addr.s_addr
这是一个网络序列,要转成主机序列,其次为了方便观察,要把它转换成点分十进制
而这两个操作系统给了一个接口能够解决:

char *inet_ntoa(struct in_addr in);

同样获取端口号的时候也要由网络序列转成主机序列:

uint16_t ntohs(uint16_t netshort);

这里我对struct sockaddr_in进行了封装
单独写了一个类InetAddr用来专门读取struct sockaddr_in内的ip和端口号

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

class InetAddr
{
    public:
    InetAddr(struct sockaddr_in &addr)
    :_addr(addr)
    {
        _ip = inet_ntoa(addr.sin_addr);
        _port = ntohs(addr.sin_port);
    }

    std::string Ip()
    {
        return _ip;
    }

    uint16_t Port()
    {
        return _port;
    }

    sockaddr_in Addr()
    {
        return _addr;
    }

    std::string PrintDebug()
    {
        std::string info = _ip;
        info += ":";
        info += std::to_string(_port);  // "127.0.0.1:4444"
        return info;
    }
    
    ~InetAddr(){}

    private:
    std::string _ip;
    uint16_t _port;
    struct sockaddr_in _addr;
};

三、用户端实现

要发送数据给服务器,就得知道服务器的IP和端口号
这里的IP就必须指明,用来表示我是连接哪台服务器上的哪个进程

uint16_t _serverport;
std::string _serverip;
int _sockfd;

这里的IP和port指的是要发送给谁,也就是需要服务器的IP和端口号

创建套接字就跟前面的一样:

    //1. 创建socket
    int _sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(_sockfd < 0)
    {
        lg.LogMessgae(Fatal, "socket error, sockfd : %d\n", _sockfd);
        exit(1);

    }
    lg.LogMessgae(Info, "socket success, sockfd : %d\n", _sockfd);

3.1 绑定问题

首先bind的作用是允许应用程序指定一个端口号用于监听传入的数据报或数据流

对于服务端:
需要绑定一个公开的端口号,允许大家访问,如果是随机的,其他人不知道,也就访问不了,所以服务端需要绑定

对于客户端:
客户端在给服务器发信息的同时,服务器也可能给客户端发信息,所以客户端为了监听服务器有没有给自己回信息也需要bind一个端口号用于监听,但客户端不需要显式的bind,因为客户端的端口号不会被所有人访问,别人不需要知道,所以OS自动帮我们bind一个随机的端口号,另外也是为了防止端口号重复,避免破坏唯一性

那么为什么前面服务端必须显示的绑定port呢?

因为服务器的端口号是众所周知的,不能改变,如果变了就找不到服务器了

而客户端只需要有就可以,只用标识唯一性即可
举个例子:

我们手机上有很多的app,而每个服务端是一家公司写的,但是客户端却是多个公司写的
如果我们绑定了特定的端口,万一两个公司都用了同一个端口号呢?这样就直接冲突了

OS会自动填充主机IP和随机生成端口号进行绑定(在发送数据的时候自动绑定)
所以创建客户端我们只用创建套接字即可

  //1. 创建socket
    int _sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(_sockfd < 0)
    {
        lg.LogMessgae(Fatal, "socket error, sockfd : %d\n", _sockfd);
        exit(1);

    }
    lg.LogMessgae(Info, "socket success, sockfd : %d\n", _sockfd);

    //2. 指定网络接口
    struct sockaddr_in send;
    memset(&send,0,sizeof(send));
    send.sin_family = AF_INET;
    send.sin_port = htons(stoi(argv[2]));
    send.sin_addr.s_addr = inet_addr(argv[1]);

3.2 发送数据sendto

在这里插入图片描述

这里的参数和上面的recvfrom差不多,而这里的结构体内部我们要自己填充目的IP和目的端口号
也就是服务器的IP和端口号

 //3. 发送消息
    while(true)
    {
        std::string buffer;
        std::cout<<"Please Enter# ";
        getline(std::cin,buffer);
        //cout<<buffer<<endl;
        ssize_t n = sendto(_sockfd,buffer.c_str(),buffer.size(),0,(struct sockaddr*)&send,sizeof(send));
        if(n == -1)
        {
            cout<<"error"<<endl;
        }
        if(n > 0)
        {
            char rec[1024];
            //没用,但有必要
            struct sockaddr_in tmp;
            socklen_t len = sizeof(tmp);

            ssize_t m = recvfrom(_sockfd, rec, sizeof(rec) - 1, 0, (struct sockaddr *)&tmp, &len);
            
            if(m>0)
            {
                rec[m] = 0;
                InetAddr addr(tmp);
                std::cout<<"server["<<addr.PrintDebug()<<"]# "<<rec<<std::endl;
            }
            else
            {
                break;
            }


        }
        else
        {
            break;
        }
    }

四、代码

UdpServer

#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include "nocopy.hpp"
#include "Log.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cerrno>
#include <strings.h>
#include <string.h>
#include "InetAddr.hpp"
#include "stdlib.h"
#include <functional>

//static const std::string default_ip = "0.0.0.0";
static const uint16_t default_port = 8888;
static const int default_fd = -1;
static const int default_size = 1024;

using func_t = function<std::string(std::string)>;


class UdpServer : public nocopy
{
public:
    UdpServer(func_t BackMessage,const uint16_t port = default_port)
        : _port(port), _sockfd(default_fd),_BackMessage(BackMessage)
    {
    }

    void Init()
    { // 1. 创建socket
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            lg.LogMessgae(Fatal, "socket error, sockfd : %d\n", _sockfd);
            exit(1);
        }

        lg.LogMessgae(Info, "socket success, sockfd : %d\n", _sockfd);
        // 2. 指定网络接口

        // 初始化结构体
        struct sockaddr_in local;
        bzero(&local, sizeof(local)); // memset

        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n != 0)
        {
            lg.LogMessgae(Fatal, "bind error\n");
            exit(2);
        }
    }

    void Start()
    {
        char buffer[1024];
        for (;;)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);

            if (n > 0)
            {
                InetAddr addr(peer);
                buffer[n] = 0;
                std::cout << "client[" << addr.PrintDebug() << "]# " << buffer << std::endl;

                std::string task = _BackMessage(buffer);
                sendto(_sockfd, task.c_str(), task.size(), 0, (struct sockaddr *)&peer, len);
            }
        }
    }

    ~UdpServer()
    {
    }

private:
    std::string _ip;
    uint16_t _port;
    int _sockfd;

    func_t _BackMessage;
};



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

void Usage(const std::string s)
{
    std::cout << "Usagr: " << s << " local_port" << std::endl;
}

std::string BackMessage(const string s)
{
    return "Back Message# " + s;
}



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

    std::unique_ptr<UdpServer> usver(new UdpServer(BackMessage,std::stoi(argv[1])));

    usver->Init();
    usver->Start();
    return 0;
}

UdpClient

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include <memory.h>
#include "InetAddr.hpp"
#include <cstdio>
#include <string>
void usage(std::string s)
{
    std::cout<<"Usagr: "<<s<<" server_ip server_port"<<std::endl;
}

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

    //1. 创建socket
    int _sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(_sockfd < 0)
    {
        lg.LogMessgae(Fatal, "socket error, sockfd : %d\n", _sockfd);
        exit(1);

    }
    lg.LogMessgae(Info, "socket success, sockfd : %d\n", _sockfd);

    //2. 指定网络接口
    struct sockaddr_in send;
    memset(&send,0,sizeof(send));
    send.sin_family = AF_INET;
    send.sin_port = htons(stoi(argv[2]));
    send.sin_addr.s_addr = inet_addr(argv[1]);

    //3. 发送消息
    while(true)
    {
        std::string buffer;
        std::cout<<"Please Enter# ";
        getline(std::cin,buffer);
        //cout<<buffer<<endl;
        ssize_t n = sendto(_sockfd,buffer.c_str(),buffer.size(),0,(struct sockaddr*)&send,sizeof(send));
        if(n == -1)
        {
            cout<<"error"<<endl;
        }
        if(n > 0)
        {
            char rec[1024];
            //没用,但有必要
            struct sockaddr_in tmp;
            socklen_t len = sizeof(tmp);

            ssize_t m = recvfrom(_sockfd, rec, sizeof(rec) - 1, 0, (struct sockaddr *)&tmp, &len);
            
            if(m>0)
            {
                rec[m] = 0;
                InetAddr addr(tmp);
                std::cout<<"server["<<addr.PrintDebug()<<"]# "<<rec<<std::endl;
            }
            else
            {
                break;
            }


        }
        else
        {
            break;
        }
    }


    return 0;
}

运行图:

在这里插入图片描述

在这里插入图片描述

五、UDP实现网络聊天室(简易版)

篇幅有限,只放gitee链接:UDP实现网络聊天室(简易版)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IDEA使用Alt + Enter快捷键自动接受返回值一直有final修饰的问题处理

在使用IDEA的过程中&#xff0c;使用快捷键Alt Enter在接收返回值时&#xff0c;可以快速完成参数接收&#xff0c;但前面一直会出现接收参数前面有final修饰的情况&#xff0c;效果如下所示&#xff1a; 看着真烦人呢&#xff0c;我们会发现在接受到返回值是上方有个 Declare…

【小白51单片机专用教程】protues仿真AT89C51入门

课程特点 无需开发板0基础教学软件硬件双修辅助入门 本课程面对纯小白&#xff0c;因此会对各个新出现的知识点在实例基础上进行详细讲解&#xff0c;有相关知识的可以直接跳过。课程涉及protues基本操作、原理图设计、数电模电、kell使用、C语言基本内容&#xff0c;所有涉及…

软件设计与体系结构

1.简要说明什么是软件体系结构&#xff0c;软件体系结构模型&#xff0c;为什么要建立软件体系结构模型&#xff1f; 答&#xff1a;软件体系结构指一个软件系统在高层次上的结构化组织方式&#xff0c;包括系统的组成部分和各个部分之间的关系&#xff0c;以及它们与环境之间的…

位置式PID-控制步进电机-位置环-stm32

基本原理 1、软件设计 本闭环控制例程是在步进电机编码器测速例程的基础上编写的,这里只讲解核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本章配套的工程。 我们创建了4个文件:bsp_pid.c和bsp_pid.h文件用来存放PID控制器相关程序,bsp_s…

汽车IVI中控开发入门及进阶(47):CarPlay开发

概述: 车载信息娱乐(IVI)系统已经从仅仅播放音乐的设备发展成为现代车辆的核心部件。除了播放音乐,IVI系统还为驾驶员提供导航、通信、空调、电源配置、油耗性能、剩余行驶里程、节能建议和许多其他功能。 ​ 驾驶座逐渐变成了你家和工作场所之外的额外生活空间。2014年,…

电力通信规约-104实战

电力通信规约-104实战 概述 104规约在广泛应用于电力系统远动过程中&#xff0c;主要用来进行数据传输和转发&#xff0c;本文将结合实际开发实例来讲解104规约的真实使用情况。 实例讲解 因为个人技术栈是Java&#xff0c;所以本篇将采用Java实例来进行讲解。首先我们搭建一…

AWS Transfer 系列:简化文件传输与管理的云服务

在数字化转型的今天&#xff0c;企业对文件传输、存储和管理的需求日益增长。尤其是对于需要大量数据交换的行业&#xff0c;如何高效、可靠地传输数据成为了一大挑战。为了解决这一难题&#xff0c;AWS 提供了一系列的文件传输服务&#xff0c;统称为 AWS Transfer 系列。这些…

动态规划<四> 回文串问题(含对应LeetcodeOJ题)

目录 引例 其余经典OJ题 1.第一题 2.第二题 3.第三题 4.第四题 5.第五题 引例 OJ 传送门Leetcode<647>回文子串 画图分析&#xff1a; 使用动态规划解决 原理&#xff1a;能够将所有子串是否是回文的信息保存在dp表中 在使用暴力方法枚举出所有子串&#xff0c;是…

stm32定时器输出比较----驱动步进电机

定时器输出比较理论 OC(Output Compare)输出比较输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形每个高级定时器和通用定时器都拥有4个输出比较通道高级定时器的前3个通道额外拥有死区生成和互补输出…

Windows电脑部署SD 3.5结合内网穿透随时随地生成高质量AI图像

文章目录 前言1. 本地部署ComfyUI2. 下载 Stable Diffusion3.5 模型3. 演示文生图4. 公网使用Stable Diffusion 3.5 大模型4.1 创建远程连接公网地址 5. 固定远程访问公网地址 前言 在数字化创意时代&#xff0c;AI技术的发展为我们带来了无限可能。尤其是对于那些追求高效和高…

Easysearch Java SDK 2.0.x 使用指南(二)

在 上一篇文章 中&#xff0c;我们介绍了 Easysearch Java SDK 2.0.x 的基本使用和批量操作。本文将深入探讨索引管理相关的功能&#xff0c;包括索引的创建、删除、开关、刷新、滚动等操作&#xff0c;以及新版 SDK 提供的同步和异步两种调用方式。 SDK 的对象构建有两种方式…

Scala——身份证号码查询籍贯

object Test_身份证查询籍贯 { def main(args: Array[String]): Unit { val code "42005200210030051".substring(0,2) println(code) //判断42是哪个省的 //湖北 // if(code "42"){ // println("42对应省份为&#xff1a;湖北") // }else…

分布式系统架构:限流设计模式

1.为什么要限流&#xff1f; 任何一个系统的运算、存储、网络资源都不是无限的&#xff0c;当系统资源不足以支撑外部超过预期的突发流量时&#xff0c;就应该要有取舍&#xff0c;建立面对超额流量自我保护的机制&#xff0c;而这个机制就是微服务中常说的“限流” 2.四种限流…

2024年11月 蓝桥杯青少组 STEMA考试 Scratch真题

2024年11月 蓝桥杯青少组 STEMA考试 Scratch真题&#xff08;选择题&#xff09; 题目总数&#xff1a;5 总分数&#xff1a;50 选择题 第 1 题 单选题 Scratch运行以下程宇后&#xff0c;小兔子会&#xff08; &#xff09;。 A. 变小 B. 变大 C. 变色 D. …

Pytorch | 从零构建ParNet/Non-Deep Networks对CIFAR10进行分类

Pytorch | 从零构建ParNet/Non-Deep Networks对CIFAR10进行分类 CIFAR10数据集ParNet架构特点优势应用 ParNet结构代码详解结构代码代码详解SSEParNetBlock 类DownsamplingBlock 类FusionBlock 类ParNet 类 训练过程和测试结果代码汇总parnet.pytrain.pytest.py 前面文章我们构…

Docker核心技术和实现原理

目录 1. Docker镜像原理介绍1.1 操作系统基础1.2 Union FS(联合文件系统)1.3 再看 Docker 镜像是什么 2. 镜像的实现原理2.1 Docker 分层存储实现原理2.2 docker 镜像加载原理 3. 镜像分层存储实战3.1 基础知识3.2 实战过程 4. overlay 文件系统工作实战5. Docker卷原理介绍5.1…

AI的进阶之路:从机器学习到深度学习的演变(二)

AI的进阶之路&#xff1a;从机器学习到深度学习的演变&#xff08;一&#xff09; 三、机器学习&#xff08;ML&#xff09;&#xff1a;AI的核心驱动力 3.1 机器学习的核心原理 机器学习&#xff08;Machine Learning, ML&#xff09;突破了传统编程的局限&#xff0c;它不再…

34.正则表达式

python正则表达式&#xff0c;使用re模块&#xff0c;模块中三个基础方法来做正则匹配。 match re.match(匹配规则&#xff0c; 被匹配的字符串) 从字符串开头进行匹配&#xff0c;匹配成功返回匹配对象&#xff08;包含匹配的信息&#xff09;&#xff0c;匹配不成功返回空。…

xpath插件安装与使用

1.背景 在使用python爬取页面数据时,经常会遇到解析页面数据,有一个非常好用的插件工具 是:xpath插件 2.安装与使用步骤 步骤1:准备xpath插件,并解压 步骤2:添加扩展程序 点击扩展程序后: 点击:加载已解压的扩展程序 安装成功后: 关闭浏览器,重新打开浏览器就可以使用了 步…

安徽医科大学卫生管理学院与和鲸科技签署“101 数智领航计划”,共拓“医学+AI”学科建设与人才培养

为进一步强化卫生健康人才培养关键方向&#xff0c;着力加强“医学AI”的复合型交叉人才培养&#xff0c;2024 年 12 月 13 日&#xff0c;安徽医科大学卫生管理学院与上海和今信息科技有限公司&#xff08;以下简称“和鲸科技”&#xff09;召开校企合作洽谈会&#xff0c;并正…