UDP编程流程(UDP客户端、服务器互发消息流程)

news2024/11/23 23:00:58

一、UDP编程流程

1.1、 UDP概述

UDP,即用户数据报协议,是一种面向无连接的传输层协议。相比于TCP协议,UDP具有以下特点:

  1. 速度较快:由于UDP不需要建立连接和进行复杂的握手过程,因此在传输数据时速度稍快于TCP协议。
  2. 适用于简单的请求/应答应用程序:对于一些简单的、对可靠性要求不高的应用程序,如DNS查询和SNMP请求,UDP能够提供高效的传输服务。
  3. 不适用于海量数据传输:由于UDP不提供可靠的数据传输机制,因此在进行大数据传输时容易出现丢包和乱序的情况,不建议使用UDP进行此类传输。
  4. 广播和多播应用必须使用UDP:UDP支持广播和多播传输,因此对于需要进行广播和多播的应用,如视频直播和组播通信,必须使用UDP协议。

UDP应用:

UDP协议被广泛应用于各种网络应用中,包括但不限于以下几种:

  • DNS(域名解析):DNS使用UDP进行域名解析请求和响应的传输,以提高查询速度。
  • NFS(网络文件系统):NFS使用UDP进行文件操作请求和响应的传输,以提高文件访问速度。
  • RTP(实时传输协议):RTP使用UDP进行实时音视频数据的传输,以减少延迟。

此外,一些实时性要求较高的应用,如在线游戏和VoIP通话,也会选择使用UDP协议来减少延迟,提高用户体验

1.2、网络编程接口socket

Socket,也被称为"套接字",是网络编程中用于实现不同主机上进程间通信的一种技术。它提供了一种将网络通信抽象为文件操作的接口,使得程序员可以通过简单的函数调用来实现复杂的网络通信功能。

Socket的特点

  1. 文件描述符:Socket是一种文件描述符,它代表了一个通信管道的端点。通过Socket,我们可以像操作文件一样,使用read、write、close等函数来发送和接收网络数据。
  2. 通信端点:Socket是网络通信的端点,每个Socket都有一个唯一的地址,通过这个地址,我们可以与远程主机上的Socket进行通信。
  3. 网络数据操作:通过Socket,我们可以方便地进行网络数据的发送和接收。Socket提供了丰富的函数接口,如send、recv、sendto、recvfrom等,用于各种网络数据操作。
  4. Socket函数:要获得一个Socket,我们需要调用socket()函数。该函数返回一个Socket描述符,用于后续的网络通信操作。

Socket的分类

根据使用的协议和通信方式的不同,Socket可以分为以下几种类型:

  • SOCK_STREAM:流式套接字,用于TCP协议。它提供了可靠的、面向连接的通信方式,数据以流的形式传输,可以保证数据的可靠性和顺序性。
  • SOCK_DGRAM:数据报套接字,用于UDP协议。它提供了不可靠、无连接的通信方式,数据以数据报的形式传输,不保证数据的可靠性和顺序性。
  • SOCK_RAW:原始套接字,用于其他层次的协议操作。它允许直接访问网络层数据,可以用于实现自定义的网络协议。

1.3 UDP编程C/S架构

UDP网络编程流程:

服务器: 创建套接字 socket( )

                将服务器的ip地址、端口号与套接字进行绑定 bind( )

                接收数据 recvfrom()

                发送数据 sendto()

客户端:  创建套接字 socket()

                发送数据 sendto()

                接收数据 recvfrom()

                关闭套接字 close() 

二、UDP编程-创建套接字 

2.1 创建socket套接字

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:
    创建一个套接字,返回一个文件描述符
参数:
    domain:通信域,协议族
    AF_UNIX 本地通信
    AF_INET ipv4网络协议
    AF_INET6 ipv6网络协议
    AF_PACKET 底层接口
type:
  套接字的类型
    SOCK_STREAM 流式套接字(tcp)
    SOCK_DGRAM 数据报套接字(udp)
    SOCK_RAW 原始套接字(用于链路层)
protocol:
    附加协议,如果不需要,则设置为0
返回值:
    成功:文件描述符
    失败:-1

特点 

  •  创建套接字时,系统不会分配端口
  •  创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的

 2.2举例创建套接字

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>

int main(int argc, char const *argv[]) {
    // 使用socket函数创建套接字
    // 创建一个用于UDP网络编程的套接字
    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        perror("fail to socket");
        exit(1);
    }
    printf("sockfd = %d\n", sockfd);
    return 0;
}

