网络基础概念和 socket 编程
网络基础概念和 socket 编程
学习目标:
- 了解 OSI 七层模型、TCP/IP 四层模型结构
- 了解常见的网络协议格式
- 掌握网络字节序和主机字节序之间的转换
- 理解 TCP 服务器端通信流程
- 理解 TCP 客户端通信流程
- 实现 TCP 服务器端和客户端的代码
推荐一个非常好的学习资料仓库
协议
协议的概念
协议是事先约定好,大家共同遵守的一组规则,如交通信号灯。从应用的角度出发,协议可理解为“规则”,是数据传输和数据的解释的规则,可以简单的理解为各个主机之间进行通信所使用的共同语言。
假设,主机 A、主机 B 双方欲传输文件,规定:
- 第一次,传输文件名,接收方接收到文件名,应答 OK 给传输方
- 第二次,发送文件的尺寸,接收方接收到该数据再次应答一个 OK
- 第三次,传输文件内容,接收方接收数据完成后应答 OK 表示文件内容接收成功
由此,无论 A、B 之间传递何种文件,都是通过三次数据传输来完成。A、B 之间形成了一个最简单的数据传输规则。双方都按此规则发送、接收数据。A、B 之间达成的这个相互遵守的规则即为协议。
这种仅在 A、B 之间被遵守的协议称之为原始协议。当此协议被更多的人采用,不断的增加、改进、维护、完善。最终形成一个稳定的、完整的文件传输协议,被广泛应用于各种文件传输过程中。该协议就成为一个标准协议。最早的 ftp 协议就是由此衍生而来。
典型的协议
实际生活中有以下几种常见的协议:
- 应用层常见的协议有 HTTP 协议、FTP 协议等
- HTTP:超文本传输协议(Hyper Text Transfer Protocol),是互联网上应用最广泛的一种网络协议
- FTP:文件传输协议(File Transfer Protocol)
- 传输层常见的协议有 TCP/UDP 协议
- TCP:传输控制协议(Transmission Control Protocol),是一种面向连接的、可靠的、基于字节流的传输层通信协议
- UDP:用户数据协议(User Datagram Protocol),是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务
- 网络层常见的协议有 IP 协议、ICMP 协议、IGMP 协议
- IP:因特网互联协议(Internet Protocol)
- ICMP:Internet 控制报文协议(Internet Control Message Protocol),是 TCP/IP 协议族的一个子协议,用于在 IP 主机、路由器之间传递控制消息
- IGMP:Internet 组管理协议(Internet Group Management Protocol),是因特网协议家族中的一个组播协议,该协议运行在主机和组播路由器之间
- 网络接口层常见的协议有 ARP 协议、RARP 协议
- ARP:正向地址解析协议(Address Resolution Protocol),通过已知的 IP,寻找对应主机的 MAC 地址
- RARP:反向地址转换协议,通过 MAC 地址确定 IP 地址
网络模型
OSI 七层模型
OSI 七层模型是国际标准组织制定的 OSI 理论模型,该模型定义了不同计算机互联的标准, 是设计和描述计算机网络通信的基本框架。七层模型分别是以下几个(从上向下):
- 应用层:是最靠近用户的 OSI 层,这一层为用户的应用程序(例如电子邮件、文件传输和终端仿真)提供网络服务
- 表示层:可确保一个系统的应用层所发送的信息可以被另一个系统的应用层读取。例如,PC 程序与另一台计算机进行通信,其中一台计算机使用扩展二一十进制交换码(EBCDIC),而另一台则使用美国信息交换标准码(ASCII)来表示相同的字符。如有必要,表示层会通过使用一种通格式来实现多种数据格式之间的转换
- 会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)
- 传输层:定义了一些传输数据的协议和端口号(WWW 端口 80 等),主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组,常常把这一层数据叫做段
- 网络层:在位于不同地理位置的网络中的两个主机系统之间提供连接和路径选择。Internet 的发展使得从世界各站点访问信息的用户数大大增加,而网络层正是管理这种连接的层
- 数据链路层:定义了如何让格式化数据以帧为单位进行传输,以及如何让控制对物理介质的访问。这一层通常还提供错误检测和纠正,以确保数据的可靠传输。如:串口通信中使用到的 115200、8、N、1
- 物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由 1、0 转化为电流强弱来进行传输,到达目的地后再转化为 1、0,也就是我们常说的数模转换与模数转换),这一层的数据叫做比特
TCP/IP 四层模型
在实际生产开发中,讨论更多的是 TCP/IP 四层模型,这是对七层模型的简化
TCP/IP 网络协议栈分为应用层(Application)、传输层(Transport)、网络层(Network)和链路层(Link)四层,如下图所示:
数据通信过程
数据的通信过程本质上在发送方是一个层层打包的过程,在接收方是一个层层解包的过程,这种打包的过程是内核帮我们完成。如下图所示,PC 机 A 通过网络向 PC 机 B 发送数据的过程
网络应用程序设计模式
目前使用最多的设计模式就两种 C/S 和 B/S:
- C/S:客户端和服务器模式,需要在通讯两端各自部署客户机和服务器来完成数据通信
- 优点:客户端在本机上可以保证性能,可以将数据缓存到本地,提高数据的传输效率,提高用户体验效果;客户端和服务端程序都是由同一个开发团队开发,协议选择比较灵活
- 缺点:服务器和客户端都需要开发,工作量相对较大,调试困难,开发周期长;从用户的角度看,需要将客户端安装到用户的主机上,对用户主机的安全构成威胁
- B/S:浏览器和服务器模式,只需在一端部署服务器,而另一端使用浏览器即可完成数据传输
- 优点:无需安装客户端,可以使用标注你的浏览器作为客户端;只需要开发服务器,工作量相对较小;由于采用标准的客户端,所以移植性好,不受平台限制;相对安全,不用安装软件
- 缺点:由于没有客户端,数据缓冲不尽人意,数据传输有限制,用户体验较差;通信协议选择只能使用 HTTP 协议,协议选择不够灵活
以太网帧
以太网帧格式就是包装在网络接口层(数据链路层)的协议,具体格式如下:
以 ARP 为例,其协议格式具体如下:
源 MAC 地址、目的 MAC 地址在以太网首部和 ARP 请求中各出现一次,对于链路层为以太网的情况是多余的,但如果链路层是其它类型的网络则有可能是必要的。硬件类型指链路层网络类型,1 为以太网,协议类型指要转换的地址类型,0x0800 为 IP 地址,后面两个地址长度对于以太网地址和IP地址分别为 6 和 4(字节),op 字段为 1 表示 ARP 请求,op 字段为 2 表示 ARP 应答。
假设现在 PC 机 A 向 PC 机 B 发送请求,简单的流程如下:
- PC 机 A 将本机的 MAC 地址填入源地址,以广播的方式发送 ARP 报文,因此数据报文的格式为
- 以太网首部(14字节):0000: ff ff ff ff ff ff 00 05 5d 61 58 a8 08 06 —— 目的主机采用广播地址,源主机的 MAC 地址是00:05:5d:61:58:a8,上层协议类型 0x0806 表示 ARP
- ARP 帧(28 字节):
- 0000: 00 01 —— 硬件类型 0x0001 表示以太网
- 0010: 08 00 06 04 00 01 00 05 5d 61 58 a8 c0 a8 00 37 —— 协议类型 0x0800 表示 IP 协议,硬件地址(MAC地址)长度为 6,协议地址(IP地址)长度为 4,op 为 0x0001 表示请求目的主机的 MAC 地址,源主机 MAC 地址为 00:05:5d:61:58:a8,源主机 IP 地址为 c0 a8 00 37(192.168.0.55)
- 0020: 00 00 00 00 00 00 c0 a8 00 02 —— 目的主机 MAC 地址全 0 待填写,目的主机 IP 地址为 c0 a8 00 02(192.168.0.2)
- 填充位(18 字节):由于以太网规定最小数据长度为46字节,ARP帧长度只有28字节,因此有18字节填充位,填充位的内容没有定义,与具体实现相关
- 0020: 00 77 31 d2 50 10
- 0030: fd 78 41 d3 00 00 00 00 00 00 00 00
- PC 机 B 收到 ARP 数据报文后,发送应答
- 以太网首部(14字节):0000: 00 05 5d 61 58 a8 00 05 5d a1 b8 40 08 06 —— 目的主机的 MAC 地址是00:05:5d:61:58:a8,源主机的 MAC 地址是 00:05:5d:a1:b8:40,上层协议类型 0x0806 表示 ARP
- ARP 帧(28 字节):
- 0000: 00 01 —— 硬件类型 0x0001 表示以太网
- 0010: 08 00 06 04 00 02 00 05 5d a1 b8 40 c0 a8 00 02 —— 协议类型 0x0800 表示 IP 协议,硬件地址(MAC地址)长度为 6,协议地址(IP地址)长度为 4,op 为 0x0002 表示应答,源主机 MAC 地址为 00:05:5d:a1:b8:40,源主机 IP 地址为 c0 a8 00 02(192.168.0.2)
- 0020: 00 05 5d 61 58 a8 c0 a8 00 37 —— 目的主机MAC地址为 00:05:5d:61:58:a8,目的主机 IP 地址为c0 a8 00 37(192.168.0.55)
- 填充位(18 字节):
- 0020: 00 77 31 d2 50 10
- 0030: fd 78 41 d3 00 00 00 00 00 00 00 00
其他的数据包格式也以差不多的方式进行发送和应答。
注意:通过 IP 地址可以确定同一网段中唯一的一台主机,主机使用端口号来区分不同的应用程序。
socket 编程
传统的进程间通信借助内核提供的IPC机制进行,但是只能限于本机通信,若要跨机通信,就必须使用网络通信(本质上借助内核-内核提供了 socket
伪文件的机制实现通信——实际上是使用文件描述符),这就需要用到内核提供给用户的 socket API 函数库。
网络字节序
在进行网络通信时,一定要注意数据的字节序问题,如果不使用同一的字节序,发送/接收的数据可能是错误的。网络字节序分为两种:
- 大端字节序:低地址存放高位数据,高地址存放低位数据
- 小段字节序:低地址存放低位数据,高地址存放高位数据
如何确定本机上是大端还是小段,代码测试实例如下:
#include <stdio.h>union {short sval;char cval[sizeof(short)];
} u1;union {int ival;char cval[sizeof(int)];
} u2;int main() {u1.sval = 0x0102;// cval[0] 中存放的是 0x01 则是大端字节序,否则则是小端字节序printf("u1.sval = %#x, u1.cval[0] = %#x, u1.cval[1] = %#x\n", u1.sval, u1.cval[0], u1.cval[1]);u2.ival = 0x01020304;printf("u2.ival = %#x, u2.cval[0] = %#x, u2.cval[1] = %#x, u2.cval[2] = %#x, u2.cval[3] = %#x\n", u2.ival, u2.cval[0], u2.cval[1], u2.cval[2], u2.cval[3]);return 0;
}
在网络传输中使用的是大端字节序,如果机器用的是小端法,则需要进行大小端的转换。一般使用以下 4 个函数:
#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);
其中 h
表示主机 host,n
表示网络 network,s
表示 short
,l
表示 long
。
socket 编程的 API 函数
socket
函数
socket
函数用来创建一个网络套接字
#include <sys/types.h>
#include <sys/socket.h>/*** @param:* domain: 协议版本,有 AF_INET 表示 IPV4,AF_INET6 表示 IPV6 等* type: 协议类型,主要有 SOCK_STREAM 和 SOCK_DGRAM,分别表示 TCP 和 UDP* protocol: 一般填 0, 表示使用对应类型的默认协议* @return: 成功返回大于 0 的文件描述符,失败返回 -1*/
int socket(int domain, int type, int protocol);
当调用 socket
函数以后,返回一个文件描述符,内核会提供与该文件描述符相对应的读和写缓冲区,同时还有两个队列,分别是请求连接队列和已连接队列。
bind
函数
bind
函数将 socket
创建的文件描述符与 IP,port 绑定
#include <sys/types.h>
#include <sys/socket.h>/*** @param:* sockfd: 调用 socket 函数返回的文件描述符* addr: 本地服务器的 IP 地址和 PORT* addrlen: addr 变量的占用的内存大小* @return: 成功返回 0,失败返回 -1*/
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockaddr
是一个保存 IP 和 PORT 的结构体,其原型如下:
struct sockaddr {sa_family_t sa_family;char sa_data[14];
}
由于 sockaddr
在保存地址数据的时候比较繁琐,有了 sockaddr_in
结构,其结构原型如下:
struct sockaddr_in {sa_family_t sin_family; /* address family: AF_INET */in_port_t sin_port; /* port in network byte order */struct in_addr sin_addr; /* internet address */
};/* Internet address. */
struct in_addr {uint32_t s_addr; /* address in network byte order */
};
在实际的使用中,使用更多的是 sockaddr_in
结构类型,传参时进行类型转换即可,这两个结构体的大小是一样的。
listen
函数
listen
函数将套接字由主动改为被动
#include <sys/types.h>
#include <sys/socket.h>/*** @param:* sockfd: 调用 socket 函数返回的文件描述符* backlog: 同时请求连接的最大来凝结个数(进入连接队列)* @return: 成功返回 0,失败返回 -1*/
int listen(int sockfd, int backlog);
accept
函数
accept
函数从连接队列中获取一个连接,如何连接队列中没有连接则会阻塞等待
#include <sys/types.h>
#include <sys/socket.h>/*** @param:* sockfd: 调用 socket 函数返回的文件描述符* addr: 传出参数,保存客户端的地址信息* addrlen: 传入传出参数,addr 变量所占内存空间大小* @return: 成功返回获取连接客户端的文件描述符,失败返回 -1*/
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
内核会负责将请求队列中的连接拿到已连接队列中。
connect
函数
connect
函数是主动向指定的 IP 和 PORT 地址发送连接请求。
#include <sys/types.h>
#include <sys/socket.h>/*** @param:* sockfd: 调用 socket 函数返回的文件描述符* addr: 服务端的地址信息* addrlen: addr 变量所占内存空间大小* @return: 成功返回 0,失败返回 -1*/
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
在发送请求连接前,需要确定服务器的地址信息,其中包括以点分十进制表示的 IP 地址,将其转换成网络字节序的 IP 地址方式使用以下的函数:
#include <arpa/inet.h>/*** @description: 将 IPV4 或 IPV6 的地址从点分十进制的 IP 转换为网络字节序* @param:* af: AF_INET 或 AF_INET6* src: 字符串形式的点分十进制的 IP 地址* dst: 存放转换后的变量的地址* @return: 成功返回指向 dst 的指针,失败返回 NULL*/
int inet_pton(int af, const char *src, void *dst);/*** @description: 将 IPV4 或 IPV6 的地址从网络字节序转换为点分十进制的 IP* @param:* af: AF_INET 或 AF_INET6* src: 网络的整形的 IP 地址* dst: 转换后的 IP 地址,一般为字符串数组* size: dst 的长度* @return: 成功返回指向 dst 的指针,失败返回 NULL*/
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
服务器端和客户端的开发流程
使用 socket 的 API 函数编写服务端和客户端程序的步骤图示
服务器端的实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>#define BUFFERSIZE 1024int main(int argc, char *argv[]) {if (2 != argc) {fprintf(stderr, "Usage: %s <port>\n", argv[0]);exit(EXIT_FAILURE);}// 创建套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == sfd) {perror("socket() error");exit(EXIT_FAILURE);}// 绑定 IP 和 PORTstruct sockaddr_in serv_addr;serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(atoi(argv[1]));if (-1 == bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {perror("bind() error");close(sfd);exit(EXIT_FAILURE);}// 将套接字由主动态变为被动态if (-1 == listen(sfd, 2)) {close(sfd);perror("listen() error");exit(EXIT_FAILURE);}// 从连接获得一个连接,没有连接则阻塞等待,连接队列取完则退出struct sockaddr_in clnt_addr;socklen_t addr_len = sizeof(clnt_addr);int cfd = accept(sfd, (struct sockaddr *)&clnt_addr, &addr_len);if (-1 == cfd) {close(sfd);perror("accept() error");exit(EXIT_FAILURE);}int rlen;char message[BUFFERSIZE] = {0};// 开始读取和发送数据while (1) {memset(message, 0, BUFFERSIZE);rlen = read(cfd, message, BUFFERSIZE);if (0 == rlen) {printf("client %d is disconnected\n", cfd);break;} else if (0 > rlen) {perror("read() error");break;}printf("READ: %s", message);write(cfd, message, rlen);}close(cfd);close(sfd);return 0;
}
客户端的实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>#define BUFFERSIZE 1024int main(int argc, char *argv[]) {if (3 != argc) {fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);exit(EXIT_FAILURE);}// 创建套接字int cfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == cfd) {perror("socket() error");exit(EXIT_FAILURE);}// 向服务器端发送连接请求struct sockaddr_in clnt_addr;clnt_addr.sin_family = AF_INET;inet_pton(AF_INET, argv[1], &clnt_addr.sin_addr.s_addr);clnt_addr.sin_port = htons(atoi(argv[2]));if (-1 == connect(cfd, (struct sockaddr *)&clnt_addr, sizeof(clnt_addr))) {close(cfd);perror("connect() error");exit(EXIT_FAILURE);}char message[BUFFERSIZE] = {0};while (1) {memset(message, 0, BUFFERSIZE);printf("Please input message(q/Q to quit): ");fgets(message, BUFFERSIZE-1, stdin);if (!strcmp(message, "Q\n") || !strcmp(message, "q\n"))break;int wlen = write(cfd, message, sizeof(message));printf("WRITE: %s", message);int rlen = read(cfd, message, BUFFERSIZE);if (rlen < 0) {perror("read() error");break;}printf("READ: %s", message);}close(cfd);return 0;
}
相关文章:

网络基础概念和 socket 编程
网络基础概念和 socket 编程 学习目标: 了解 OSI 七层模型、TCP/IP 四层模型结构了解常见的网络协议格式掌握网络字节序和主机字节序之间的转换理解 TCP 服务器端通信流程理解 TCP 客户端通信流程实现 TCP 服务器端和客户端的代码 推荐一个非常好的学习资料仓库 协…...

TypeScript 中的接口、泛型与自定义类型
TypeScript 是一种超集语言,它为 JavaScript 添加了静态类型检查。通过 TypeScript,开发者可以获得更好的工具支持,并且能够编写出更加健壮的代码。本文将探讨 TypeScript 中的几个关键特性:接口、泛型以及如何创建自定义类型。 …...

常州威雅学校:跟随这场音乐盛宴,溯回她的音乐之路
时同学 常州威雅2021届毕业生 英国皇家北方音乐学院 钢琴系 西太湖畔清凉的晚风送来阵阵悦耳的钢琴声,时同学在母校的个人钢琴独奏悄然拉开序幕。这是她自毕业三年后,在常州威雅的首场钢琴独奏会。 随着第一个音符落下,她用手指在黑白键盘…...

【YashanDB知识库】由于hist_head$中analyze time小于tab$中analyze time导致的sql语句执行慢
本文内容来自YashanDB官网,具体内容请见https://www.yashandb.com/newsinfo/7459465.html?templateId1718516 问题现象 某局点yashandb cpu使用率100%,经线上分析是由于几个sql执行慢,其中一个sql为简单的单行等值绑定变量过滤排序。 经分…...

