linux并发服务器 —— linux网络编程(七)

网络结构模式

C/S结构 - 客户机/服务器;采用两层结构,服务器负责数据的管理,客户机负责完成与用户的交互;C/S结构中,服务器 - 后台服务,客户机 - 前台功能;

优点

1. 充分发挥客户端PC处理能力,先在客户端处理再提交服务器,响应速度快;

2. 操作界面好看,满足个性化需求;

3. 安全性较高,面向固定用户群,程序更注重流程;

缺点

1. 需要安装专用的客户端软件;

2. 对客户端的操作系统有限制,不能跨平台;

B/S结构 - 浏览器/服务器;将系统功能实现的核心部分集中于服务器,简化系统开发,维护;

优点

总体成体低,维护方便,分布性强,开发简单;

缺点

1. 通信开销大,系统和数据的安全性较低;

2. 无法实现个性化的功能要求;

3. 协议固定;

4. 响应速度明显降低;

MAC地址、IP地址、端口

MAC地址

网卡是一块被设计用来允许计算机在计算机网络上进行通讯的计算机硬件,又称为网络透配器或网络接口卡NIC,其拥有 MAC 地址,属于 OS 模型的第 2层。

每个网卡都有一个被称为MAC地址的第一无二的48位串行号(以太网卡/无线网卡);

网卡的功能:

1. 数据封装与解封装

2. 链路管理

3. 数据编译与译码

MAC地址 - 媒体存取控制地址/局域网地址/以太网地址/物理地址/硬件地址

MAC地址是用来确认网络设备位置的地址,由网络设备制造商生产时烧录在网卡中;一台设备可以有多个网卡;

IP地址

IP地址是互联网的协议地址,是IP协议提供的一种统一的地址格式,为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此屏蔽物理地址的差异;

A类IP地址 - 一个字节网络地址,三个字节主机地址;1.0.0.1 - 126.255.255.254;子网掩码255.0.0.0;用于广域网

B类IP地址 - 两个字节网络地址,两个字节主机地址;128.0.0.1 - 191.255.255.254;子网掩码255.255.0.0;用于城际网络

C类IP地址 - 三个字节网络地址,一个字节主机地址;192.0.0.1 - 223.255.255.254;子网掩码255.255.255.0;用于局域网

0.0.0.0 - 当前主机

255.255.255.255 - 当前子网的广播地址

IP地址不能以127开头,127.0.0.1可以代表本机IP地址

子网掩码

子网掩码必须结合IP地址一起使用,用于屏蔽IP地址一部分,区分网络地址和主机地址;

192.168.100.10/24 - IP地址192.168.100.10 , 子网掩码24个1;

端口

设备与外界通讯交流的出口 - 虚拟端口/物理端口;

虚拟端口是逻辑意义上的端口,特指TCP/IP协议中的端口;一个IP地址可以由65536个端口,端口通过端口号标识 0 - 65535;一个计算机中不能出现同样端口号的进程,不用进程号的原因是因为进程号是变化的;0~1023是周知端口,紧密绑定一些特定服务,不能自己设置使用;

1024~49151为注册端口,用户选择安装的一些应用程序

49152~65535 动态分配

网络模型

七层参考模型/osi参考模型

1. 物理层 - 定义物理设备的标准(接口类型、传输速率)

2. 数据链路层 - 提供介质访问、链路管理

3. 网络层 - IP选址、路由选择

4. 传输层 - 建立、管理、维护端到端的连接

5. 会话层 - 建立、管理、维护会话

6. 表示层 - 数据格式转化、数据加密

7. 应用层 - 为应用程序提供服务,用户和网络服务的接口

TCP/IP四层模型

协议

通信双方必须共同遵从的一组约定;三要素:语法、语义、时序,最终体现为在网络上传输的数据包格式;各个层之间的协议不互相影响

应用层协议 - FTP(文件传输)/HTTP(超文本传输协议)/NFS(网络文件协议)