使用socket()函数创建了一个UDP类型的套接字,并打印出该套接字的文件描述符。如果创建套接字失败,则会输出错误信息并退出程序。

 执行结果

三、UDP编程-发送、绑定、接收数据 

3.1 IPv4套接字地址结构

在网络编程中经常使用的结构体

头文件:#include <netinet/in.h>

  struct in_addr
  { 
    in_addr_t s_addr;//ip地址 4字节
  };
struct sockaddr_in
  {
    sa_family_t sin_family;//协议族 2字节
    in_port_t sin_port;//端口号 2字节
    struct in_addr sin_addr;//ip地址 4字节
    char sin_zero[8]//填充,不起什么作用 8字节
  };

为了使不同格式地址能被传入套接字函数,地址须要强制转换成通用套接字地址结构,原因是因为不同场合所使用的结构体不一样,但是调用的函数却是同一个,所以定义一个通用结构体,当在指定场合使用时,在根据要求传入指定的结构体即可

通用结构体 sockaddr

头文件:#include 

struct sockaddr
  { 
    sa_family_t sa_family; // 2字节
    char sa_data[14] //14字节
  };

3.2 两种地址结构使用场合

在定义源地址和目的地址结构的时候,选用struct sockaddr_in;

struct  sockaddr_in  my_addr;

当调用编程接口函数,且该函数需要传入地址结构时需要用struct sockaddr进行强制转换

bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));

3.3 发送数据—sendto函数 

#include <sys/types.h>
#include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);

功能: sendto函数用于通过套接字发送数据。

参数:

  • sockfd:文件描述符,由socket函数返回。
  • buf:指向要发送数据的缓冲区的指针。
  • len:要发送的数据的长度。
  • flags:标志位,用于指定发送操作的行为。常见的标志位有:
    • 0:阻塞模式,函数会一直等待直到数据发送完成。
    • MSG_DONTWAIT:非阻塞模式,如果数据不能立即发送,函数会立即返回错误。
  • dest_addr:指向目的网络信息结构体的指针,用于指定数据发送的目标地址。
  • addrlen:目的网络信息结构体的长度。

返回值:

  • 成功:返回发送的字节数。
  • 失败:返回-1,并设置errno以指示错误原因。

sendto函数是网络编程中常用的函数之一,它允许我们向指定的目标地址发送数据。在使用该函数时,我们需要正确地指定目的地址和地址长度,以确保数据能够准确地发送到目标。同时,我们也需要注意选择合适的标志位,以满足不同的发送需求。

 3.4 向“网络调试助手”发送消息

我们用一个“网络调试助手”的软件来模拟服务器,给服务器发送消息。

客户端的代码编写

#include <stdio.h> //printf
#include <stdlib.h> //exit
#include <sys/types.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //htons inet_addr
#include <unistd.h> //close
#include <string.h> 

#define N 128

int main(int argc, char const *argv[])
{
    //./a.out 192.168.3.78 8080
    if(argc < 3)
    {
        fprintf(stderr, "Usage:%s ip port\n", argv[0]);
        exit(1);
    }

    //第一步:创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    printf("sockfd = %d\n", sockfd);

    //第二步:填充服务器网络信息结构体 sockaddr_in
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    serveraddr.sin_family = AF_INET; //协议族,AF_INET:ipv4网络协议
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //ip地址
    serveraddr.sin_port = htons(atoi(argv[2]));

    //第三步:发送数据
    char buf[N] = "";
    while(1)
    {
        fgets(buf, N, stdin);
        buf[strlen(buf) - 1] = '\0'; //把buf字符串中的\n转化为\0

        if(sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&serveraddr, addrlen) == -1)
        {
            perror("fail to sendto");
            exit(1);
        }
    }

    //第四步:关闭套接字文件描述符
    close(sockfd);

    return 0;
}

执行结果 

 3.5 绑定--bind函数

UDP网络程序想要收取数据需什么条件?

确定的ip地址

确定的port

怎样完成上面的条件呢?

接收端 使用bind函数,来完成地址结构与socket套接字的绑定,这样ip、port就固定 了 发送端 在sendto函数中指定接收端的ip、port,就可以发送数据了

 由于服务器是被动的,客户端是主动的,所以一般先运行服务器,后运行客户端,所以服务 器需要固定自己的信息(ip地址和端口号),这样客户端才可以找到服务器并与之通信,但 是客户端一般不需要bind绑定,因为系统会自动给客户端分配ip地址和端口号

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:
    将套接字与网络信息结构体绑定
