当前位置:   article > 正文

牛客测试开发面经总结(一)_抽象类和接口的区别

抽象类和接口的区别

目录

Java当中实例化对象可以用哪些方式?

序列化

常用的集合类有哪些?

开发过程中用过哪些设计模式?

JDBC在数据库当中的一些增删改查操作,流程怎么样的?

用代码写一个本地的脚本,然后怎么去执行

SpringBoot的基本实现原理?

Mybatis都用过哪些注解?

HTTP协议有哪些基本方法?

GET和POST的区别?

脏读,幻读,不可重复读

为什么选择测开

怎么理解测试开发:

购物车场景设计测试用例

输入url到展示界面的流程(键入网址到网页,期间发生了什么?)

什么是死锁,产生死锁的原因及必要条件

自己设计测试用例(唉就根据日常生活来联想回答)

Java创建线程的方式

反射

获取class对象的三种方式

黑盒白盒测试

什么是装饰器(python里面的内容)

什么是单例模式

Python如何实现多线程

进程和线程的关系:

Python的垃圾回收机制

TCP/UDP区别

HTTP/HTTPS区别

GET/POST区别

TCP三次握手和四次挥手,以及中间就是会怎样

Java当中实例化对象可以用哪些方式?

java 实例化是什么及五种实例化方法_实例化对象是什么意思-CSDN博客

简单来说,实例化对象就是new对象

new的过程就是实例化过程,比如new Cat

语法:类名 引用变量名 = new 构造器名();

调用成员属性/成员方法:引用变量名.成员名

一般是这样的,除了反射比较特殊是反着来的

方式:(5种)

1 用new语句创建对象

2 用Class类的newInstance方法,调用无参构造器创建对象

3 用Constructor类的newinstance方法(这里运用反射),调用有参的和私有的构造器对象

4 调用对象的clone()方法,

5 使用反序列化,必须先实现Cloneable接口并实现其定义的clone方法

序列化

简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自 己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。

什么情况下需要序列化

a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;

b)当你想用套接字在网络上传送对象的时候;

c)当你想通过RMI传输对象的时候;

相关注意事项

a)序列化时,只对对象的状态进行保存,而不管对象的方法;

b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;

c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;

d)并非所有的对象都可以序列化,,至于为什么不可以,有很多原因了,比如:

1.安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,

比如写到文件,或者进行rmi传输 等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。

2. 资源分配方面的原因,比如socket,thread类,

如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分 配,而且,也是没有必要这样实现。

常用的集合类有哪些?

Java集合框架主要包括两种类型的容器,一种是集合(Collection),另一种是图(Map)。Collection接口又有3种子类型,List、Set和Queue,再下面是一些抽象类,最后是具体实现类,常用的有ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap等等。Map常用的有HashMap,LinkedHashMap等。

常用的几种java集合类总结_java集合分为哪几大类-CSDN博客

开发过程中用过哪些设计模式?

面试必问的设计模式该如何回答 - 面试总结之设计模式专篇_面试中怎么聊设计模式-CSDN博客

设计模式的定义:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案

设计模式主要分为三大类:创建型模式、结构型模式和行为型模式

单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。

太多了只能过一遍具体看链接

说一说设计模式的六大原则

开闭原则(总纲)、里氏替换原则、依赖倒置原则、单一职责原则、接口隔离原则、迪米特法则。

JDBC在数据库当中的一些增删改查操作,流程怎么样的?

JDBC增删改查(使用Java对数据库操作)_java 访问在对数据库进行增删改时需要判断返回值吗-CSDN博客

如果需要对一个数据库的表进行操作,需要那几步?

1 对数据库进行连接(不连接怎么用)

2 写sql语句

3 执行sql语句

4 关闭资源

查询操作又可以分为几步呢

1 对数据库进行连接

2 写sql语句

