当前位置: 首页 > news >正文

【网络编程】demo版UDP网络服务器实现

文章目录

  • 一、引入
  • 二、服务端实现
    • 2.1 创建套接字socket
    • 2.2 绑定bind
    • 2.3 启动服务器
    • 2.4 IP的绑定
    • 2.5 读取数据recvfrom
  • 三、用户端实现
    • 3.1 绑定问题
    • 3.2 发送数据sendto
  • 四、源码

一、引入

在上一章【网络编程】socket套接字中我们讲述了TCP/UDP协议,这一篇就是简单实现一个UDP协议的网络服务器。
我们也讲过其实网络通信的本质就是进程间通信。而进程间通信无非就是读和写(IO)。
所以现在我们就要写一个服务端(server)接收数据,客户端(client)发送数据。

二、服务端实现

通过上一章的介绍,要通信首先需要有IP地址,和绑定端口号。

uint16_t _port;
std::string _ip;

2.1 创建套接字socket

在通信之前要先把网卡文件打开。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int socket(int domain, int type, int protocol);RETURN VALUE
On success, a file descriptor for the new socket is returned.  
On error, -1 is returned, and errno is set appropriately.

这个函数的作用是打开一个文件,把文件和网卡关联起来。

参数介绍:

domain:一个域,标识了这个套接字的通信类型(网络或者本地)。
在这里插入图片描述
只用关注上面两个类,第一个AF_UNIX表示本地通信,而AF_INET表示网络通信。
type:套接字提供服务的类型。
在这里插入图片描述
这一章我们讲的式UDP,所以使用SOCK_DGRAM
protocol:想使用的协议,默认为0即可,因为前面的两个参数决定了,就已经决定了是TCP还是UDP协议了。

返回值:

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

而从这里我们就联想到系统中的文件操作,未来各种操作都要通过这个文件描述符,所以在服务端类中还需要一个成员变量表示文件描述符。

static const std::string defaultip = "0.0.0.0";// 默认IPclass UDPServer
{
public:UDPServer(const uint16_t& port, const std::string ip = defaultip): _port(port), _ip(ip), _sockfd(-1){}void InitServer(){// 创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(_sockfd == -1){std::cerr << "socket error" << errno << " : " << strerror(errno) << std::endl;exit(1);}}
private:uint16_t _port;std::string _ip;int _sockfd;
};

创建完套接字后我们还需要绑定IP和端口号。

2.2 绑定bind

#include <sys/socket.h>int bind(int socket, const struct sockaddr *address,socklen_t address_len);RETURN VALUE
Upon successful completion, bind() shall return 0; 
otherwise, -1 shall be returned and errno set to indicate the error.

参数介绍:

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

所以我们要先定义一个sockaddr_in结构体填充数据,在传递进去。
在这里插入图片描述

struct sockaddr_in {short int sin_family;           // 地址族,一般为AF_INET或PF_INETunsigned 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);

填充端口号的时候要注意端口号是两个字节的数据涉及到大小端问题

大小端转化接口:

#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,首先我们要先转成整数,再要解决大小端问题。
系统给了直接能解决这两个问题的接口:

#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就是把一个点分十进制的字符串转化成整数再进行大小端处理。

整体代码:

void InitServer()
{// 创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(_sockfd == -1){std::cerr << "socket error" << errno << " : " << strerror(errno) << std::endl;exit(1);}// 绑定IP与portstruct 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// 绑定int n = bind(_sockfd, (struct sockaddr*)&si, sizeof si);assert(n != -1);
}

2.3 启动服务器

首先要知道服务器要死循环,永远不退出,除非用户删除。站在操作系统的角度,服务器是常驻内存中的进程。

而我们启动服务器的时候要传递进去IP和端口号。

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<UDPServer> ptr(new UDPServer(port, ip));return 0;
}

那么IP该怎么传递呢?

2.4 IP的绑定

在这里插入图片描述
这里的127.0.0.1叫做本地环回

它的作用就是用来做服务器代码测试的,意思就是如果我们绑定的IP是127.0.0.1的话,在应用层发送的消息不会进入物理层,也就不会发送出去。
在这里插入图片描述
当我们运行起来后想要查看网络情况就可以用指令netstat
后边也可以附带参数:

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

在这里插入图片描述

在这里插入图片描述
那我们如果想要全网通信呢?该用什么IP呢?难道是云服务器上的公网IP吗?
在这里插入图片描述
但是我们发现绑定不了。
因为云服务器是虚拟化服务器(不是真实的IP),不能直接绑定公网IP。

既然公网IP邦绑定不了,那么内网IP(局域网IP)呢?
在这里插入图片描述
在这里插入图片描述
答案是可以,说明这个IP是属于这个服务器的
但是这里不是一个内网的就无法找到。

所以现在的问题是服务器启动后怎么收到信息呢?(消息已经发送到主机,现在要向上交付)
实际上,一款服务器不建议指明一个IP。因为可能服务器有很多IP,如果我们绑定了一个比如说IP1,那么其他进程发送给IP2服务器就收不到了。

在这里插入图片描述
这里的INADDR_ANY实际上就是0,这样绑定后,发送到这台主机上所有的数据,只要是访问绑定的端口(8080)的,服务器都能收到。这样就不会因为绑定了一个具体的IP而漏掉其他IP的信息

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

所以现在我们就不需要传递IP了。

2.5 读取数据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代表阻塞式读取。
src_addraddrlen:输出型参数,返回对应的消息内容是从哪一个客户端发出的。第一个是自己定义的结构体,第二个是结构体长度。

void start()
{char buf[1024];while(1){struct sockaddr_in peer;socklen_t len = sizeof peer;sssize_t s = recvfrom(_sockfd, buf, sizeof buf - 1, 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);

整体代码:

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){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;}}
}