参数:
    sockfd:文件描述符,socket的返回值
    addr:网络信息结构体

通用结构体(一般不用)
    struct sockaddr
网络信息结构体 
    sockaddr_in

#include <netinet/in.h>
struct sockaddr_in
addrlen:addr的长度
返回值:
    成功:0
    失败:-1

3.6 bind示例

//第二步:将服务器的网络信息结构体绑定前进行填充
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));

//第三步:将网络信息结构体与套接字绑定
if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
{
    perror("fail to bind");
    exit(1);
}

3.7 接收数据—recvfrom 函数

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);
功能:
    接收数据
参数:
    sockfd:文件描述符,socket的返回值
    buf:保存接收的数据
    len:buf的长度
    flags:标志位
        0 阻塞
        MSG_DONTWAIT 非阻塞
    src_addr:源的网络信息结构体(自动填充,定义变量传参即可)
    addrlen:src_addr的长度

返回值:
    成功:接收的字节数
    失败:-1

3.8 接收“网络调试助手”的数据

此时我们把刚刚也用到过的网络调试助手作为客户端,ubuntu的程序作为服务器

设置服务器(ubuntu程序) 

#include <stdio.h> //printf
#include <stdlib.h> //exit
#include <sys/types.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //htons inet_addr
#include <unistd.h> //close
#include <string.h> 

#define N 128

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s ip port\n", argv[0]);
        exit(1);
    }

    //第一步:创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    //第二步:将服务器的网络信息结构体绑定前进行填充
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //192.168.3.103
    serveraddr.sin_port = htons(atoi(argv[2])); //9999

    //第三步:将网络信息结构体与套接字绑定
    if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
    {
        perror("fail to bind");
        exit(1);
    }

    //接收数据
    char buf[N] = "";
    struct sockaddr_in clientaddr;
    socklen_t addrlen = sizeof(struct sockaddr_in);
    while(1)
    {
        if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&clientaddr, &addrlen) == -1)
        {
            perror("fail to recvfrom");
            exit(1);
        }

        //打印数据
        //打印客户端的ip地址和端口号
        printf("ip:%s, port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
        //打印接收到数据
        printf("from client: %s\n", buf);
    }

    return 0;
}

执行结果

 四、UDP编程-client、server

接下来我们就可以自己实现服务器与客户端互发消息了

4.1 UDP客户端

1、本地IP、本地端口(我是谁)

2、目的IP、目的端口(发给谁)

3、在客户端的代码中,我们只设置了目的IP、目的端口

客户端的本地ip、本地port是我们调用sendto的时候linux系统底层自动给客户端分配 的;分配端口的方式为随机分配,即每次运行系统给的port不一样

//udp客户端的实现
#include <stdio.h> //printf
#include <stdlib.h> //exit
#include <sys/types.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //htons inet_addr
#include <unistd.h> //close
#include <string.h>

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
        exit(1);
    }

    int sockfd; //文件描述符
    struct sockaddr_in serveraddr; //服务器网络信息结构体
    socklen_t addrlen = sizeof(serveraddr);

    //第一步:创建套接字
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("fail to socket");
        exit(1);
    }

    //客户端自己指定自己的ip地址和端口号,一般不需要,系统会自动分配
    #if 0
    struct sockaddr_in clientaddr;
    clientaddr.sin_family = AF_INET;
    clientaddr.sin_addr.s_addr = inet_addr(argv[3]); //客户端的ip地址
    clientaddr.sin_port = htons(atoi(argv[4])); //客户端的端口号
    if(bind(sockfd, (struct sockaddr *)&clientaddr, addrlen) < 0)
    {
        perror("fail to bind");
        exit(1);
    }
    #endif

    //第二步:填充服务器网络信息结构体
    //inet_addr:将点分十进制字符串ip地址转化为整形数据
    //htons:将主机字节序转化为网络字节序
    //atoi:将数字型字符串转化为整形数据
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    //第三步:进行通信
    char buf[32] = "";
    while(1)
    {
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = '\0';

        if(sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
        {
            perror("fail to sendto");
            exit(1);
        }

        char text[32] = "";
        if(recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr *)&serveraddr, &addrlen) < 0)
        {
            perror("fail to recvfrom");
            exit(1);
        }
        printf("from server: %s\n", text);
    }
    //第四步:关闭文件描述符
    close(sockfd);

    return 0;
}

4.3 UDP服务器

1、服务器之所以要bind是因为它的本地port需要是固定,而不是随机的

2、服务器也可以主动地给客户端发送数据