传输层协议 - TCP(传输控制协议)/UDP(用户数据包协议)

网络层协议 - IP(因特网互联协议)/ICMP(因特网控制报文协议)/IGMP(因特网组管理协议)

网络接口层协议 - ARP(地址解析协议)/RARP(反向地址解析协议)

UDP协议

TCP协议

IP协议

以太网帧协议

ARP协议

网络通信的过程

封装 - 上层协议通过封装使用下层协议提供的服务;每层协议在上层数据的基础上加上自己的头部/尾部信息,实现该层的功能;

分用 - 帧到达主机,沿着协议栈自底向上依次传递,各层协议处理本层负责的头部数据,获取信息;

ARP协议 - 通过IP地址查找MAC地址; - 28个字节

RARP协议 - 通过MAC地址查找IP地址;

Socket介绍

套接字 - 对网络中不同主机上的应用进程之间进行双向通信的端点的抽象;一个套接字就是网络上进程通信的一段,提供了应用层进程利用网络协议交换数据的机制;上联应用程序,下联网路协议栈,是应用程序与网络协议进行交互的接口;

通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的 socket 中,该 socket通过与网络接口卡 (NIC)相连的传输介质将这段信息送到另外一台主机的 socket 中,使对方能够接收到这段言息。socket 是由 IP 地址和端口结合的,提供向应用层进程传送数据包的机制。

在linux环境下,用于表示进程间网络通信的特殊文件类型;本质为内核借助缓冲区形成的伪文件;

套接字通信分两部分

服务器端:被动接收连接,一般不主动 

客户端:主动向服务器发起连接

socket地址

socket地址是一个结构体 - 封装IP和端口号

sa_family是地址族类型的变量,地址族类型通常与协议族相对应;sa_data存放socket地址值

PF_UNIX - 文件路径名 - 108字节

PF_INET - 6个字节 , 16位端口号,32位IP

PFINET6 - 26个字节 , 16位端口号,32位流标识 ,128位IP,32bit范围ID

为了方便使用提出专用的SOCKET地址:

所有专用socket地址实际使用时都需要转换为通用的socket地址;

socket函数

#inc]ude <sys/types .h>
#incIude <sys/socket .h>
#incTude <arpa/inet .h> // 包含了该头文件上面两个可以省略
int socket(int domain, int type, int protoco1);功能:创建一个套接字参数:domain - 协议族 AF_INET - ipv4 AF_INET6 - ipv6AF_UNIX AF_LOCAL - 本地套接字通信(进程间)type - 通信过程中实现的协议类型SOCK_STREAM - 流式协议SOCK_DGRAM - 报式协议protocol - 具体的协议0 - SOCK_STREAM (TCP)- SOCK_DGRAM (UDP)返回值:成功 - 返回文件描述符,操作的就是内核缓冲区失败 - -1
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);功能:绑定fd和本地的IP/端口参数:sockfd - socket函数得到的fdaddr - 需要绑定的socket地址addrlen - 第二个参数结构体的内存大小
int listen(int sockfd, int backlog);// /proc/sys/net/core/somaxconn功能:监听socket上的连接参数:sockfd - 文件描述符backlog - 未连接和已连接的和的最大值
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);功能:接收客户端连接,默认阻塞,等待客户端连接进来参数:sockfd - 文件描述符addr - 传出参数,记录了连接成功后客户端的地址信息addrlen - 第二个参数的内存大小返回值成功 - 用于通信的文件描述符失败 - -1
int connect(int sockfd, const struct sockaddr *addr , socklen_t addrlen) ;功能:客户端连接服务器参数:sockfd - 用于通信的文件描述符addr - 客户端要连接的服务器的地址信息addtrlen - 第二个参数的内存大小返回值:成功 - 0失败 - -1
ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf,size_t count);

字节序

字节序,顾名思义宁节的顺序,就是大于一个宁节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)

字节序分为大端字节序和小端字节序;