现在只需要等待用户端发送数据即可。

三、用户端实现

首先我们要发送数据,就得知道客户端的IP和port
而这里的IP就必须指明

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

这里的IP和port指的是要发送给谁。

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

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

3.1 绑定问题

这里的客户端必须绑定IP和端口,来表示主机唯一性和进程唯一性。
但是不需要显示的bind

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

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

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

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

所以操作系统会自动形成端口进行绑定。(在发送数据的时候自动绑定)
所以创建客户端我们只用创建套接字即可。

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

3.2 发送数据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);

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

void start()
{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);}
}

在这里插入图片描述
在这里插入图片描述
当然这里是同一台主机之间测试,如果是不同的机器,我们传递参数的时候就要传递公网IP,例如我们这台云服务器的公网IP是:
在这里插入图片描述
我们在运行的时候:

./UDPClient 43.143.106.44 8080

四、源码

// 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";// 默认IPclass UDPServer
{
public:UDPServer(const uint16_t& port, const std::string ip = defaultip): _port(port), _ip(ip), _sockfd(-1){}void InitServer(){// 创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(_sockfd == -1){std::cerr << "socket error" << errno << " : " << strerror(errno) << std::endl;exit(1);}// 绑定IP与portstruct 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());// ipsi.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;}}}
private:uint16_t _port;std::string _ip;int _sockfd;
};// UDPServer.cc
#include "UDPServer.hpp"
#include <memory>int main(int argc, char* argv[])
{if(argc != 2){std::cout << "incorrect number of parameters" << std::endl;exit(1);}uint16_t port = atoi(argv[1]);std::unique_ptr<UDPServer> ptr(new UDPServer(port));ptr->InitServer();ptr->start();return 0;
}// 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>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);}}void start(){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;
};// 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;
}


相关文章:

【网络编程】demo版UDP网络服务器实现

文章目录 一、引入二、服务端实现2.1 创建套接字socket2.2 绑定bind2.3 启动服务器2.4 IP的绑定2.5 读取数据recvfrom 三、用户端实现3.1 绑定问题3.2 发送数据sendto 四、源码 一、引入 在上一章【网络编程】socket套接字中我们讲述了TCP/UDP协议&#xff0c;这一篇就是简单实…...