【有啥问啥】深度理解主动学习:机器学习的高效策略
深度理解主动学习:机器学习的高效策略 在大数据时代,数据量的爆炸性增长与有限的标注资源之间的矛盾日益凸显。如何高效地利用标注资源来训练高质量的模型,成为了机器学习领域亟待解决的问题。主动学习(Active Learning, AL&…...

智能守护者X100 - 自动化生产线智能机器人安全监控管理系统
1.产品介绍 产品名称: 智能守护者X100 - 自动化生产线智能机器人安全监控管理系统 主要功能: 全方位实时监控:智能守护者X100采用高清摄像头与红外夜视技术,实现对自动化生产线及智能机器人的360无死角监控。系统能自动识别并追踪生产线上的机器人活动轨迹,确保生产安全…...

harmonyos面试题
你在项目中用过线程通信吗,线程是怎么进行通信的? 页面的生命周期有哪些? UAbility的生命周期呢? 你在项目中使用首选项主要用来数什么 组件通信的方式有息些 弹室UI是怎么在页面UI中使用的 常用的修饰符有愿些介绍一下 缓冲区是什么与16进制和数组有什么关…...

神经网络介绍及其在Python中的应用(一)
作者简介:热爱数据分析,学习Python、Stata、SPSS等统计语言的小高同学~ 个人主页:小高要坚强的博客 当前专栏:Python之机器学习 本文内容:神经网络介绍及其在Python中的线性回归应用 作者“三要”格言:要坚…...

数据流处理技术与Flink框架
一数据流 数据流定义: 数据流(Data Stream)是指数据以连续不断的方式到达和处理的序列。在现实世界中,许多数据来源都是以流的形式存在,比如: 1. 用户行为:用户在网站上的点击流、移动应用中…...

qt中QTatlewidget类常用操作表格的函数有哪些?
在Qt中,QTableWidget 类提供了丰富的函数来操作表格数据。以下是一些常用的操作表格的函数: 1. 初始化与配置 构造函数:QTableWidget(int rows, int columns, QWidget *parent nullptr):创建一个指定行数和列数的表格控件。设置…...

Linux上的C/C++编程
Linux上的C/C编程 yum软件包管理器Linux编辑器-vimvim命令模式指令集vim末行模式指令集 gcc/g的使用Linux自动化编译工具-make/MakefileLinux调试器-gdb调试命令 多人合作工具git yum软件包管理器 yum 是Linux上常用的包管理器,类似于Windows上的“应用商店”。 语…...

注意 秋季饮酒的正确打开方式
选择合适的白酒1.秋季气候干燥,适合选择一些口感醇厚、温润的白酒。比如酱香型白酒,它具有浓郁的香气和醇厚的口感,能在秋季给你带来温暖的感觉。2.浓香型白酒也是不错的选择,香气扑鼻,口感绵甜,能为秋季增…...

Python如何配置环境变量详解
一、概述 前提:已安装 Python,如下图: 1.1 检查是否已配置成功(选) 1 2 3 4 5 1. 打开运行窗口 (1) 快捷键 : Win r,并输入 cmd (2) 直接输入: Python 2. 若有下列提示,即为 安装成功…...

Linux驱动开发(速记版)--并发与竞争
第十八章 并发与竞争 18.1 并发与竞争 18.1.1 并发 早期计算机 CPU单核心时,由于 CPU执行速度快于I/O操作,常因等待 I/O而空闲。 为提高 CPU利用率,引入了并发执行理论。并发通过算法在CPU执行I/O等待时切换至其他任务,使多个任…...

AI赋能,数字技术服务平台促进产业协同发展
在当今数字化浪潮席卷全球的时代,数字技术服务平台应运而生,成为推动各行业发展的强大引擎。数字技术服务平台是一个汇聚了众多先进数字技术和资源的综合性服务体系。它就像是一个功能强大的百宝箱,为用户提供了全方位的数字技术支持。 在这…...

RabbitMQ下载安装运行环境搭建
RabbitMQ运行环境搭建 1、Erlang及RabbitMQ安装版本的选择2、下载安装Erlang2.1、下载Erlang2.2、安装Erlang2.2.1、安装Erlang前先安装Linux依赖库2.2.2、解压Erlang压缩包文件2.2.3、配置2.2.4、编译2.2.5、安装2.2.6、验证erlang是否安装成功 3、RabbitMQ下载安装3.1、下载3…...

Redis过期时间删除策略详解
文章目录 Redis过期时间删除策略详解一、引言二、Redis过期键删除策略1、定时删除2、惰性删除3、定期删除 三、Redis实际采用的策略1、惰性删除Java 伪代码 2、定期删除Java 伪代码 四、总结 Redis过期时间删除策略详解 一、引言 在许多应用程序中,我们经常需要缓…...

mysql数据库的基本管理
目录 一.数据库的介绍 二.mariadb的安装 三.软件基本信息 四.数据库开启 五.数据库的安全初始化 六.数据库的基本管理 七.数据密码管理 八.用户授权 九.数据库的备份 十.web控制器 一.数据库的介绍 1.什么是数据库 数据库就是个高级的表格软件 2.常见数据库 Mysql Oracl…...

根据现有html里的元素上面动态创建el-tooltip组件并显示的几种方式
1、 在这个示例中,我们创建了一个 ref 引用来指向我们想要附加 Tooltip 的 DOM 元素。然后在 onMounted 生命周期钩子中,我们创建了 Tooltip 组件的实例,并将其挂载到一个新创建的 DOM 元素上。我们还在触发元素上添加了 mouseenter 和 mouse…...

【C++篇】迈入新世界的大门——初识C++(上篇)
文章目录 前言 下篇已出:【C篇】迈入新世界的大门——初识C(下篇) C发展历史 C起源 与C语言一样,C也是在贝尔实验室诞生的,Bjarne Stroustrup于20世纪80年代在这里开发出了这种语言。Stroustrup比较关系的是让C更有用࿰…...

啥?Bing搜索古早BUG至今未改?
首先,大家先看下面的一个数学公式。 Γ ( z ) ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)∫0∞tz−1e−tdt. 看不懂?没关系,因为我也看不懂 这不是谈论的重点。 当你把鼠标光标移到公式的最开头&…...