大端小端的判断

/*字节序判断 - 大端/小端
*/
#include <iostream>
using namespace std;int main(){union{short value;char byte;}test;test.value = 0x0102;if(test.byte == 1){cout<<"大端存储"<<endl;}else if(test.byte == 2){cout<<"小端存储"<<endl;}else{cout<<"未知"<<endl;}return 0;
}

字节序转换函数

格式化的数据在两台不同字节序的主机之间传递会发生问题;所以需要发送端先转大端,接收端再根据自身情况进行转换;socket提供了封装好的转换函数

s - 转换端口;l - 转换IP

 网络通信时,需要将主机字节序转换成网络字节序(大端),另外一段获取到数据以后根据情况将网络字节序转换成主机字节

IP地址转换

将字符串IP转为整数/主机网络字节序转换

#include <arpa/inet.h>
// p - 点分十进制字符串 ; n - 网络字节序的整数
int inet_pton(int af, const char *src, void *dst);af - 地址族AF - INET IPV4AF - INET6 IPV6src - 需要转换的点分十进制的IP字符串dst - 转换后的结果保存在这儿
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);size - 第三个参数的大小(数组大小)返回值 - 转换后的数据地址,和dst一样
#include <iostream>
#include <arpa/inet.h>
#include<cstdio>
using namespace std;// int inet_pton(int af, const char *src, void *dst);// const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);int main(){//字符串转整数char buf[] = "192.168.1.4";unsigned int num = 0;inet_pton(AF_INET , buf , &num);unsigned char* p = (unsigned char*)&num;cout<<(int)*p<<" "<<(int)*(p+1)<<" "<<(int)*(p+2)<<" "<<(int)*(p+3)<<endl;// 整数转字符串char ip[16] = "";const char* str = inet_ntop(AF_INET , &num , ip , sizeof(ip));cout<<str<<endl;return 0;
}

TCP通信流程

UDPTCP
用户数据包协议传输控制协议
面向无连接面向连接
可以单播,多播,广播只能1v1(单播)
面向数据报基于字节流
不可靠的协议可靠的协议

TCP 通信流程

服务器

        - 创建有一个用于监听的套接字

         - 将监听的文件描述符和本地的IP、端口绑定(IP 端口就是服务器的地址信息)

        - 设置监听,监听的fd开始工作

        - 阻塞等待,当有客户端发起连接,解除阻塞,接受客户端连接,得到和客户端通信的套接字

        - 通信(接受数据/发送数据)

        - 通信结束断开连接

客户端

        - 创建一个用于通信的套接字

        - 连接服务器,需要指定连接服务器的IP/端口

        - 连接成功了,发生通信

        - 通信结束断开连接

TCP通信实现(服务端/客户端)

// 实现TCP服务器端
#include <iostream>
#include <arpa/inet.h>
#include<cstdio>
#include <unistd.h>
#include <string.h>
using namespace std;int main(){// 1. 创建socket(用于监听)int lfd = socket(AF_INET , SOCK_STREAM , 0);// 2. 绑定struct sockaddr_in saddr;saddr.sin_family = PF_INET;inet_pton(AF_INET , "192.168.93.129" , &saddr.sin_addr.s_addr);saddr.sin_port = htons(9998);bind(lfd , (struct sockaddr*)&saddr , sizeof(saddr));// 3. 监听listen(lfd , 8);// 4. 接收客户端连接struct sockaddr_in caddr;socklen_t len = sizeof(caddr); int cfd = accept(lfd , (struct sockaddr*)&caddr , &len);// 输出客户端的信息char client_ip[16];inet_ntop(AF_INET , &caddr.sin_addr.s_addr , client_ip , sizeof(client_ip));unsigned short client_port = ntohs(caddr.sin_port);cout<<"IP:"<<client_ip<<" "<<"PORT: "<<client_port<<endl;// 获取客户端的数据char buf[1024] = {0};len = read(cfd , buf , sizeof(buf));cout<<"服务器读取数据:"<<buf<<endl;// 给客户端发数据const char *str = "hello 647";write(cfd , str , strlen(str));close(lfd);close(cfd);return 0;
}
// TCP通信客户端
#include <iostream>
#include <arpa/inet.h>
#include<cstdio>
#include <unistd.h>
#include <string.h>
using namespace std;int main(){// 1. 创建套接字int fd = socket(AF_INET , SOCK_STREAM , 0);// 2. 连接服务器端struct sockaddr_in caddr;caddr.sin_family = AF_INET;inet_pton(AF_INET , "192.168.93.129" , &caddr.sin_addr.s_addr);caddr.sin_port = htons(9998);connect(fd , (struct sockaddr*)&caddr , sizeof(caddr));// 3. 读写数据const char *str = "hello zry";write(fd , str , strlen(str));char buf[1024] = {0};int len = read(fd , buf , sizeof(buf));cout<<"客户端读取数据:"<<buf<<endl;close(fd);return 0;
}