C++的stack和queue

stack和queue 1.stackstack的模拟实现 2.queuequeue的模拟实现 3.容器适配器3.1. 什么是容器适配器3.2. STL标准库中stack和queue的底层结构3.3. deque的简单介绍3.3.1. deque原理介绍3.3.2. deque的缺陷3.3.3. 为什么选择deque作为stack和queue的底层默认容器 1.stack stack的…...

C++ RAII机制

C RAII机制 1. 介绍2. 示例2.1 示例一2.2 示例二 3. 使用 Reference&#xff1a; C RAII 浅析ChatGPT 相关文章&#xff1a; 3. C 并行编程(thread) 1. 介绍 RAII 的全称是 Resource Acquisition Is Initialization&#xff0c;它是一种编程技术&#xff0c;用于管理资源的…...

AI模型部署概述

心口如一&#xff0c;犹不失为光明磊落丈夫之行也。——梁启超 文章目录 :smirk:1. AI模型部署方法:blush:2. AI模型部署框架ONNXNCNNOpenVINOTensorRTMediapipe如何选择 :satisfied:3. AI模型部署平台 &#x1f60f;1. AI模型部署方法 在AI深度学习模型的训练中&#xff0c;…...

【Rust 日报】2023-05-17 pgx -- 用于在 Rust 中开发 PostgreSQL 扩展的框架

pgx -- 用于在 Rust 中开发 PostgreSQL 扩展的框架 pgx是一个用于在 Rust 中开发 PostgreSQL 扩展的框架&#xff0c;并力求尽可能地惯用和安全。pgx 支持 Postgres v10-v14。 主要特征: 带有 cargo-pgx 的完全托管的开发环境; # 快速创建新的扩展 cargo pgx new# 安装新的&…...

二十、Zipkin持久化链路跟踪

目录 Zipkin持久化 使用mysql数据库持久化 1、创建zipkin数据库 2、启动zipkin使用以下脚本 3、访问接口&#xff08;配置了sleuth链路跟踪&#xff09; 使用ElasticSearch持久化 zipkin启动脚本 Zipkin持久化 Zipkin server默认会将追踪数据信息保存在内存中&#xff0…...

大学毕业设计这样做可以吗

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…...

NSUserDefaults

目录 1. 简介2. 基本使用2.1 步骤2.2 对应接口2.2 示例代码 3. 常用接口及属性3.1 存储3.2 获取 4. 注意点4.1 NSUserDefaults 存储的对象全是不可变的4.2 将自定义类型数据读写 NSUserDefaults4.3 非即时写入 5.参考链接 1. 简介 NSUserDefaults是一个单例&#xff0c;在整个程…...

Windows下通过cwRsync备份到服务器服务器之间使用rsync备份传输

Windows下通过cwRsync备份到服务器&服务器之间使用rsync备份传输 Linux服务器配置Rsync服务端1、安装Rsync2、配置rsyncd.conf3、创建目录、密码文件并修改权限4、启动rsync服务 Windows配置cwRsync客户端1、下载并解压cwRsync客户端2、打开cmd&#xff0c;执行同步命令 Wi…...

IS420UCSBH4A 用于高速应用中的Mark VIe系列

控制器IS420UCSBH4A由通用电气公司设计&#xff0c;用于高速应用中的Mark VIe系列&#xff0c;采用1066 MHz EP80579英特尔处理器。该控制器可与Mark VIe系列一起用于EX2100e和LS2100e应用。 IS420UCSBH4A技术规格 系列马克维处理机类型1066兆赫EP80579英特尔型号IS420UCSBH4A…...

将JSON写入文件

首先需要引入fastjson的依赖文件&#xff0c;以下是maven的依赖&#xff1a; <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.73</version></dependency> 然后是将json字符串写到…...

