浅谈 TCP 握手/数据传输/挥手过程以及 tcpdump 抓包工具使用

news2024/11/16 23:31:09

  • 前言
  • 浅谈 OSI
  • TCP
    • 三次握手
    • 数据传输
    • 四次挥手
    • Socket 服务端/客户端通信测试
      • 服务端代码
      • 客户端代码
      • tcpdump 命令监控
      • 命令总结
    • FAQ
      • 怎么确认数据包的大小?
      • TCP 拥塞如何避免?
      • 如何理解 TCP keep-alive 原理?
  • 总结

前言

在网络知识体系,TCP 这块的三次握手、四次挥手是必备的,在面试中,也是老生常谈了,以下通过抓包工具抓取整个握手、挥手交互的过程进行分析.

浅谈 OSI

OSI(Open System Interconnect)缩写,意为开放式地系统互联,七层参考模型,利用了软件工程学的概念,是为了分层解耦
七层参考模型

  1. 应用层:与用户打交道的那一层,通过 interface 接口交互的,比如浏览器、Tomcat,应用层出现的协议有 HTTP、FTP、SSH等等
  2. 表示层:协议、语义,是否符合协议规定由表示层来处理
  3. 会话层:服务器验证用户登录,session->保持会话的动作由会话层来完成
  4. 传输控制层:如何建立连接、如何传输,是成功还是失败的一个控制,传输层出现的协议有 TCP/UDP,TCP 是面向连接、可靠的传输协议,UDP 恰恰相反
  5. 网络层:由网络层来处理设备之间是如何路由,是如何找到的
  6. 链路层:点与点之间的通信 、点与点之间是什么样的通信协议,具体能发出什么
  7. 物理层:WIFI、光纤等设备

TCP

在这里主要介绍在 TCP 协议中服务端与客户端三次握手、四次挥手是如何完成的一个过程,TCP 是在传输控制层中进行交互的

服务端/客户端它们都会各自去维护自己的序列号:seq

三次握手

在这里插入图片描述

  1. 客户端调用 connect() 指令,发送 SYN-建立连接标识、seq 起始序列号给到服务端
  2. 服务端会有 listent() 指令监听来自客户端的链接,收到 SYN 标识后,发送服务端的 seq 起始序列号、ACK 确认号「客户端起始序列号+1」给到客户端,客户端应答后即可建立
  3. 客户端建立了连接以后,回复 ack 确认号「服务端起始序列号+1」给服务端,服务端也建立好了

以上是握手的交互流程,同时也说明会产生一些资源的消耗/开辟:线程、对象、文件描述符、调用等等操作

到这里,就会问两次握手可以去完成握手吗?

不可能是两次,若为两次的话;客户端先发起,服务端回复了,客户端后续就不管了做其他的事情去了,而服务端还在傻傻的等着回应,同时资源也还在占用,这及其不安全也消耗资源的

数据传输

在这里插入图片描述
在数据传输交互中过程中

  1. 客户端通过 write 指令去写入数据,客户端会携带:序列号+1 值、ack 确认号到服务端
  2. 服务端通过 read 指令读取数据,将数据进行处理后,会发送 ack 确认号到 客户端
  3. 这样,数据传输就能在客户端/服务端之间完成

四次挥手

在这里插入图片描述

  1. 客户端调用 close() 指令,携带 FIN-断开连接标识、序列号+2「之前 +1 的值用过了」、ack 确认号,发起断开连接请求
  2. 服务端确认后,回复确认号序列号 +3
  3. 服务端调用 close() 指令,携带 FIN-断开连接标识、系列号+1,发起断开连接请求
  4. 客户端确认后,回复确认号系列号 +2

到这里,就会问为什么要发生四次挥手,两次不可以吗?

因为 TCP 连接是双向的,因此在四次挥手中前两次是用于断开一方的连接,而后两次是用于断开另外一方的连接;同时,考虑到 socket 问题,端口号数量是有限的{socket:套接字通信,端口数量最多 65535 个},用完了必须要记得回收、关闭资源,保证端口释放以便给其他的服务进行使用;

Socket 服务端/客户端通信测试

通过 Socket 通信,采用 TCP 协议来进行服务端、客户端之间的交互演示