3 执行sql语句并得到返回值治理需要使用executeQuery()方法(因为它具有返回集的作用

对得到的结果集进行遍历输出处理

关闭资源

这里返回的结构肯定是对某一个表中的元素值进行返回,so定义一个对象来进行保存我们的结果值,顺便可以重写tostring方法进行遍历打印

改进一下:因为这样子结果只能一条语句一样的输出,so想到了使用集合来保存 每一条数据

但是集合类型不一致,所以使用泛型定义

1. 连接数据库

加载数据库驱动程序: 使用 Class.forName() 方法加载数据库驱动程序,例如:

Class.forName("com.mysql.cj.jdbc.Driver");

建立数据库连接: 使用 DriverManager.getConnection() 方法连接数据库,传入数据库 URL、用户名和密码:

String url = "jdbc:mysql://localhost:3306/mydatabase";Connection connection = DriverManager.getConnection(url, "username", "password");

2. 执行查询操作(SELECT)

创建 Statement 或者 PreparedStatement 对象: 使用 Connection 对象创建 Statement 或者 PreparedStatement 对象,用于执行 SQL 查询语句:

Statement statement = connection.createStatement();

执行 SQL 查询语句: 使用 Statement 或者 PreparedStatement 的 executeQuery() 方法执行查询操作,并获取结果集:

ResultSet resultSet = statement.executeQuery("SELECT * FROM table_name");

处理查询结果集: 遍历 ResultSet 对象,获取查询结果:

while (resultSet.next()) { // 处理每一行数据}

3. 执行插入、更新和删除操作(INSERT、UPDATE、DELETE)

创建 Statement 或者 PreparedStatement 对象: 使用 Connection 对象创建 Statement 或者 PreparedStatement 对象,用于执行 SQL 更新语句:

PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO table_name (column1, column2) VALUES (?, ?)");

设置参数并执行更新操作: 对于 PreparedStatement,设置参数后执行更新操作:

preparedStatement.setString(1, value1);preparedStatement.setString(2, value2);int rowsAffected = preparedStatement.executeUpdate();

4. 关闭资源

关闭结果集、Statement 和 Connection: 在操作结束后,关闭打开的结果集和数据库连接:

resultSet.close();statement.close();connection.close();

参考这篇:

JDBC的六大步骤(增、删、改、查的具体代码)_jdbc的查询步骤是什么,添加修改删除的步骤是什么,数据库操作对象有什么区别,getst-CSDN博客

后端可分为:控制层(controller),业务层(service),持久层(Dao)三个部分

数据库就是执行增删改查等功能

增删改为一部分,查单独为一部分

增删改:

当进行增删改操作的时候,数据从前端来,经过controller,service,Dao层,然后把数据增删改到数据库查询操作的时候与增删改相反,从数据库里面查询数据,经过这三层,在前端显示出来

查:

数据库与后端连接的过程称作JDBC的过程,(看图)数据库到Dao层就是数据库到与后端的连接过程,一共六个步骤:Dao层到service层,直接return service层到controller层,return;然后把数据送到前端显示出来

步骤:

装载驱动程序

建立连接

操作数据:创建语句,执行语句,处理结果

释放资源

用代码写一个本地的脚本,然后怎么去执行

【Linux】编写一个 shell 脚本&执行-CSDN博客

1 创建脚本文件:

首先,文本编辑器创建一个新的文件。

这个文件通常会有 .sh 的扩展名,以表明它是一个shell脚本。

创建一个名为 myscript.sh 的文件。

vim myscript.sh

2 编写脚本内容

3 保存并退出编辑器(esc + :wq)

4 为脚本添加执行权限才能执行它(chmod)

chmod +x myscript.sh(这里+a,+x等可以执行的操作字母都行吧)

5 运行脚本

./myscript.sh

SpringBoot的基本实现原理?

参考链接:

Spring Boot的工作原理-CSDN博客

核心原理:

基于spring框架,基于自动配置和约定优于配置快速构建和部署spring应用

看主类:

一个是创建SpringApplication对象,一个是调用实例的run方法

Mybatis都用过哪些注解?

mybatis常用的注解大全(持续更新)_mybatis常用注解-CSDN博客

1 增删改查类的注解

@Select

@Delete

@Insert

@Update

@Options 设置缓存时间,能够为对象生成自增的key

2 注解

@Mapper

@MapperScan

如果想要每个接口都要变成实现类,那么需要在每个接口类上加上@Mapper注解,比较麻烦,解决这个问题用@MapperScan(添加位置:是在Springboot启动类上面添加)

HTTP协议有哪些基本方法?

http1.0三种请求方法:get获取资源 post 传输实体文本 head 获得报文首部

http1.1新增方法:opnins询问支持的方法 put传输文件 patch 局部更新文件delete删除文件 trace 追踪路径connect 要求用隧道协议连接代理

GET和POST的区别?

HTTP 基础:详解 GET 和 POST 请求的关键区别_前端_Apifox_InfoQ写作社区

他们相对应的特点就是他们的区别

get请求(获取资源)-------参数会附加在url的末尾

特点:

可从服务器获取资源

参数暴露在url中 ,存在安全隐患

受url长度的限制,无法传输大量的数据

get请求具有幂等性,多次请求得到的结果相同

响应结果可被浏览器缓存

post请求(向服务器提交数据,或者创建新的资源)-----------数据会放在请求体body中 传输 而不是暴露在url里

特点:(和get对应着来)

向服务器提交数据,或者创建新的资源

不暴露在url里,参数在请求体中传输,安全隐蔽

请求体大小没有限制,可大量传输数据

post请求默认不具有幂等性,多次请求可能得到不同的结果

响应结果默认不会被浏览器缓存

如何选择 Post 请求和 Get 请求?(从他们的特点考虑)

获取资源------get

提交数据 创建新的资源 -------post

数据包含敏感信息(比如密码)或数据量大----post

要支持url收藏和分享 使用get(响应结果可被浏览器缓存)

获取资源列表: GET /resources

获取单个资源: GET /resources/:id

创建新资源: POST /resources

更新资源: PUT 或 PATCH /resources/:id(根据id更新单个)

删除资源: DELETE /resources/:id(根据id删除单个)

有没有进行过什么测试?(我做的黑马点评)

参考两篇文章:

【软件测试】Jmeter性能测试(性能测试,Jmeter使用与结果分析)_jmeter测试-CSDN博客

全网最详细的Postman接口测试教程(完整版)_postman使用开发者文档-CSDN博客

脏读,幻读,不可重复读

赃读

(我的理解)两个事务,一个事务的数据还没有提交就被另一个事务读出来了

补充:就是读到了别的事务回滚前的脏数据,比如事务B执行过程中修改了数据x,还没有提交的就被事务A读到了,而事务B却回滚了

幻读(针对insert)

(我的理解)上一次读到的数据和上一次读到的数据的数量不同

补充:当前事务读第一次取到的数据比后来读取到的数据条目少

不可重复读(针对update或delete)

后面读到的数据和之前的不一样

事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。

为什么选择测开

首先,我认为的测开是测试和开发工作都在做的。一方面,据我了解,在近几年,国内对软件测试越来越重视,并且从用户角度来说,对于同类产品,可能会更加注重产品的质量和服务,所以我觉得测试的发展前景是非常好的。其次,测试在一个项目开发的过程中是非常重要的一环。测试人员的责任非常大,责任越大成就感就越大。我很喜欢这样的工作。另一方面,测开还有一部分开发工作,无论是自动化脚本还是测试工具或框架,都提高了测试的效率,为质量效率保证工作提供了有力的保障。并且测开的所需技术广度也是很高,所以我认为测开会激发我对这个岗位的热爱和持续学习的态度。(并且来说,我目前具备了一些测开所必备的理论知识和技能并且还在不断地学习中,我认为我可以较快地胜任这个岗位。)

怎么理解测试开发:

首先从岗位名字来看,要求测开工程师即要懂测试,又要懂开发。其次这个岗位融合了开发角色和质量意识,要求我们兼具开发人员的技能和测试人员的思维。总的来说,测试开发工程师的定位就是保障产品的质量和提高测试的效率。

购物车场景设计测试用例

给你一个购物车模块,你会如何设计测试用例?【测试用例设计】_购物车设计测试用例-CSDN博客

输入url到展示界面的流程(键入网址到网页,期间发生了什么?)

非常非常常见的面试题了在哪都能看到

2.2 键入网址到网页显示,期间发生了什么? | 小林coding (xiaolincoding.com)

从输入URL到页面展示的详细过程(从输入url到页面展示到底发生了什么)_程序实现批量滑动打开网页到底并另存为mhtml-CSDN博客

大概流程:

1 输入url地址

2 应用层进行dns解析

3 应用层生成http请求报文

4 传输层建立TCP连接

5 网络层使用ip协议来选择路线

6 数据链路层实现网络相邻节点间可靠的数据通信

7 物理层传输数据

8 服务器处理反向传输

9 服务器返回一个HTTP响应

10 浏览器渲染

剩下的太多了看之前总结的面经和链接

什么是死锁,产生死锁的原因及必要条件

什么是死锁?死锁如何解决?-CSDN博客

什么是死锁

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。

通俗一点就是

当多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进 ,这种情况就是死锁

若没有外力作用 锁死涉及到的线程都会处于永远封锁状态

死锁产生原因:

竞争资源引起进程死锁

可剥夺资源和不可剥夺资源(这个啥意思呢,可剥夺资源----->某进程获得这类资源后,这个资源可再被其他进程或系统剥夺 不可剥夺资源,当系统把这类资源分配给某进程后,再不能强行收回)

竞争不可剥夺资源

竞争临时资源

解决死锁的方法:

如果不同的程序会并发存取多个表,尽量以相同的顺序访问表,可大大降低死锁机会【就相当于维护秩序吧,避免同时并发很挤容易死锁】

在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率

对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率。

如果业务处理不好,可以用分布式事务锁或者使用乐观锁。

如何确保 N 个线程可以访问 N 个资源,同时又不导致死锁?

根据产生死锁的原因去针对性解决这个问题:

指定获取锁的顺序,并强制线程按照指定的顺序获取锁

所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了

自己设计测试用例(唉就根据日常生活来联想回答)

设计测试用例(万能思路 + 六种设计用例方法)(详细 + 图解 + 实例)-CSDN博客

万能思路:功能测试 + 界面测试 + 性能测试 + 兼容性测试 + 易用性测试 + 安全测试。

总结:

功能测试:

水杯:装水、喝水...

注册场景:注册 + 登录

想象日常使用中的注册场景有哪些功能。

界面测试:

非软件:颜色、形状、大小、材质、整体

软件:

文字/输入框/图片/下拉框 ——> 颜色、大小、形状、布局都要进行测试;

文字是否存在错别字、病句、折叠、重叠...

性能测试:

水杯:耐热性、耐寒性、耐摔性、抗压性...

软件:

响应时间

几千万人同时访问...

兼容性测试:

水杯:水杯可以装液体,针对不同液体。

软件:

系统:Linux、Windows、Mac;

终端:PC、移动端

浏览器:Chrome、FireFox、Safari...

易用性测试:

需要具备便捷、简单易上手的属性,用户引导、符合用户使用习惯。

安全测试:

水杯:

水杯的材质是否安全;

特殊情况下(高温低温)材质是否会释放毒性;

存放特殊的液体会不会导致化学反应,材质是否会释放毒性;

软件:

SQL注入

XSS漏洞

越权(垂直越权、水平越权)

等价划分法:将所有可能的输入数据划分成若干个等价类,从等价类中选出一个测试用例,如果这个测试用例通过,就认为这个等价类通过(就相当于等分然后随机取样来检测局部测试结果就代表整个测试结果了)

有效等价类(合理的、有意义)

无效等价类(无意义的)

还有一个边界值,判定表法

Java创建线程的方式

自己公众号做过的笔记:

终于学到进程线程部分了!!!

一 ,继承于Thread类

步骤:

1.创建一个继承于Thread类的子类

2.重写Thread类的run() --> 将此线程执行的操作声明在run()中

3.创建Thread类的子类的对象

4.通过此对象调用start()执行线程

二 实现Runnable 接口

创建一个实现了Runnable接口中 的类

实现类去实现Runnable 的抽象方法 run()

创建实现类的对象

将此对象作为参数传递到Thread类的构造器中 创建Thread类

通过Thread类的对象调用start()

1 启动线程

2 调用当前线程的run() 调用Runnable类型 的target 的run()

三 ,实现Callable接口

步骤:

1.创建一个实现Callable的实现类

2.实现call方法,将此线程需要执行的操作声明在call()中

3.创建Callable接口实现类的对象

4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象

5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()

6.获取Callable中call方法的返回值

四 使用线程池

五 使用匿名类

反射

Java基础之—反射(非常重要)-CSDN博客

反射就是把java类中的

各种成分映射成一个个的java对象(就如同照镜子一样)

呃就是在运行状态中对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意方法和属性。就是反射(都知道都能调用)

获取class对象的三种方式

Object———>getClass()

任何数据类型都有一个“静态”的class属性

通过class类的静态方法 forName(String className)

黑盒白盒测试

黑白测试与白盒测试及其方法-CSDN博客

白盒测试(结构测试)就是程序员因为状态不好或者对工具的使用不熟练造成的代码问题,对这些问题进行测试

补:白盒是可视的,清楚盒子内部的东西以及以及里面是如何运作的,黑盒是不可视的,

黑盒测试就是对功能进行测试,通过测试来检测每个功能是否能正常使用

测试中把程序看作一个不能打开的黑盒

在完全不考虑程序内部结构和内部特性的情况下,在程序接口进行测试

什么是装饰器(python里面的内容)

python语言的一种特性

它允许在 不修改已有函数 的情况下,向函数 添加帧外 的功能。

装饰器本质上是一个函数,它接受一个函数作为参数,并返回新的函数

本质函数 接受函数做参数 返回新的函数

什么是单例模式

什么是单例模式?单例模式有什么作用?为什么要用单例模式_单例模式的作用-CSDN博客

设计模式里面最简单的一种,我只记得这个

首先它是创建型模式

其次它涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建

单例类———->只能有一个实例

单例类————>必须自己创建自己的唯一实例

必须给其他所有对象提供这一实例

如果在系统中不使用单例模式的话,在碰请到多线程访问的时候,printer就会给要求的类,分别在内存中new出一个printer对象,让这些请求的类去做print方法。这样大量占有内存,就会导致系统运行变慢,像电脑的CPU一样,占有量极高,电脑卡死不动的感觉。

因为系统的硬件设施需求变动量小,所以只能想出一个节约成本 的方法就是,单例模式,让多线程处于等待的状态,一个 一个的去解决,这样,即节约内存,提交了运行的成本。也就是单例存在的意义

Python如何实现多线程

Python中线程与进程_python爬虫的线程进程-CSDN博客

看下面这个

### 1. 导入 threading 模块

首先,需要导入 Python 的 threading 模块:

import threading

### 2. 定义线程执行的函数

创建一个函数,这个函数包含了线程将要执行的任务。例如:

  1. def task(thread_name):
  2. print(f"Thread {thread_name} is running")

这个函数接受一个参数 thread_name,用于显示线程的名称。

### 3. 创建线程对象

使用 threading.Thread 类创建线程对象,指定要运行的函数和参数。例如,创建两个线程:

  1. thread1 = threading.Thread(target=task, args=("Thread-1",))
  2. thread2 = threading.Thread(target=task, args=("Thread-2",))

### 4. 启动线程

通过调用线程对象的 `start()` 方法来启动线程:

  1. thread1.start()
  2. thread2.start()

### 5. 等待线程结束

如果需要等待线程执行完毕,可以使用 join() 方法:

  1. thread1.join()
  2. thread2.join()

这样主线程会等待 `thread1` 和 `thread2` 执行完毕后再继续执行。

### 解释

- task 函数模拟了线程要执行的任务,这里简单地打印线程名,并休眠 2 秒钟。

- 创建了两个线程 thread1 和 thread2,分别执行 task 函数。

- thread1.start() 和 thread2.start() 启动了这两个线程。

- thread1.join() 和 thread2.join() 确保主线程等待 thread1 和 thread2 执行完成后再继续执行。

并行与并发

并发:有处理多个任务的能力,不一定同时处理

并行;有同时处理多个任务的能力

进程和线程的关系:

一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。(即所属关系)

资源分配给进程,同一进程的所有线程共享该进程的所有资源。

CPU分给线程,即真正在CPU上运行的是线程。



Python的垃圾回收机制

python面试题7:Python垃圾回收机制(难度--中等)-CSDN博客

垃圾回收机制是一个复杂的系统,用于管理不在使用的内存(垃圾回收嘛),防止内存泄漏并提高程序的性能,通常由引用计数,标记–清除,分代收集算法来处理循环引用等问题。

1 引用技术

2 标记-清除

通常分为两个阶段:

第一 阶段从根对象出发,遍历所有可达对象的“活动对象”并打击对象

第二阶段清除阶段,释放所有未标记对象的空间

3 分代收集

对于新对象进行高频率的检查,老对象则采用低频率检查,因而往往新对象更容易成为垃圾。

主要目的是为了减少垃圾回收的开销。

TCP/UDP区别

TCP面向连接 UDP不面向连接

TCP可靠传输 UDP不可靠,尽最大努力交付

TCP面向字节流 UDP面向数据报文

TCP支持点对点通信 UDP一对一,一对多,多对多

TCP报文首部20个字节,TCP报文首部8个字节

TCP有拥塞控制机制 UDP没有

TCP协议下双方发送接受缓冲区都有,UDP并无实际意义上的发送缓冲区,但是存在接受缓冲区

HTTP/HTTPS区别

HTTPS和HTTP的区别主要如下:

  1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

  2、http是超文本传输协议,信息是明文传输; https则是具有安全性的ssl加密传输协议。

  3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

  4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

GET/POST区别

get特点:

可从服务器获取资源

参数暴露在url中 ,存在安全隐患

受url长度的限制,无法传输大量的数据

get请求具有幂等性,多次请求得到的结果相同

响应结果可被浏览器缓存

post特点:

提交资源或创建新的资源

参数在请求体body中,比较安全隐蔽

不受url长度的限制 可以传输大量的数据

post请求不具有幂等性,多次请求的结果不相同

响应结果不被浏览器缓存

TCP三次握手和四次挥手,以及中间就是会怎样

简单理解TCP三次握手四次挥手(看一遍你就懂)-CSDN博客

最开始的时候客户端和服务器都是处于CLOSED关闭状态。主动打开连接的为客户端,被动打开连接的是服务器。

TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了 LISTEN 监听状态

第一次握手 TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT 同步已发送状态

第二次握手 TCP服务器收到请求报文后,如果同意连接,则会向客户端发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了 SYN-RCVD 同步收到状态

第三次握手 TCP客户端收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED已建立连接状态 触发三次握手

有人可能会很疑惑为什么要进行第三次握手?

主要原因:防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误

第一次握手: 客户端向服务器端发送报文

证明客户端的发送能力正常

第二次握手:服务器端接收到报文并向客户端发送报文

证明服务器端的接收能力、发送能力正常

第三次握手:客户端向服务器发送报文

证明客户端的接收能力正常

如果采用两次握手会出现以下情况:

客户端向服务器端发送的请求报文由于网络等原因滞留,未能发送到服务器端,此时连接请求报文失效,客户端会再次向服务器端发送请求报文,之后与服务器端建立连接,当连接释放后,由于网络通畅了,第一次客户端发送的请求报文又突然到达了服务器端,这条请求报文本该失效了,但此时服务器端误认为客户端又发送了一次连接请求,两次握手建立好连接,此时客户端忽略服务器端发来的确认,也不发送数据,造成不必要的错误和网络资源的浪费。

如果采用三次握手的话,就算那条失效的报文发送到服务器端,服务器端确认并向客户端发送报文,但此时客户端不会发出确认,由于客户端没有确认,由于服务器端没有接收到确认,就会知道客户端没有请求连接。

TCP四次挥手

建立TCP连接需要三次握手,终止TCP连接需要四次挥手

举个例子

张三和李四的对话

张三:好的,那我先走了

李四:好的,那你走吧

李四:那我也走了?

张三:好的,你走吧

第一次挥手 客户端发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态

第二次挥手 服务器端接收到连接释放报文后,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT 关闭等待状态

第三次挥手 客户端接收到服务器端的确认请求后,客户端就会进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文,服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

第四次挥手 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态,但此时TCP连接还未终止,必须要经过2MSL后(最长报文寿命),当客户端撤销相应的TCB后,客户端才会进入CLOSED关闭状态,服务器端接收到确认报文后,会立即进入CLOSED关闭状态,到这里TCP连接就断开了,四次挥手完成

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/774661
推荐阅读
相关标签
  

闽ICP备14008679号