ValueError: Out of range float values are not JSON compliant
可能原因一 可能原因二 数据里面有NaN...

【架构】NewSQL
文章目录 NewSQLTiDBTiDB 主要组件特点使用场景安装与部署 推荐阅读 NewSQL NewSQL是一种数据库管理系统(DBMS)的类别,它结合了NoSQL数据库的可扩展性和传统SQL数据库的事务一致性。具体来说,NewSQL数据库旨在解决传统关系型数据库在处理大规模并发事务…...

禁止吸烟监测系统 基于图像处理的吸烟检测系统 YOLOv7
吸烟是引发火灾的重要原因之一。烟头在未熄灭的情况下,其表面温度可达200℃-300℃,中心温度甚至能高达700℃-800℃。在易燃、易爆的生产环境中,如化工厂、加油站、仓库等,一个小小的烟头就可能引发灾难性的火灾,造成巨…...

《中国工程科学》
《中国工程科学》为工程科技战略咨询学术期刊,主要发布我国工程科技战略咨询研究成果,以及工程科技各领域前瞻性综合研究成果,为政府科学决策提供参谋、为行业科学发展提供指导、为相关学术研究提供参考。 一、2024年度征稿主题 本刊以专题…...

碳钢液动紧急切断阀QDY421F-16C DN200
在深入探讨碳钢液动紧急切断阀QDY421F-16C DN200的卓越性能与应用场景时,不得不提及其在化工、石油、天然气等高危行业中的核心地位。这款阀门以其高度的自动化控制能力和快速响应机制,成为了保障生产安全、防止介质泄漏的关键防线。 其内置的液动执行机…...

【C++】红黑树的封装——同时实现map和set
目录 红黑树的完善默认成员函数迭代器的增加 红黑树的封装红黑树模板参数的控制仿函数解决取K问题对Key的非法操作 insert的调整map的[]运算符重载 在list模拟实现一文中,介绍了如何使用同一份代码封装出list的普通迭代器和const迭代器。今天学习STL中两个关联式容器…...

Tableau|一入门
一 什么是BI工具 BI 工具即商业智能(Business Intelligence)工具,是一种用于收集、整理、分析和展示企业数据的软件系统,其主要目的是帮助企业用户更好地理解和利用数据,以支持决策制定。 主要功能: 1.数据…...

Android 12系统源码_输入系统(三)输入事件的加工和分发
前言 上一篇文章我们具体分析了InputManagerService的构造方法和start方法,知道IMS的start方法经过层层调用,最终会触发Navite层InputDispatcher的start方法和InputReader的start方法。InputDispatcher的start方法会启动一个名为InputDispatcher的线程&…...

【Elasticsearch系列廿二】特殊参数
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...