3、客户端也可以用bind,这样客户端的本地端口就是固定的了,但一般不这样做

//udp服务器的实现
#include <stdio.h> //printf
#include <stdlib.h> //exit
#include <sys/types.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //htons inet_addr
#include <unistd.h> //close
#include <string.h>

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
        exit(1);
    }

    int sockfd; //文件描述符
    struct sockaddr_in serveraddr; //服务器网络信息结构体
    socklen_t addrlen = sizeof(serveraddr);

    //第一步:创建套接字
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("fail to socket");
        exit(1);
    }

    //第二步:填充服务器网络信息结构体
    //inet_addr:将点分十进制字符串ip地址转化为整形数据
    //htons:将主机字节序转化为网络字节序
    //atoi:将数字型字符串转化为整形数据
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    //第三步:将套接字与服务器网络信息结构体绑定
    if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0)
    {
        perror("fail to bind");
        exit(1);
    }

    while(1)
    {
        //第四步:进行通信
        char text[32] = "";
        struct sockaddr_in clientaddr;
        if(recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr *)&clientaddr, &addrlen) < 0)
        {
            perror("fail to recvfrom");
            exit(1);
        }
        printf("[%s - %d]: %s\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), text);
        
        strcat(text, " *_*");

        if(sendto(sockfd, text, sizeof(text), 0, (struct sockaddr *)&clientaddr, addrlen) < 0)
        {
            perror("fail to sendto");
            exit(1);
        }
    }

    //第四步:关闭文件描述符
    close(sockfd);

    return 0;
}

执行的结果

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

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

相关文章

【深度学习】第二门课 改善深层神经网络 Week 2 3 优化算法、超参数调试和BN及其框架

&#x1f680;Write In Front&#x1f680; &#x1f4dd;个人主页&#xff1a;令夏二十三 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;深度学习 &#x1f4ac;总结&#xff1a;希望你看完之后&#xff0c;能对…

ASV1000视频监控平台:通过SDK接入海康网络摄像机IPC

目录 一、为何要通过SDK接入海康网络摄像机 &#xff08;一&#xff09;海康网络摄像机的SDK的功能 1、视频采集和显示 2、视频存储 3、视频回放 4、报警事件处理 5、PTZ控制 6、自定义设置 7、扩展功能 &#xff08;二&#xff09;通过SDK接入的好处&#xff08;相对…

【1小时掌握速通深度学习面试3】RNN循环神经网络

目录 12.描述循环神经网络的结构及参数更新方式&#xff0c;如何使用神经网络对序列数据建模? 13.循环神经网络为什么容易出现长期依赖问题? 14.LSTM 是如何实现长短期记忆功能的? 15.在循环神经网络中如何使用 Dropout ? 16.如何用循环神经网络实现 Seg2Seq 映射? …

2024新版Java基础从入门到精通全套教程(含视频+配套资料)

前言 Java基础是所有入门java的同学必过的一关&#xff0c;基础学习的牢固与否决定了程序员未来成就的高度。因此&#xff0c;基础学习的重要性不言而喻。 但是很多同学学习java基础知识&#xff0c;要么是学的太“基础”&#xff0c;就是只会各个知识点的简单概念和使用&…

idea 新建spring maven项目、ioc和依赖注入

文章目录 一、新建Spring-Maven项目二、在Spring-context使用IOC和依赖注入 一、新建Spring-Maven项目 在pom.xml文件中添加插件管理依赖 <build><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.1</ver…

恶补《操作系统》4_2——王道学习笔记

4.1_5 文件存储空间管理 1、存储空间的划分与初始化 文件卷&#xff08;逻辑卷&#xff09;的概念目录区与文件区 2、几种管理方法 空闲表法&#xff1a;首位置长度&#xff0c;回收时注意修改空闲链表法&#xff08;空闲盘块链、空闲盘区链&#xff09;位示图法 成组链接法…

2024年 Java 面试八股文——Mybatis篇

目录 1. 什么是Mybatis&#xff1f; 2. 说说Mybatis的优缺点 3. Xml映射文件中&#xff0c;都有哪些标签 4. #{}和&{}有什么区别 5. Mybatis是如何进行分页的,分页插件的原理是什么 6. Mybatis是如何将sql执行结果封装为目标对象并返回的&#xff1f; 7. Mybatis是怎…

JavaWeb--1.Servlet

Servlet&#xff08;基础&#xff09; 1、配置依赖&#xff1a; ​ 在pom.xml文件中加入相关依赖 <dependencies><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>5.0.0&l…