TCP三次握手

三次握手发生在客户端丽连接,调用connect(),底层会通过TCP协议进行三次握手;

注意:第三次握手可以携带数据

滑动窗口

滑动窗口的大小意味着接收方还有多大的缓冲区可用于接收数据;

滑动窗口的大小会随着发送数据/接收数据而变化;

通信双方都有发送缓冲区和接收缓冲区

mss: 一条数据最大的数据量;
win: 滑动窗口;

TCP四次挥手

发生在断开连接的时候,程序调用close()会使用TCP协议进行四次挥手;

客户端/服务端都可以主动发起/断开连接,谁调用close()就是谁发起的;

注意:发起方FIN请求可以携带数据!!

多进程实现并发服务器

实现TCP通信服务器处理并发的任务,使用多线程或者多进程解决;

1. 一个父进程,多个子进程

2. 父进程负责等待并接收客户端的连接

3. 子进程:完成通信,接收一个客户端请求就创建一个子进程

#include <iostream>
#include <arpa/inet.h>
#include<cstdio>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <wait.h>
#include <errno.h>
using namespace std;void fun(int arg){while(1){int ret = waitpid(-1 , NULL , WNOHANG);if(ret == - 1){break;}else if(ret == 0){break;}else{cout<<"回收到了子进程: "<<ret<<endl;}}
}int main(){// 注册信号捕捉struct sigaction act;act.sa_flags = 0;sigemptyset(&act.sa_mask);act.sa_handler = fun;sigaction(SIGCHLD , &act , NULL);// 创建int lfd = socket(AF_INET , SOCK_STREAM , 0);if(lfd == -1){perror("socket");exit(0);}// 绑定struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(9999);saddr.sin_addr.s_addr = INADDR_ANY;int ret = bind(lfd , (struct sockaddr*)&saddr , sizeof(saddr));if(ret == -1){perror("bind");exit(0);}// 监听ret = listen(lfd , 5);if(ret == -1){perror("listen");exit(0);}// 不断循环等待客户端连接while(1){struct sockaddr_in caddr;socklen_t len = sizeof(caddr);int cfd = accept(lfd , (struct sockaddr*)&caddr , &len);if(cfd == -1){if(errno == EINTR){continue;}perror("accept");exit(0);}// 每一个连接进来,创建子进程与客户端通信pid_t pid = fork();if(pid == 0){// 获取客户端信息char ip[16];inet_ntop(AF_INET , &caddr.sin_addr.s_addr , ip , sizeof(ip));unsigned short port = ntohs(caddr.sin_port);cout<<"IP: "<<ip<<" "<<"port: "<<port<<endl;// 接收客户端发来的数据char buf[1024] = {0};while(1){int len = read(cfd , &buf , sizeof(buf));if(len == -1){perror("read");exit(0);}else if(len > 0){cout<<"读到了数据:"<<buf<<endl;}else{cout<<"已经断开连接了.....";}write(cfd , buf , strlen(buf));}}close(cfd);}close(lfd);return 0;
}
// TCP通信的客户端
#include <iostream>
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
int main() {// 1.创建套接字int fd = socket(AF_INET, SOCK_STREAM, 0);if(fd == -1) {perror("socket");exit(-1);}// 2.连接服务器端struct sockaddr_in caddr;caddr.sin_family = AF_INET;inet_pton(AF_INET , "192.168.93.129" , &caddr.sin_addr.s_addr);caddr.sin_port = htons(9999);int ret = connect(fd, (struct sockaddr *)&caddr, sizeof(caddr));if(ret == -1) {perror("connect");exit(-1);}// 3. 通信char recvBuf[1024];int i = 0;while(1) {sprintf(recvBuf, "data : %d\n", i++);// 给服务器端发送数据write(fd, recvBuf, strlen(recvBuf)+1);int len = read(fd, recvBuf, sizeof(recvBuf));if(len == -1) {perror("read");exit(-1);} else if(len > 0) {printf("recv server : %s\n", recvBuf);} else if(len == 0) {// 表示服务器端断开连接printf("server closed...");break;}sleep(1);}// 关闭连接close(fd);return 0;
}