effective c++ 35 考虑virtual函数以外的其他选择

effective c 35 考虑virtual函数以外的其他选择 在本节中&#xff0c;作者给出了一些可以替代调用virtual函数的方法。下面就一一进行介绍。 分析 1.考虑NVI的实现方式(模板方法设计模式) 父类和子类都调用healthValue同一接口&#xff0c;但是返回值不同。这是一种public非…...

Akura Medica:新型静脉血栓切除系统,完成首次人体试验

Akura Medical公司宣布&#xff0c;其机械血栓切除平台在人体首次使用成功&#xff0c;这是一项具有突破性的技术&#xff0c;可以有效地治疗肺栓塞、深静脉血栓等血栓栓塞疾病。该平台使用了一种与众不同的方法&#xff0c;可以高效地清除血管内的血栓&#xff0c;同时保护血管…...

大型央企集团财务经营分析框架系列(三)

01集团经营管理分析的切入点 集团经营管理分析的切入点往往是从财务分析开始。 往往在一家企业里面&#xff0c;财务方面的信息化建设是要早于其它方面的信息化建设的&#xff0c;业务标准化程度比较高&#xff0c;数据标准化程度也比较高&#xff0c;分析框架也相对成熟。 …...

C++并发编程:std::future、std::async、std::packaged_task与std::promise的深度探索

C并发编程&#xff1a;std::future、std::async、std::packaged_task与std::promise的深度探索 一、引言 (Introduction)1.1 并发编程的概念 (Concept of Concurrent Programming)1.2 C并发编程的重要性 (Importance of Concurrent Programming in C)1.3 关于std::future、std:…...

测牛学堂:2023软件测试学习教程之sql的单表查询排序和模糊查询

单表查询的排序 关键字&#xff1a;order by 排序的类型&#xff0c;升序字段&#xff1a;ASC &#xff0c;省略的话默认就是升序。 降序的字段&#xff1a;DESC 语法&#xff1a; order by 字段名 ASC| DESC返回的表则会按照给定的字段排序 例子&#xff1a;查询学生的考试成…...

CSS第一天总结

css第一天总结 css简介 CSS 是层叠样式表 ( Cascading Style Sheets ) 的简称. 有时我们也会称之为 CSS 样式表或级联样式表。 CSS 是也是一种标记语言 CSS 主要用于设置 HTML 页面中的文本内容&#xff08;字体、大小、对齐方式等&#xff09;、图片的外形&#xff08;宽高、…...

js中各种console使用方法大全

console 1.console.log() &#xff08;1&#xff09;用于标准输出流的输出&#xff0c;也就是在控制台中显示一行信息。 &#xff08;2&#xff09;当传递多个参数时&#xff0c;控制台输出时将以空格分隔这些参数。 &#xff08;3&#xff09;也可以用占位符来定义输出的格…...

江西棒球未来发展规划·棒球1号位

关于江西棒球未来发展规划: 一、总体思路 江西棒球运动要立足当前,着眼长远,切实增强鼓励支持体育运动的社会氛围,弘扬体育精神,深化体育改革,加强体育基层建设,努力建设中国棒球之乡。把打造品牌赛事和培养明星运动员作为两手抓的发展方向,不断增强江西棒球运动的整体实力和…...

【笔记】做二休五

在记录去超市购物&#xff0c;菜场买菜&#xff0c;社区团购的花费时&#xff0c;将每个物品的价格记录下来。 大家应该善加利用自己所拥有的事物&#xff0c;若勉强想要利用自己没有的&#xff0c;只会让自己痛苦。 打扫&洗衣服 小苏打是可用于家庭清洁的万能清洁剂&…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…...

django blank 与 null的区别

1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是&#xff0c;要注意以下几点&#xff1a; Django的表单验证与null无关&#xff1a;null参数控制的是数据库层面字段是否可以为NULL&#xff0c;而blank参数控制的是Django表单验证时字…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...