基于Python的LSTM网络实现单特征预测回归任务(TensorFlow)

目录 一、数据集 二、任务目标 三、代码实现 1、从本地路径中读取数据文件 2、数据归一化 3、创建配置类&#xff0c;将LSTM的各个超参数声明为变量&#xff0c;便于后续使用 4、创建时间序列数据 5、划分数据集 6、定义LSTM网络 &#xff08;1&#xff09;创建顺序模…

【ESP32之旅】合宙ESP32-C3 使用PlatformIO编译和Debug调试

工程创建 首先打开PIO Home窗口&#xff0c;然后点击New Project来创建新的工程&#xff0c;工程配置选择如下图所示&#xff1a; 注&#xff1a; 选择板子型号的时候需要选择ESP32C3&#xff0c;勾选取消Location可以自定义路径。 修改配置文件 工程创建完毕之后在工程根…

模式识别作业:颜色算子的三种阈值分割算法

一、引言&#xff1a; 在图像处理中&#xff0c;我们往往需要提取图像的一些关键信息&#xff0c;比如本篇文章的内容——提取颜色&#xff0c;然而当我们需要提取某一种颜色时&#xff0c;无论图像余下的部分如何“丰富多彩”&#xff0c;他们都不再重要&#xff0c;需要被忽…

C#核心之面向对象-继承

面向对象-继承 文章目录 1、继承的基本规则1、基本概念2、基本语法3、示例4、访问修饰符的影响5、子类和父类的同名成员 2、里氏替换原则1、基本概念2、is和as3、基本实现 3、继承中的构造函数1、基本概念2、父类的无参构造函数3、通过base调用指定父类构造 4、万物之父和装箱拆…

8.k8s中网络资源service

目录 一、service资源概述 二、service资源类型 1.ClusterIP类型 2.service的nodeport类型 3.service的loadbalancer类型&#xff08;了解即可&#xff09; 4.service的externalname类型&#xff08;了解即可&#xff09; 三、nodeport的端口范围设置和svc的endpoint列表 1.修…

扩散模型(Diffusion Model)概述

扩散模型&#xff08;Diffusion Model&#xff09;是图像生成模型的一种。有别于此前 AI 领域大名鼎鼎的 GAN、VAE 等算法&#xff0c;扩散模型另辟蹊径&#xff0c;其主要思想是一种先对图像增加噪声&#xff0c;再逐步去噪的过程&#xff0c;其中如何去噪还原图像是算法的核心…

web3风格的网页怎么设计?分享几个,找找感觉。

web3风格的网站是指基于区块链技术和去中心化理念的网站设计风格。这种设计风格强调开放性、透明性和用户自治&#xff0c;体现了Web3的核心价值观。 以下是一些常见的Web3风格网站设计元素&#xff1a; 去中心化标志&#xff1a;在网站的设计中使用去中心化的标志&#xff0…

RocketMQ SpringBoot 3.0不兼容解决方案

很多小伙伴在项目升级到springBoot3.0版本以上之后&#xff0c;整合很多中间件会有很多问题&#xff0c;下面带小伙伴解决springBoot3.0版本以上对于RocketMQ 不兼容问题 报错信息 *************************** APPLICATION FAILED TO START *************************** Des…

深入 Django 模型层:数据库设计与 ORM 实践指南

title: 深入 Django 模型层&#xff1a;数据库设计与 ORM 实践指南 date: 2024/5/3 18:25:33 updated: 2024/5/3 18:25:33 categories: 后端开发 tags: Django ORM模型设计数据库关系性能优化数据安全查询操作模型继承 第一章&#xff1a;引言 Django是一个基于Python的开源…

Lucene从入门到精通

**************************************************************************************************************************************************************************** 1、概述 【1】入门&#xff1a;作用、有点与缺点 【2】应用&#xff1a;索引、搜索、fie…

最短路径(朴素)+堆排序+模拟堆

文章目录 Dijkstra求最短路 I堆排序模拟堆 Dijkstra求最短路 I 给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为正值。 请你求出 1 号点到 n 号点的最短距离&#xff0c;如果无法从 1 号点走到 n 号点&#xff0c;则输出 −1。 输…

对命令模式的理解

目录 一、场景1、文本编辑器并不是一个好的例子&#xff0c;设备控制器才是2、设备控制器的demo 二、不用命令模式1、代码2、问题 三、使用命令模式1、代码2、当需求变化时2.1 新增代码2.2 优点 四、进一步思考1、省略对Command的建模可以吗&#xff1f;2、命令模式的价值 一、…