多线程实现并发服务器 - client同多进程并发

#include <iostream>
#include <arpa/inet.h>
#include <cstdio>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
using namespace std;struct sockInfo{int fd;pthread_t tid;struct sockaddr_in addr;
};struct sockInfo sockinfos[128];void *work(void * arg){// 子线程和客户端通信 cfd/客户端信息/线程号// 获取客户端信息struct sockInfo *pinfo = (struct sockInfo *)arg;char ip[16];inet_ntop(AF_INET , &pinfo->addr.sin_addr.s_addr , ip , sizeof(ip));unsigned short port = ntohs(pinfo->addr.sin_port);cout<<"IP: "<<ip<<" "<<"port: "<<port<<endl;// 接收客户端发来的数据char buf[1024] = {0};while(1){int len = read(pinfo->fd , &buf , sizeof(buf));if(len == -1){perror("read");exit(0);}else if(len > 0){cout<<"读到了数据:"<<buf<<endl;}else{cout<<"已经断开连接了.....";break;}write(pinfo->fd , buf , strlen(buf)+1);}close(pinfo->fd);return NULL;
}int main(){// 创建int lfd = socket(AF_INET , SOCK_STREAM , 0);if(lfd == -1){perror("socket");exit(0);}cout<<1;// 绑定struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(9999);saddr.sin_addr.s_addr = INADDR_ANY;int ret = bind(lfd , (struct sockaddr*)&saddr , sizeof(saddr));if(ret == -1){perror("bind");exit(0);}// 监听ret = listen(lfd , 5);if(ret == -1){perror("listen");exit(0);}// 初始化数据int max = sizeof(sockinfos)/sizeof(sockinfos[0]);for(int i = 0 ; i<max ; i++){bzero(&sockinfos[i] , sizeof(sockinfos[i]));sockinfos[i].fd = -1;sockinfos[i].tid = -1;}// 不断循环等待客户端连接(子线程创建)while(1){struct sockaddr_in caddr;socklen_t len = sizeof(caddr);int cfd = accept(lfd , (struct sockaddr*)&caddr , &len);struct sockInfo *pinfo;for(int i = 0 ; i<max ; i++){// 从数组中找到可用的sockInfoif(sockinfos[i].fd == -1){pinfo = &sockinfos[i];break;}if(i == max - 1){sleep(1);i--;}}pinfo->fd = cfd;memcpy(&pinfo->addr , &caddr , len);// 每一个连接进来,创建子线程与客户端通信pthread_t tid;pthread_create(&pinfo->tid , NULL , work , pinfo);pthread_detach(pinfo->tid);}close(lfd);return 0;
}

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

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

相关文章

RK开发板的USB连接(Ubuntu)

