简单的 UDP 网络程序

文章目录:

  • 简单的UDP网络程序
    • 服务端创建套接字
    • 服务端绑定
    • 启动服务器
    • udp客户端
    • 本地测试
    • INADDR_ANY
  • 地址转换函数
  • 关于 inet_ntoa

简单的UDP网络程序

服务端创建套接字

我们将服务端封装为一个类,当定义一个服务器对象之后,需要立即进行初始化服务器,在 UDP 网络程序中,初始服务器的第一个步骤就是创建套接字。我们使用 socket 函数创建套接字。

socket 函数的定义如下所示:

int socket(int domain, int type, int protocol);

参数说明:

  • domain:整数,指定通信域(Communication Domain)。在同一主机上的进程之间进行通信时,我们使用 POSIX 标准定义的 AF_LOCAL。在不同的主机通过 IPv4 连接的进程之间通信时,我们使用 AF_INET,对于通过 IPv6 连接的进程之间进行通信时,我们使用 AF_INET6。
  • type:通信类型(Communication Type)。SOCK_STREAM:TCP(可靠的、面向连接的);SOCK_DGRAM:UDP(不可靠的、无连接的)。
  • protocol:互联网协议的协议值,通常为0。这与数据包的 IP 头中的协议字段中显示的数字相同。

返回值:套接字创建成功返回一个文件描述符,否则返回-1,同时错误码被设置。

在初始化服务器创建套接字时,需要调用 socket 函数创建套接字,我们需要填入的协议家族是 AF_INET(PF_INET),因为我们需要进行的是网络通信。服务器类型填入 SOCK_DGRAM ,因为编写的 UDP 服务器是面向数据报的。第三个参数设置为0,表示使用默认的传输协议。

下面是使用这些参数创建套接字的示例:

#include<iostream>
#include<sys/socket.h>class UdpServer
{
public:void init(){// 创建套接字sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){std::cerr << "Failed to create socket." << std::endl;exit(1);}std::cout << "socket create success, sockfd: " << sockfd_ << std::endl;// ......}
private:int sockfd_; // 文件描述符
};

我们对其进行一个测试,查看套接字的创建是否成功。

int main()
{UdpServer *svr=new UdpServer();svr->init();return 0;
}

运行测试结果:

在这里插入图片描述

服务端绑定

在套接字创建成功之后,需要将其与网络相关联,以便进行网络通信。对于一个 UDP 服务器,绑定操作是必要的。通过绑定,将套接字与指定的IP地址和端口号关联起来,以便监听和处理该地址上的网络数据。

使用 bind 函数将套接字与特定的IP地址和端口号进行绑定,bind 函数的定义如下:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

  • sockfd:需要绑定的套接字描述符。
  • addr:指向要绑定的地址结构的指针,需要将其转化为 struct sockaddr 类型。
  • addrlen:地址结构长度。

返回值:

  • 绑定成功返回0。失败则返回-1,且错误码被设置。

bind 函数的主要作用就是将套接字与指定的地址进行关联,以便在该地址上进行监听和处理网络数据。调用 bind 之前,需要确保套接字已经创建成功。

在绑定套接字之前,需要定义一个 struct sockaddr_in 结构,并将网络属性信息填充到该结构体中。由于 struct sockaddr_in 结构体中的一些字段是可选的,建议在填充网络信息之前先将结构体变量进行清空,然后填充协议家族、端口号、IP地址等信息。

下列示例展示了如何使用 bind 函数将套接字绑定到指定的IP地址和端口号:

class UdpServer
{
public:UdpServer(int port = 8080, std::string ip = ""): port_((uint16_t)port), ip_(ip), sockfd_(-1){}~UdpServer() {}void init(){// 1.创建套接字sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){std::cerr << "Failed to create socket." << std::endl;exit(1);}std::cout << "socket create success, sockfd: " << sockfd_ << std::endl;// 2.绑定网络信息,指明IP+port// 2.1 先填充基本信息到 struct sockaddr_instruct sockaddr_in local;      // lock在用户栈上开辟的空间 -> 临时变量 -> 写入内核中bzero(&local, sizeof(local));  // bzerolocal.sin_family = AF_INET;    // 填充协议家族,域local.sin_port = htons(port_); // 填充服务器对应的端口信息,一定会发给对方,_port一定会到网络中// local.sin_addr // 服务器都必须具有IP地址("xx.yy.zz.aaa"字符串风格点分十进制) -> 4字节IP -> uint32_t iplocal.sin_addr.s_addr = ip_.empty() ? htonl(INADDR_ANY) : inet_addr(ip_.c_str());// 2.2 绑定网络信息if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) == -1){std::cerr << "Failed to bind socket." << std::endl;exit(2);}std::cout << "Socket bound successfully." << std::endl;}private:uint16_t port_;  // 服务器必须得有端口号信息std::string ip_; // 服务器必须得有IP地址int sockfd_;     // 文件描述符
};

启动服务器

UDP 服务器的初始化在上面已经完成。服务器初始化完成之后,就可以启动服务器并提供服务了。服务器通常是以循环的方式运行的,以便持续接收和处理客户端请求。UDP 服务器在接收到客户端发送的数据后,可以直接读取这些数据,而无需建立连接。

recvfrom函数:
recvfrom 函数是在网络编程中使用的一个系统调用函数,用于从一个指定的套接字接收数据,并将数据存储到指定的缓冲区中。

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:套接字描述符,表示从该套接字描述符索引的文件中读取数据。
  • buf:指向接收数据的缓冲区。
  • len:期望读取数据的字节数。
  • flags:可选的标志参数,用于控制接收操作的行为。常用的标志有:0、MSG_DONTWAIT。
  • src_addr:指向用于存储发送方地址信息的 struct sockaddr 结构体的指针,包括协议家族、IP地址、端口号等。
  • addrlen:src_addr 结构体的长度,及其可用空间大小,这是一个输出型参数。

当使用 recvfrom 函数接收 UDP 数据时,除了获取数据内容外,还可以获取发送方的网络属性信息,包括IP地址和端口号。调用 recvfrom 函数之前,需要将 addrlen 参数设置为接收方地址结构体的大小,以确保函数可以正确填充发送方的地址信息。

接下来使用下列函数启动服务器:

当使用 recvfrom 函数读取客户端数据后,可以将读取到的数据视为字符串,并将最后一个位置设置为 ‘\0’ ,以便于进行输出。此时,我们也获取到了客户端的IP地址和端口号。recvfrom 函数调用成功之后返回的端口号是网络序列(以大端字节序表示),我们需要调用 ntohs 函数将其转换为主机序列(与本地字节序相匹配)。同样的,获取的IP地址也需要用 inet_ntoa 函数进行转化。

class UdpServer
{
public:void start(){// 服务器设计的时候都是死循环char inbuffer[1024];  // 储存读取到的数据char outbuffer[1024]; // 储存发送的数据while (true){struct sockaddr_in peer;      // 输出型参数socklen_t len = sizeof(peer); // 输入型参数ssize_t s = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&peer, &len);if (s > 0)inbuffer[s] = 0; // 将其当作字符串else if (s == -1){std::cerr << "recvfrom:" << strerror(errno) << ":" << sockfd_ << endl;continue; // 调用失败,继续进行读取,服务器不能因为一个客户端连接失败就退出}// 读取成功,除了读取到对方的数据,还要读取到对方的网络地址[ip,port]std::string peerIp = inet_ntoa(peer.sin_addr);uint32_t peerPort = ntohs(peer.sin_port);}}private:uint16_t port_;  // 服务器必须得有端口号信息std::string ip_; // 服务器必须得有IP地址int sockfd_;     // 文件描述符
};

在构建服务器时,我们可以引入命令行参数用于指定服务器的IP地址和端口号。使用云服务器,实际上不需要传入IP地址,直接运行程序时指定端口即可,这里可以将IP地址设置为(127.0.0.1),它代表本地主机或本地环回地址,相当于 localhost 。

以下为一个示例代码,演示如何通过命令行参数传递端口号,并将IP地址设置为本地环回地址:

static void Usage(const std::string proc)
{cout << "Usage:\n\t" << proc << " port [ip]" << endl;
}// ./udpServer port [ip]
int main(int argc,char *argv[])
{if(argc!=2&&argc!=3){Usage(argv[0]);return 1;}std::string ip="127.0.0.1";uint16_t port=atoi(argv[1]);UdpServer svr(port,ip);svr.init();svr.start();
}

如下所示,运行程序并加上端口号就可以创建 udp 服务器成功了:

在这里插入图片描述

使用 netstat 命令来查看当前网络的状态,下图标出的就是当前运行的 udpServer 程序:

在这里插入图片描述

netstat 是一个用于显示网络连接、路由表和网络接口等相关信息的命令。以下是 netstat 常用的选项:

  • -a (all):显示所有的sockets(包括监听和非监听的)。
  • -t (tcp):显示 TCP 协议相关的连接信息。
  • -u (udp):显示 UDP 协议相关的连接信息。
  • -n (numeric):以数字形式显示IP地址和端口号,不进行主机名和服务名的解析。
  • -p (program):显示与每个连接关联的进程/程序的 PID 和名称。
  • -l (listening):显示每个处于监听状态的 sockets。

注意:不同操作系统上的 netstat 命令可能会不同,包括选项的名称和支持的功能。因此,在使用 netstat 命令时可以通过 netstat --help 来查看系统文档使用说明。

udp客户端

udp客户端的实现步骤如下所示,用于与服务器进行 UDP 通信:

  • 参数检查:通过检查命令行参数,来确保命令行输入的参数个数是否正确,这是为了确保程序能够正确获取服务器的IP地址和端口号,避免后续出现错误。
  • 获取服务端的IP地址和端口号:根据命令行参数获取服务器的IP地址和端口号,并将它们存储在变量 server_ip 和 server_port 中。这样客户端就获取到了需要连接服务器的地址信息。
  • 创建 UDP 套接字:使用 socket 函数创建一个 UDP 套接字,即创建一个用于网络通信的套接字。AF_INET 参数表示使用 IPv4 地址,SOCK_DGRAM 参数表示使用数据报(UDP) 套接字类型。创建套接字成功之后,会返回一个文件描述符,用于后续该套接字的操作。
  • 设置服务器地址信息:UDP 通信中,需要指定连接的服务器地址信息。这里通过填充 server 结构体来实现。bzero 函数将 server 结构体清零。sin_family 表示地址族为 IPv4 ,sin_port 表示服务器的端口号,sin_addr.s_addr 表示服务器的IP地址。
// ./udpServer server_ip server_port
// 客户端要连接服务端,需要知道server对应的IP和port
class UdpClient
{
public:UdpClient(std::string ip = "", uint16_t port = 8080): ip_(ip), port_(port), sockfd_(-1){}void init(){// 创建套接字sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){std::cerr << "socket create error" << std::endl;exit(1);}}void start(){// 填写服务器对应信息struct sockaddr_in peer;bzero(&peer, sizeof peer);peer.sin_family = AF_INET;peer.sin_port = htons(port_);peer.sin_addr.s_addr = inet_addr(ip_.c_str());std::string buffer;// 启动客户端while (true){std::cout << "Please Enter# ";getline(cin, buffer);// 发送消息给serversendto(sockfd_, buffer.c_str(), buffer.size(), 0, (const struct sockaddr *)&peer, sizeof(peer));}}~UdpClient(){if (sockfd_ >= 0)close(sockfd_);}private:std::string ip_;uint16_t port_;int sockfd_;
};static void Usage(const std::string name)
{cout << "Usage:\n\t" << name << " server_ip server_port" << endl;
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(2);}// 根据命令行,设置需要访问的服务器IPstd::string server_ip = argv[1];uint16_t server_port = atoi(argv[2]);UdpClient client(server_ip, server_port);client.init();client.start();return 0;
}

sendto函数

sendto 函数用于在 UDP 通信中发送数据,它的函数定义如下:

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

参数说明:

  • sockfd:使用 socket 函数创建的套接字描述符。

  • buf:指向要发送的数据的缓冲区的指针。数据可以是任意类型的指针,通常是一个 char 数组或字符串。

  • len:要发送的数据的长度,单位是字节。

  • flags:指定发送操作的可选标志。一般使用0或与 MSG_DONTROUTE 等特殊标志进行按位或运算。有以下常用标志:
    MSG_DONTROUTE:不使用路由表发送数据,直接发送到目的地址。
    MSG_OOB:发送外带数据。
    MSG_NOSIGNAL:在发送数据时忽略 SIGPIPE 信号,避免导致进程终止。

  • dest_addr:对端网络相关的属性信息。在 UDP 通信中,该参数指向 sockaddr 结构体的指针,其中包含了目标服务器的IP地址和端口号。

  • addrlen:传入 dest_addr 结构体的长度,通常是 sizeof(struct sockaddr_in)。

本地测试

udp 的服务端和客户端的代码都已经编写完成。因此,我们可以先在本地进行测试,使用本地环回地址(127.0.0.1)作为服务器的IP地址,客户端可以连接到该地址来与服务器进行通信。同时,我们需要确保服务器绑定的端口号与客户端连接时指定的端口号一致。

在这里插入图片描述
当客户端和服务端成功建立连接并进行通信时,可以通过使用 netstat 命令来看网络信息来确认连接的建立。

在这里插入图片描述

INADDR_ANY

INADDR_ANY 是一个IP地址,当我们不想将套接字绑定到任何特定的IP时使用。在实现通信时,我们需要将套接字绑定到IP地址。当我们不知道自己及其的IP地址或者其它情况时,可以使用特殊的IP地址 INADDR_ANY。它允许我们的服务器接收任何接口作为目标的数据包。

在进行网络测试时,你需要确保服务器能够通过公网IP地址进行访问。然而,直接将服务器绑定到公网IP可能会导致绑定失败的问题。

在这里插入图片描述

在云服务器中,你所获得的公网IP地址并不一定是真正的公网IP,而是有云服务器厂商提供的内部IP地址,这些内部IP地址无法直接在服务器代码中进行绑定。

为了让服务器能够通过公网IP进行访问,可以使用一个特殊的绑定值,即 INADDR_ANY 。这个值是系统提供的宏,对应的数值为0。通过将服务器绑定到 INADDR_ANY ,就可以让服务器接收来自任意IP地址的连接请求。

若想要我们写的服务端能够被外部网络进行访问,我们可以这样做:

  1. 在服务器代码中,将与IP地址相关的代码去掉。这样可以使服务器动态绑定到可用的IP地址。
  2. 填充 struct sockaddr_in 结构体时,将IP地址设置为 INADDR_ANY 。这样服务器就可以接收到来自任意IP地址的连接请求了。
struct sockaddr_in peer;
bzero(&peer, sizeof peer);
peer.sin_family = AF_INET;
peer.sin_port = htons(port_);
peer.sin_addr.s_addr = INADDR_ANY;

注意:INADDR_ANY 的值是0,它不需要进行网络字节序的转化。因此,在设置时无需进行大小端的处理。

在这里插入图片描述

编译并运行修改后的代码时,再次使用 netstat 命令查看网络连接情况时,该服务器的IP地址变成了 0.0.0.0 ,意味着该服务器可以接收任意主机发起的连接请求:

在这里插入图片描述

地址转换函数

这里主要介绍的是 IPv4 的 socket 网络编程。sockaddr_in 中的成员 struct in_addr sin_addr 表示32位的IP地址。但是我们通常使用点分十进制的字符串表示IP地址,以下函数可以在字符串表示和 in_addr 表示之间进行转换:

字符串转 in_addr 的函数:

#include <arpa/inet.h>// 将字符串cp表示的IP地址转换为in_addr结构体,并将结果存储在inp中
int inet_aton(const char *cp, struct in_addr *inp);// 将字符串cp表示的IP地址转换为in_addr_t类型的值
in_addr_t inet_addr(const char *cp);// 将字符串从src表示的IP地址转换为指定地址族af的二进制表示,并将转换后的结果存储在dst中
int inet_pton(int af, const char *src, void *dst);

in_addr转字符串的函数:

// 将in_addr结构体中的IP地址转换为字符串形式
char *inet_ntoa(struct in_addr in);// 将指定地址族af的二进制表示src转化为字符串形式,并将结果存储在dst中
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

其中 inet_pton 和 inet_ntop 不仅可以转换 IPv4 的 in_addr ,还可以转换 IPv6 的 in6_addr 因此函数接口是 void *addrptr 。

关于 inet_ntoa

inet_ntoa 这个函数的返回值类型是 char*,很显然这个函数在自己内部申请了一块内存来报错IP结果,那么是否需要调用者手动释放呢?

在这里插入图片描述
man 手册上说,inet_ntoa 函数,是把这个结果放在了静态存储区。这时不需要我们进行手动释放。那么,若多次调用该函数,会有什么样的效果呢?

请看代码:

#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;int main()
{struct sockaddr_in addr1;struct sockaddr_in addr2;addr1.sin_addr.s_addr = 0;addr2.sin_addr.s_addr = 0xffffffff;char *ptr1 = inet_ntoa(addr1.sin_addr);char *ptr2 = inet_ntoa(addr2.sin_addr);cout << "ptr1:" << ptr1 << "  ptr2:" << ptr2 << endl;return 0;
}

运行结果如下所示:

在这里插入图片描述

由于 inet_ntoa 函数使用了静态缓冲区,每次调用该函数时,返回的指针都会指向同一个缓冲区。这意味着如果在多个地方同时使用了 inet_ntoa 返回值,并且后续的调用覆盖了之前的结果,那么之前获取的字符串指针将变为无效。

思考:如果有多个线程调用 inet_ntoa,是否会出现异常情况呢?

  • 在 APUE 中,明确提出了 inet_ntoa 不是线程安全的函数。因为它使用了一个全局共享的缓冲区来保存转化后的字符串。所以多线程环境下,该函数可能会导致竞争条件,从而出错。
  • 但是在 centos7 上测试,并没有出现问题,这可能是在内部加了互斥锁来保证线程安全性。
  • 在多线程环境下,推荐使用 inet_ntop 函数,它是线程安全的。inet_ntop 函数要求调用者提供一个缓冲区来存储转换后的字符串,避免了静态缓冲区的共享和竞争条件,可以规避线程安全的问题。

多线程调用 inet_ntoa 代码示例如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>void *Func1(void *p)
{struct sockaddr_in *addr = (struct sockaddr_in *)p;while (1){char *ptr = inet_ntoa(addr->sin_addr);printf("addr1: %s\n", ptr);}return NULL;
}
void *Func2(void *p)
{struct sockaddr_in *addr = (struct sockaddr_in *)p;while (1){char *ptr = inet_ntoa(addr->sin_addr);printf("addr2: %s\n", ptr);}return NULL;
}int main()
{pthread_t tid1 = 0;struct sockaddr_in addr1;struct sockaddr_in addr2;addr1.sin_addr.s_addr = 0;addr2.sin_addr.s_addr = 0xffffffff;pthread_create(&tid1, NULL, Func1, &addr1);pthread_t tid2 = 0;pthread_create(&tid2, NULL, Func2, &addr2);pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}

在该程序测试中并没有出现问题,但这并不意味着 inet_ntoa 函数在多线程环境中是安全的。为了确保线程安全,仍然建议使用线程安全的函数 inet_ntop 来替代 inet_ntoa 。

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

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

相关文章

【uniapp/uview1.x】u-upload 在 v-for 中的使用时, before-upload 如何传参

引入&#xff1a; 是这样一种情况&#xff0c;在接口获取数据之后&#xff0c;是一个数组列表&#xff0c;循环展示后&#xff0c;需要在每条数据中都要有图片上传&#xff0c;互不干扰。 分析&#xff1a; uview 官网中有说明&#xff0c;before-upload 是不加括号的&#xff…

《QT从基础到进阶·三十三》QT插件开发QtPlugin

插件和dll区别&#xff1a; 插件 插件主要面向接口编程&#xff0c;无需访问.lib文件&#xff0c;热插拔、利于团队开发。即使在程序运行时.dll不存在&#xff0c;也可以正常启动&#xff0c;只是相应插件功能无法正常使用而已&#xff1b; 调用插件中的方法只要dll即可&#x…

Ajax 之XMLHttpRequest讲解

一直以来都听别人说Ajax,今天终于接触到了。。。。。。。。。。 一.什么是Ajax? 答: AJAX即“Asynchronous Javascript And XML”&#xff08;异步JavaScript和XML&#xff09;&#xff0c;是指一种创建交互式网页应用的网页开发技术。 AJAX 异步 JavaScript和XML&#x…

Linux(2):初探

Linux 是什么 Linux 就是一套操作系统。Linux 就是核心与系统呼叫接口那两层。 应用程序不算 Linux。 Linux 提供了一个完整的操作系统当中最底层的硬件控制与资源管理的完整架构&#xff0c; 这个架构是沿袭Unix 良好的传统来的&#xff0c;相当的稳定而功能强大。 在 Lin…

CPD:使用restAPI和cpd-cli命令创建DMC实例

环境 Red Hat Enterprise Linux release 8.6 (Ootpa)OCP 4.12.22IBM CP4D 4.8.0Data Management Console 3.1.12 (DMC for CPD 4.8.0) 注&#xff1a;使用了fyre VM。 创建DMC实例 准备 首先export环境变量&#xff1a; . ./stg_env.sh把 cpd-cli 放到PATH里。编辑 ~/.ba…

【网络奇遇记】那年我与计算机网络的初相识 —— 网络的体系结构

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. 常见的三种计算机网络体系结构1.1 开放系统互连参考模型1.2 TCP/IP参考模型1.3 原理参考模型 二…

k8s pod常用运维命令

1. 概述 kubectl 命令是操作 Kubernetes 集群的最直接和最高效的途径&#xff0c;熟练掌握命令的使用能起到事半功倍的效果&#xff0c;整理命令有助于加深记忆&#xff0c;该文仅记录关于pod常用的操作运维命令。 2. 查看namespaces 查看k8s集群中目前存在的namespaces kub…

【设计一个缓存--针对各种类型的缓存】

设计一个缓存--针对各种类型的缓存 1. 设计顶层接口2. 设计抽象类 -- AbstractCacheManager3. 具体子类3.1 -- AlertRuleItemExpCacheManager3.2 -- AlertRuleItemSrcCacheManager 4. 类图关系 1. 设计顶层接口 // 定义为一个泛型接口,提供给抽象类使用 public interface Cach…

Centos7安装Redis

1.背景 2.步骤 1.安装gcc依赖 检查是否已安装gcc gcc -v 上图表示已安装,如果没有安装执行如下命令 [rootlocalhost local]# yum install -y gcc 2.下载并解压安装包 [rootlocalhost local]# wget http://download.redis.io/releases/redis-5.0.3.tar.gz [rootlocalhost lo…

《011.SpringBoot之餐厅点餐系统》

《011.SpringBoot之餐厅点餐系统》【界面简洁功能简单】 项目简介 需要源码及数据库的私信… [1]本系统涉及到的技术主要如下&#xff1a; 推荐环境配置&#xff1a;DEA jdk1.8 Maven MySQL 前后端分离; 后台&#xff1a;SpringBootMybatisPlus; 前台&#xff1a;Layuivue; …

【python】OpenCV—Image Pyramid(8)

文章目录 1 图像金字塔2 拉普拉斯金字塔 1 图像金字塔 高斯金字塔 在 OpenCV 中使用函数 cv2.pyrDown()&#xff0c;实现图像高斯金字塔操作中的向下采样&#xff0c;使用函数 cv2.pyrUp() 实现图像金字塔操作中的向上采样 import cv2img cv2.imread(C://Users/Administrat…

Mysql中的进阶增删查改操作(二)

联合查询和合并查询 一.联合查询1.内连接2.外链接2.1左外连接2.2右外连接 3.自连接4.子查询5.合并查询 一.联合查询 步骤 1.进行笛卡尔积 2.列出连接条件 3.根据需求再列出其他条件 4.针对列进行精简(可以使用聚合函数) 我们先搭建一个多表查询的框架 这样一个多表查询就搭建出…

基于JAVA SpringBoot和HTML美食网站博客程序设计

摘要 美食网站是一个提供各种美食信息和食谱的网站&#xff0c;旨在帮助用户发现、学习和分享美食。旨在探讨美食网站在现代社会中的重要性和影响。随着互联网的普及&#xff0c;越来越多的人开始使用美食网站来获取各种美食信息和食谱。这些网站不仅提供了方便快捷的搜索功能&…

MySQL8.0学习笔记

1. CMD命令 1.1 数据库启动与停止 (1) 启动数据库&#xff1a;net start mysql80 (2) 停止数据库&#xff1a;net stop mysql80 1.2 数据库连接与退出 (1) 连接数据库&#xff1a;mysql [-hlocalhost -P3306] -uroot -p[123456] // 本地数据库可省略-h -P (2) 退出数据库…

在市场发展中寻变革,马上消费金融树行业发展“风向标”

11月11日&#xff0c;2023金融街论坛年会第三届全球金融科技大会“金融科技创新与合规安全”平行论坛在北京召开。会上&#xff0c;马上消费金融副总经理孙磊就数据对金融的赋能作用、数据安全治理等方面展开了深度讨论。 公开信息显示&#xff0c;马上消费金融是一家经中国银保…

CodeWhisperer--手把手教你使用一个十分强大的工具

Amazon CodeWhisperer 是一款能够帮助我们智能生成代码的工具。经过数十亿行代码的训练&#xff0c;可以根据提示和现有代码实时生成从片段到完整功能的代码建议。类似 Cursor 和 Github Copilot 编码工具。目前&#xff0c;CodeWhisperer 兼容 Python、Java 和 JavaScript&…

mysql数据模型

创建数据库 命令 create database hellox &#xff1a; &#xff08; hellox名字&#xff09; sql语句 创建 数据库 命令 create database hell&#xff1b; 也是创建但是有数据库不创建 命令 create database if not exists hell ; 切换数据库 命令 use hello&…

2023解析企业数据中台:突破数据孤岛,实现数据化管理升级-亿发

当前&#xff0c;各大企业纷纷将业务中台、数据中台、安全中台等纳入建设计划&#xff0c;其中&#xff0c;数据中台被视为重中之重。但是&#xff0c;对于初接触者而言&#xff0c;对数据中台的定义可能存在一些模糊。 下面我们将讨论和讲解对企业建设数据中台的3点建议&#…

小型机房380V断电报警门磁开关状态检测远程控制RTU

在现代社会中&#xff0c;小型机房起到了至关重要的作用&#xff0c;为各种系统和设备提供稳定的电力供应。然而&#xff0c;由于各种原因&#xff0c;如供电故障、设备故障或非法侵入等&#xff0c;机房的正常运行可能会受到威胁。为了保障机房的安全和可靠性&#xff0c;我们…