服务端代码

import java.io.*;
import java.net.*;
public class SocketIoServer {
    // server socket listen property:
    private static final int RECEIVE_BUFFER = 10;
    private static final int SO_TIMEOUT = 0;
    private static final boolean REUSE_ADDR = false;
    // 备胎可以有两个:后台最多可以有多少个待处理的连接
    private static final int BACK_LOG = 2;
    // client socket listen property on server endpoint:服务端客户端之间维护着心跳,互相确认自己还活着
    private static final boolean CLI_KEEPALIVE = false;
    private static final boolean CLI_OOB = false;
    private static final int CLI_REC_BUF = 20;
    private static final boolean CLI_REUSE_ADDR = false;
    private static final int CLI_SEND_BUF = 20;
    private static final boolean CLI_LINGER = true;
    private static final int CLI_LINGER_N = 0;
    private static final int CLI_TIMEOUT = 0;
    // 关闭 Nagle 算法:不组合小分组的数据,而是每次都立即发送出去
    // 开启 Nagle 算法:组合小分组的数据,以一个分组的方式发送出去,降低了吞吐量
    private static final boolean CLI_NO_DELAY = false;
		/*
    StandardSocketOptions.TCP_NODELAY
    StandardSocketOptions.SO_KEEPALIVE
    StandardSocketOptions.SO_LINGER
    StandardSocketOptions.SO_RCVBUF
    StandardSocketOptions.SO_SNDBUF
    StandardSocketOptions.SO_REUSEADDR
 		*/
    public static void main(String[] args) {

        ServerSocket server = null;
        try {
            server = new ServerSocket();
            server.bind(new InetSocketAddress(9090), BACK_LOG);
            server.setReceiveBufferSize(RECEIVE_BUFFER);
            server.setReuseAddress(REUSE_ADDR);
            server.setSoTimeout(SO_TIMEOUT);
            System.out.println("server up use 9090!");
            while (true) {
                // System.in.read();  //分水岭:
                Socket client = server.accept();  // 阻塞的,一直卡着不动,内核指令:accept(4,
                System.out.println("client port: " + client.getPort());
                client.setKeepAlive(CLI_KEEPALIVE);
                client.setOOBInline(CLI_OOB);
                client.setReceiveBufferSize(CLI_REC_BUF);
                client.setReuseAddress(CLI_REUSE_ADDR);
                client.setSendBufferSize(CLI_SEND_BUF);
                client.setSoLinger(CLI_LINGER, CLI_LINGER_N);
                client.setSoTimeout(CLI_TIMEOUT);
                client.setTcpNoDelay(CLI_NO_DELAY);
                Thread thread = new Thread(
                        () -> {
                            try {
                                InputStream in = client.getInputStream();
                                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                                char[] data = new char[1024];
                                while (true) {
                                    int num = reader.read(data);
                                    if (num > 0) {
                                        System.out.println("client read some data is :" + num + " val :" + new String(data, 0, num));
                                    } else if (num == 0) {
                                        System.out.println("client readed nothing!");
                                        continue;
                                    } else {
                                        System.out.println("client readed -1...");
                                        System.in.read();
                                        client.close();
                                        break;
                                    }
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                );
                thread.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                server.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

客户端代码

import java.io.*;
import java.net.Socket;
public class SocketClient {
    public static void main(String[] args) {
        try {
        	// 172.16.249.12:虚拟机中 ifconfig 网卡中的外网 IP
            Socket client = new Socket("172.16.249.12",9090);
            client.setSendBufferSize(20);
            client.setTcpNoDelay(true);
            OutputStream out = client.getOutputStream();
            InputStream in = System.in;
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            while(true){
                String line = reader.readLine();
                if(line != null ){
                    byte[] bb = line.getBytes();
                    for (byte b : bb) {
                        out.write(b);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

tcpdump 命令监控

在这里会介绍一些常用的 Linux 命令以及如何在 Linux 使用 tcpdump

在这里插入图片描述
通过 ifconfig 查看虚拟机中的网卡,ens160 基于外网交互的,lo 基于虚拟机内网交互的,由于服务端、客户端代码都会在这台虚拟机节点上进行编译,所以这里 会使用 lo 这个网卡去 dump 它们之间的 TCP 交互过程,使用 ens160 网卡去 dump 在控制台上是不会输出内容的!

1、启动 socket 服务端,编译后运行

[root@172 ~]# javac SocketIoServer.java && java SocketIoServer
server up use 9090!

2、开启一个新的窗口,查看 socket/tcp 网络信息:netstat -natp,会发生多了下面这条 listen 条目信息

[root@172 ~]# netstat -natp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name           
tcp6       0      0 :::9090                 :::*                    LISTEN      3236/java 

3、查询服务端进程下文件描述符信息:lsof -op pid,此时 offset 偏移量为 0

[root@172 ~]# lsof -op 3236
COMMAND  PID USER   FD   TYPE             DEVICE     OFFSET     NODE NAME
java    3236 root    5u  IPv6              28833        0t0      TCP *:websm (LISTEN)

4、新开启一个窗口,打开 tcp 抓包的程序:tcpdump -i lo -nn port 9090

  • i:网络配置的接口,通过 ifconfig 命令拿到的
  • nn:显示 IP、端口号
  • v:显示更多详细信息

5、新开启一个窗口,客户端连接到服务端,按回车键

[root@172 ~]# javac SocketClient.java  && java SocketClient

此时,tcpdump 窗口就会显示出很多的信息出来,如下:

16:14:35.969838 IP 172.16.249.12.48028 > 172.16.249.12.9090: Flags [S], seq 823509243, win 65495, options [mss 65495,sackOK,TS val 1014857448 ecr 0,nop,wscale 7], length 0
48028 客户端发送 Flags:SYN,序列号:823509243,给到 9090 服务端
16:14:35.969852 IP 172.16.249.12.9090 > 172.16.249.12.48028: Flags [S.], seq 3254927829, ack 823509244, win 1152, options [mss 65495,sackOK,TS val 1014857448 ecr 1014857448,nop,wscale 0], length 0
9090 服务端发送 Flags:SYN,序列号:3254927829,ack=客户端序列号+1值,给到 48028 客户端
16:14:35.969859 IP 172.16.249.12.48028 > 172.16.249.12.9090: Flags [.], ack 1, win 512, options [nop,nop,TS val 1014857448 ecr 1014857448], length 0
48028 客户端发送 ack=1,双方已互相确认,此次连接建立完成

6、客户端发送数据:111,在服务端配置时,为客户端设置了参数 CLI_NO_DELAY{不组合小分组的数据},会有多条信息出来,如下所示:

16:21:29.986816 IP 172.16.249.12.48028 > 172.16.249.12.9090: Flags [P.], seq 1:2, ack 1, win 512, options [nop,nop,TS val 1015271464 ecr 1014857448], length 1
客户端 48028 发送 Flags:PSH{数据传输标识}、序列号:1:2,代表的意思就是发出去的是 1,期望收到的是 2,给到 9090 服务端
16:21:29.987257 IP 172.16.249.12.9090 > 172.16.249.12.48028: Flags [.], ack 2, win 1151, options [nop,nop,TS val 1015271465 ecr 1015271464], length 0
服务端 9090 发送 ack=客户端序列号+1,也就是客户端期望收到的值
16:21:29.987428 IP 172.16.249.12.48028 > 172.16.249.12.9090: Flags [P.], seq 2:3, ack 1, win 512, options [nop,nop,TS val 1015271465 ecr 1015271465], length 1
16:21:29.987555 IP 172.16.249.12.48028 > 172.16.249.12.9090: Flags [P.], seq 3:4, ack 1, win 512, options [nop,nop,TS val 1015271465 ecr 1015271465], length 1
客户端 48028 发送 Flags:PSH{数据传输标识}、序列号:2:3、3:4,组合两条数据一起发出去「受到了客户端 SEND_BUFFER_SIZE 参数值」,给到 9090 服务端
16:21:29.989499 IP 172.16.249.12.9090 > 172.16.249.12.48028: Flags [.], ack 4, win 1151, options [nop,nop,TS val 1015271467 ecr 1015271465], length 0
服务端 9090 发送 ack=客户端最新的序列号+1,也就是客户端期望收到的值

6、到这里,TCP 三次握手、数据传输的过程通过 dump 就搞定了,还剩下四次挥手的过程!
由于在虚拟机内网 lo 网卡中监测不到挥手是四次的过程,所以采用外网网卡 ens160 这里也不懂这是什么原因导致的?
所以在这里我模拟请求百度首页:www.baidu.com,tcpdump 80 端口,来演示四次挥手的过程

  • 在一个窗口命令:tcpdump -i ens160 -nn port 80,监控
  • 另外一个窗口操作命令:curl www.baidu.com 80,等这个请求处理完成以后,当前窗口强制退出断开,在第一个窗口尾部就会出现以下的信息

16:42:30.946631 IP 172.16.249.12.44270 > 163.177.151.109.80: Flags [F.], seq 447954833, ack 1899774463, win 62780, length 0
客户端 44270 发送 Flags:FIN(关闭连接标识) 、序列号、ack 确认号请求,给到百度服务端
16:42:30.948011 IP 163.177.151.109.80 > 172.16.249.12.44270: Flags [.], ack 1, win 64239, length 0
百度服务端确认,发送确认号给客户端 44270
16:42:30.962906 IP 163.177.151.109.80 > 172.16.249.12.44270: Flags [FP.], seq 1, ack 1, win 64239, length 0
百度服务端 发送 Flags:FIN(关闭连接标识) |PSH(数据传输)、序列号、确认号给客户端 44270
16:42:30.963000 IP 172.16.249.12.44270 > 163.177.151.109.80: Flags [.], ack 2, win 62780, length 0
客户端 44270 确认,发送确认号给百度服务端,此次通信完毕

命令总结

  1. yum install -y tcpdump:安装 tcpdump,⼀个命令⾏的⽹络流量分析⼯具,功能⾮常强⼤,⼀般我们⽤来抓 TCP 包
  2. lsof -p:进程中的文件描述符信息,比如:偏移量、文件类型、状态
  3. netstat -natp:socket 网络的信息,listener 条目信息
  4. tcpdump -i ens160 -nn port 9090:监听某端口下的 tcp 交互

TCP 序列号详解:在同一个 TCP 链接过程窗口中,seq 序列号值是持续递增的;SYN 建立链接、FIN 关闭链接,都会消耗 seq,凡是涉及到对端确认的,一定需要消耗 TCP 报文的序列号

FAQ

怎么确认数据包的大小?

最大传输单元: MTU,通过 ifconfig 查看,此时的大小是携带上了 ip、port 的

ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

通过 tcpdump 观察数据传输过程的报文会有如下信息:

options [mss 1460,sackOK,TS val 2603013609 ecr 0,nop,wscale 7]

MSS:最大报文段长度-1460,由 MTU=1500,得出 ip、port 占有 40 字节

TCP 拥塞如何避免?

在网络交互过程中,当服务端的窗体没有容量了,在与客户端握手的过程,通过确认包会告知服务端没有余量了,客户端因此会将自己阻塞住,不再发送数据给到服务端了,等服务端空余了,再补充一个包告知客户端可以继续发了

如何理解 TCP keep-alive 原理?

在一个 TCP 连接上,若通信双方都不再向对方发送数据,那么 TCP 连接就不会有任何数据交换了;假设应用程序是一个 WEB 服务器,客户端发出三次握手以后故障宕机或被剔除网线,对于 WEB 服务器而言,下一个数据包将永远无法到来,但它却一无所知

TCP 协议设计者,考虑到了这种检测长时间死连接的需求,于是乎设计了 keep-alive 机制,它的作用就是探测对端的连接有没有失效,通过定时发送探测包来探测连接的对端是否存活,不过默认情况下需要 7200 s 没有数据包交互才会发送这个探测包,往往这个时间太久了,我们熟知的很多组件都没开启 keep-alive 特性,而是选择在应用层做心跳机制

通过 sysctl -a | grep keepalive 命令可查看 keep-alive 内核参数配置


# 探测包检测频率,多长一次,默认 75 s
net.ipv4.tcp_keepalive_intvl = 75
# 探测包次数,默认 9 次
net.ipv4.tcp_keepalive_probes = 9
# 没有数据交互后多长时间去探测
net.ipv4.tcp_keepalive_time = 7200

总结

该文章浅谈了 TCP 三次握手、数据传输、四次挥手的交互过程,通过 tcpdump 抓包工具对这三个流程进行了信息的输出以及介绍,文末整理了一些操作 TCP 常用的命令以及一些常见的问题,当然,对于整个 TCP 可靠性协议,这些只是皮毛一角了,后续会细究出更多内容进行输出!!

点一波关注不迷路,你的支持是对我最大的鼓励

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

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

相关文章

【计算机组成原理】指令系统

目录 指令格式 按指令数目分类&#xff1a; 零地址指令 一地址指令 二地址指令 三地址指令 四地址指令 按指令长度分类&#xff1a; 指令字长 机器字长 存储字长 按操作码的长度分类 定长操作码 可变长操作码 定长指令字结构可变长操作码------>拓展操作码指令…

女子举重问题

一、问题的描述 问题及要求 1、搜集各个级别世界女子举重比赛的实际数据。分别建立女子举重比赛总成绩的线性模型、幂函数模型、幂函数改进模型&#xff0c;并最终建立总冠军评选模型。 应用以上模型对最近举行的一届奥运会女子举重比赛总成绩进行排名&#xff0c;并对模型及…

Java分布式事务(二)

文章目录&#x1f525;分布式事务处理_认识本地事务&#x1f525;关系型数据库事务基础_并发事务带来的问题&#x1f525;关系型数据库事务基础_MySQL事务隔离级别&#x1f525;MySQL事务隔离级别_模拟异常发生之脏读&#x1f525;MySQL事务隔离级别_模拟异常发生之不可重复读&…

信息安全与数学基础-笔记-②同余

知识目录同余完全剩余系剩余类完全剩余系❀简化剩余系❀欧拉函数逆元&#xff01;欧拉定理 &#xff01;同余 a,b 两个数字&#xff0c;都模m&#xff0c;当两个数字模m后余的数一样即为同余。 例子&#xff1a; a bq r (mod m)&#xff0c;这里的a 和 r 就是同余 &#xff…

如何使用Unity3d实现多人对战联机游戏

所需资源 课程来源&#xff08;请支持正版课程&#xff09; 安装Unity Hub 安装Visual Studio 角色模型 环境准备 ①Unity设置 不设置的话编写有些代码没有自动补全 点开 Preferences 选择 visual studio ②角色导入 点击 windows—>Package Manager 左上角 My Ass…

数据结构与算法(七):排序算法

排序算法是《数据结构与算法》中最基本的算法之一&#xff0c;排序算法可以分为内部和外部排序。 内部排序&#xff1a;数据记录在内存中进行排序。 外部排序&#xff1a;因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。 常…

xgboost:分割Sparsity-aware Split Finding

Sparsity-aware Split Finding1 在许多现实问题中&#xff0c;输入xxx是稀疏的是很常见的。造成稀疏性的可能原因有很多: 1)数据中存在缺失值&#xff1b; 2)统计中频繁出现零项&#xff1b; 3)特征工程的处理结果&#xff0c;如独热编码。 重要的是使算法意识到数据中的稀…

RocketMQ5.1.0单机安装与启动

RocketMQ单机安装与启动系统要求下载地址安装步骤RocketMq启动NameServer查看是否启动成功启动BrokerProxy查看是否启动成功修改tool.sh测试消息产生消息的消费关闭服务器系统要求 下载地址 官网下载地址 二进制包是已经编译完成后可以直接运行的&#xff0c;源码包是需要编译…

javaWeb核心02-RequestResponse

文章目录Request&Response1&#xff0c;Request和Response的概述2&#xff0c;Request对象2.1 Request继承体系2.2 Request获取请求数据2.2.1 获取请求行数据2.2.2 获取请求头数据2.2.3 获取请求体数据2.2.4 获取请求参数的通用方式基于上述理论&#xff0c;request对象为我…

python:使用 Jupyter notebook(测试 matplotlib 和 opencv)

环境&#xff1a; window1python 3.10.6 参考&#xff1a; https://jupyter.org/https://opencv.org/ 一、创建虚拟环境 这个步骤可以跳过&#xff08;因为笔者不喜欢在全局环境安装任何东西&#xff0c;所以搞一个新环境&#xff09;。 先选中一个目录&#xff1a;D:\jackl…

论文笔记 | Conducting research in marketing with quasi-experiments

这篇论文是Journal of Marketing上的论文&#xff0c;讲了使用准实验来进行论文研究的一些事项。外生性识别的来源、几种准实验方法的注意点还有内生性的解决。 这篇论文对于准实验或者是平常论文的展开有一个非常友善的指导功能&#xff0c;可以阅读~ 摘要&#xff1a;本文旨…

多线程实现的三种方法、线程名称的获取

文章目录多线程实现的三种方法1、通过继承Thread&#xff0c;并重写里面的run()方法2、实现Runnable接口&#xff0c;并重写其中run()方法将runnable类传递给Thread类中3、实现Callable接口&#xff0c;重写其中的call()方法Callable接口时有泛型的&#xff0c;该泛型值call()方…

字符串模式匹配,经典KMP算法你还不会?我可不允许你不会!

文章目录重点1. 简单模式匹配算法2. 部分匹配值PM的算法&#xff08;Move j-1 PM[j-1]&#xff09;3. 部分匹配值PM的两次改进&#xff08;Move j-next[j]&#xff09;4. 快速得到next数组5. KMP匹配算法重点 童鞋们看网上讲解的时候一定要分清楚序列是从0开始还是从1开始&…

大数据框架之Hive:第1章 Hive入门

1.1 什么是Hive 1&#xff09;Hive简介 Hive是由Facebook开源&#xff0c;基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供类SQL查询功能。 那为什么会有Hive呢&#xff1f;它是为了解决什么问题而诞生的呢&#xff1f; 下…

性能优化|记一次线上OOM问题处理

概述最近线上监控发现 OOM 涨幅较大&#xff0c;因此去尝试定位和修复这个问题&#xff0c;在修复了一些内存泄漏和大对象占用问题后, OOM 依旧未达到正常标准&#xff0c;在这些新上报的 hprof 文件中&#xff0c;发现几乎所有 case 中都有个叫 FinalizerReference 的对象&…

集合体系概述以及Collection集合常用API

一. 集合 集合与数组类似&#xff0c;都是一种容器。集合是Java中存储对象数据的一种容器。集合也被称为对象容器。 数组的特点 集合的特点 集合的大小不固定&#xff0c;启动后可以动态变化&#xff0c;类型也可以选择不固定。集合更像气球&#xff0c;可大可小。集合非常适合…

python与pycharm从零安装

python&#xff08;解释器&#xff09;下载地址&#xff1a;Welcome to Python.orgpycharm&#xff08;编译器&#xff09;下载地址&#xff1a;PyCharm: the Python IDE for Professional Developers by JetBrains一、python的下载与安装到官网后根据步骤下载安装包后&#xf…

xgboost:分割查找:Weighted Quantile Sketch

Weighted Quantile Sketch 专门处理流式和分布式加权数据集的一种分桶的方法 近似算法的一个重要步骤是提出候选分裂点。通常使用特征的百分位数来使候选数据均匀分布。形式上&#xff0c;设Dk(x1k,h1)&#xff0c;(x2k,h2)⋅⋅⋅(xnk,hn)D_k {(x_{1k}, h_1)&#xff0c;(x_…

Redis持久化:RDB、AOF

Redis持久化一. RDB(1) save(2) bgsave(3) 总结二. AOF(1) 重写优化(2) RDB和AOF的区别引入&#xff1a;Redis用内存存储数据&#xff0c;有数据丢失的问题&#xff1b; 一. RDB RDB&#xff08;Redis Database Bcakup file&#xff09;即Redis数据备份文件&#xff0c;或Red…

如何用 Python采集 <豆某yin片>并作词云图分析 ?

嗨害大家好鸭&#xff01;我是小熊猫~ 总有那么一句银幕台词能打动人心 总有那么一幕名导名作念念不忘 不知道大家有多久没有放松一下了呢&#xff1f; 本次就来给大家采集一下某瓣电影并做词云分析 康康哪一部才是大家心中的经典呢&#xff1f; 最近又有哪一部可能会成为…