一、安装连接工具 sudo apt-get install putty 二、启动putty工具 sudo putty 三、连接usb&#xff0c;并查看相关的信息 # 查看接入的是否有usb ls /dev/tty* 显示如下&#xff1a;&#xff08;含有usb接口&#xff1a; /dev/ttyUSB0&#xff09; /dev/tty /dev/tty23 /d…

2.4 PE结构:节表详细解析

节表&#xff08;Section Table&#xff09;是Windows PE/COFF格式的可执行文件中一个非常重要的数据结构&#xff0c;它记录了各个代码段、数据段、资源段、重定向表等在文件中的位置和大小信息&#xff0c;是操作系统加载文件时根据节表来进行各个段的映射和初始化的重要依据…

微信小程序 通过响应式数据控制元素class属性

我想大家照这个和我最初的目的一样 希望有和vue中v-bind:class一样方便的指令 但答案不太尽人意 这里 我们只能采用 三元运算符的形式 参考代码如下 <view class"item {{ userId item.userId ? isThisUser : }}"> </view>这里 我们判断 如果当前ite…

【Java】关于JDK 8的HashMap

文章目录 HashMap 简介数据结构Hash构造方法get(key)方法步骤一&#xff1a;通过key获取所在桶的第一个元素是否存在步骤二:该节点的hash和key是否与要查询的hash和key匹配步骤三:当对应桶中不止一个节点时&#xff0c;根据不同节点类型查询 put(key,value)为什么树化&#xff…

elasticsearch分析插件 安装analysis-ik

首先下载安装es 和 插件 &#xff0c;注意 两者的版本要保持一致,如果要用到kibana 则三者保持一致 ik&#xff1a;https://github.com/medcl/elasticsearch-analysis-ik/releases es/kibana&#xff1a;https://www.elastic.co/cn/downloads/past-releases/ 然后在 es— elast…

【Node.js】Node.js安装详细步骤和创建Express项目演示

Node.js是一个开源的、跨平台的JavaScript运行环境&#xff0c;用于在服务器端运行JavaScript代码。它提供了一个简单的API&#xff0c;可以用于开发各种网络和服务器应用程序。 以下是Node.js的安装和使用的详细步骤和代码示例&#xff1a; 1、下载Node.js 访问Node.js官方…

异步驱动电机总成汇总

特斯拉双电机 蔚来ET7异步电驱 蔚来ET5异步电驱 问界M5异步电驱 比亚迪海豹异步异步电驱 汇川800v异步电驱 阿维塔异步电驱 小鹏G6异步电驱 小鹏G9异步电驱 大众ID4异步电驱 奥迪etron异步电驱 欢迎补充&#xff5e;&#xff5e;&#xff5e;欢迎转载&#xff01;&#xff01;&…

47、TCP的流量控制

从这一节开始&#xff0c;我们学习通信双方应用进程建立TCP连接之后&#xff0c;数据传输过程中&#xff0c;TCP有哪些机制保证传输可靠性的。本节先学习第一种机制&#xff1a;流量控制。 窗口与流量控制 首先&#xff0c;我们要知道的是&#xff1a;什么是流量控制&#xff…

电脑怎么设置定时关机,2个简单的操作

电脑作为现代生活中不可或缺的工具&#xff0c;我们通常会在工作或娱乐过程中使用它。但有时候&#xff0c;我们可能需要在一段时间后自动关机&#xff0c;例如在下载完成后或在睡觉前。那么电脑怎么设置定时关机呢&#xff1f;为了满足这种需求&#xff0c;电脑提供了多种定时…

手写Mybatis:第9章-细化XML语句构建器,完善静态SQL解析

文章目录 一、目标&#xff1a;XML语句构建器二、设计&#xff1a;XML语句构建器三、实现&#xff1a;XML语句构建器3.0 引入依赖3.1 工程结构3.2 XML语句构建器关系图3.3 I/O资源扫描3.4 SQL源码3.4.1 SQL对象3.4.2 SQL源码接口3.4.3 原始SQL源码实现类3.4.4 静态SQL源码实现类…

C到C++的升级

C和C的关系 C继承了所有C语言的特性&#xff1b;C在C的基础上提供了更多的语法和特性&#xff0c;C语言去除了一些C语言的不好的特性。C的设计目标是运行效率与开发效率的统一。 变化一&#xff1a;所有变量都可以在使用时定义 C中更强调语言的实用性&#xff0c;所有的变量…

CentOS7 Hadoop3.3.0 安装与配置

一、安装JDK 1、创建文件夹tools和training用于存放压缩包和解压使用&#xff0c;tools存放压缩包&#xff0c;training用于解压后安装jdk和hadoop的路径。 1&#xff09;回到路径为 / 的位置 cd /2) 创建 tools 和 training mkdir toolsmkdir training3) 进入tools文件夹 …

测试工程师的领航指南:《Effective软件测试》

目录 前言一、本书适合对象二、本书大纲第1章&#xff1a;有效和系统的软件测试第2章&#xff1a;基于需求规格的测试第3章&#xff1a;结构化测试与代码覆盖第4章&#xff1a;契约式设计第5章&#xff1a;基于属性的测试第6章&#xff1a;测试替身和模拟对象第7章&#xff1a;…

Python综合案例(基本地图使用)

一、基本地图的使用 基本代码&#xff1a; """ 演示地图可视化的基本使用 """ from pyecharts.charts import Map from pyecharts.options import VisualMapOpts# 准备地图对象 map Map() # 准备数据 data [("北京", 99),("…

【C++进阶(四)】STL大法--list深度剖析list迭代器问题探讨

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 链表list 1. 前言2. list的使用2.1 list的构造函…

【树形权限】树形列表权限互斥选择、el-tree设置禁用等等

文章目录 一、实现如上树形列表1.1 首先要就是渲染树形列表1.2 然后通过插槽处理头部标题1.3 再通过插槽处理表格body体内容1.4 让body体中的选框和表头中的选框产生关联 二、将 el-tree 整棵树设为禁用状态三、动态表格合并 需求&#xff1a;按照权限管理配置的数据权限树展开…

如何利用客户旅程打造好的用户体验?

在当今竞争激烈的市场中&#xff0c;提供卓越的用户体验已经成为企业脱颖而出的关键因素之一。客户旅程是实现出色用户体验的有力工具之一&#xff0c;而HubSpot的客户旅程规划功能为企业提供了强大的支持&#xff0c;帮助他们更好地理解、管理和改善客户的互动过程。今天运营坛…

【USRP】调制解调系列5:16QAM、32QAM、64QAM、256QAM、1024QAM、基于labview的实现

QAM 正交振幅键控是一种将两种调幅信号&#xff08;2ASK和2PSK&#xff09;汇合到一个信道的方法&#xff0c;因此会双倍扩展有效带宽&#xff0c;正交调幅被用于脉冲调幅。正交调幅信号有两个相同频率的载波&#xff0c;但是相位相差90度&#xff08;四分之一周期&#xff0c…

参编三大金融国标,奇富科技以技术促行业规范化演进

近期&#xff0c;由中国互联网金融协会领导制定的《互联网金融智能风险防控技术要求》《互联网金融个人网络消费信贷信息披露》《互联网金融个人身份识别技术要求》三项国家标准颁布&#xff0c;由国家市场监督管理总局、国家标准化管理委员会发布&#xff0c;奇富科技作为核心…

Flutter 混合开发调试

针对Flutter开发的同学来说&#xff0c;大部分的应用还是Native Flutter的混合开发&#xff0c;所以每次改完Flutter代码&#xff0c;运行整个项目无疑是很费时间的。所以Flutter官方也给我们提供了混合调试的方案【在混合开发模式下进行调试】&#xff0c;这里以Android Stud…