TCP 三次握手的意义

概述

在网络的传输层协议中, 存在着两大悍将: TCP 和 UDP . 从前, 我傻傻的以为自己对他们虽谈不上精通, 但还是知道的, 但是, 我错了, 我被自己问住了, 我傻了. 啥也不是.

UDP

(这里为了介绍简单, 就不提数据在传输过程中的失真(纠错码)等情况了. 简单介绍一下, TCP才是今天的主角)

UDP 就是, 我把数据发给你了, 我不管你有没有收到, 反正我发出去了, 任性. 就比如我要给我的女神表白, 但是我又不好意思, 所以我托我的好兄弟马六帮我给女神带句话, 但是这个马六也脸皮薄, 他又找周三转达, 就这样虽然历经波折, 但最后还是顺利的将话带到了女神那里. 在这个过程中我做了什么? 我只是将消息送出去了, 仅此而已. 最后我满心欢喜的等待着女神的回复, 可能换回一句: 我们还是做朋友吧. 但还有一种可能, 那就是最终根本就没有送到女神那里, 中间转到周三的时候, 他因为自己的事情, 把这事给忘了, 可怜的我还苦苦的等...

UDP 虽然省事, 高效, 但是却不可靠. 因为我仅仅是发出去了, 但是我不确定你有没有收到. 不可靠有什么问题么? 上面就是个例子. 再比如, 咱俩聊天, 我给你发了一段话: 123456, 结果中间4丢了, 你收到的信息是: 12356. 这种还好, 如果快过年了, 我发给你这样一段话: 明天把你的猪宰了吧, 哎, 中间 的猪 丢了, 那估计免不了一番腥风血雨.

那如此不可靠的UDP协议, 有什么用呢? 还真有, 虽然不可靠, 但是他快啊. 在一些对数据的可靠性要求不高, 但是实时性很强的地方就有了用武之地, 比如视频电话(我也不知道底层是不是 UDP, 举个例子), 打视频电话的时候, 视频要保证其连续性, 而且中间如果丢了一帧也不会有什么影响.

但是在大多数场景下, 数据的可靠性还是要有保证的, 你从网上下载一个程序的安装包, 如果中间丢了一个字节的数据, 那可能就导致一个200mb 的文件废了, 根本不能执行.

TCP

为了保证传输数据的可靠性, TCP 诞生了. 还记得刚才我给女神表白的时候, 问题出在哪里吗? 没错, 就是因为我到最后苦苦等待, 结果她悲剧的没有收到我的心意, 伤心. 怎么办呢? 这次我想通了, 求人不如求己, 我要鼓起勇气, 我到她面前当面告诉她, 即使我多了一个朋友(没办法, 咱就喜欢交朋友), 也好过她收不到消息的好. 这下可靠了, 我确信她收到了.

区别在哪里? 不是我到他面前, 而是不管她是否愿意, 至少给爷们回句话吧. 没错, 就是回句话.

如果有这样一种机制, 每次我发出去的数据, 如果对方收到了, 就给我回句话, 告诉我收到了, 那消息就变得可靠了. 我发出去的所有消息, 都可以确信对方已经收到了.

  • 如果数据中间丢了, 对方没有收到怎么办? 没有等到对方回复, 我就重新发一遍就好啦.
  • 如果对方的回复丢了, 没有收到回复怎么办? 处理方式同上, 对方收到重复数据, 把重复的数据包丢弃再回复一条就好啦.

一个来自灵魂的提问, 现在的数据发送可靠吗? 我觉得是不可靠的, 现在仅仅能够保证一个数据包, 我百分百的确信对方已经收到了. 那什么样的连接才是可靠的呢?

我要发你100个数据包, 那这100个数据包你每一个都要能够收到, 并且要按照顺序将他们再拼装起来, 我觉得这样的连接才能称得上可靠. 这里面涉及到了两个概念, 确保收到 和 顺序. 确保收到我们已经做到了, 如何保证包的顺序呢? 我把要发的数据排排队, 一个一个发就行了? 天真, 如果有包1在网络中某个地方喝了杯茶, 睡了一觉, 结果接收方先收到了包2后收到包1, 顺序就乱了. 保证顺序的方式其实很简单, 在每一个包上, 都加上一个序号, 接收方按照序号从小到大把收到的包组装起来就好了.

经过改造, 现在已经基本能够保证传输的可靠性了, 到这里, 有没有发现什么? 现在和TCP的区别就是少了三次握手四次挥手(不仅仅是). 那三次握手的意义何在?

三次握手意义何在

今天在接收了身边大神的一些思想之后, 我还是没有太明白. 不过现在, 我貌似明白了些什么. 要想知道三次握手有什么用, 就需要知道三次握手都做了什么事情.

1. 确保对方能够正常接收数据, 测试连接

还是上面的例子, 我去女神面前表白, 但不凑巧, 女神正在午休, 我站着旁边傻傻的表白, 还是没有用. 所以, 在开始之前, 我要先确保女神能够听到我说的话, 我得把她叫醒, 庄重的告诉她. 而这, 就是握手的意义.

2.建立系统开销

在发送 UDP 包的时候, 因为其不可靠性, 所以基本不会用其发送很大的文件, 因为将较大的数据拆分后发出, 中间丢了几个数据包就尴尬了. 而且 UDP 也不能够保证包的顺序, 还是一样的原因. 但是 TCP 就不一样了, 它是可靠的啊, 你可以将多个数据包分开发给我, 到我这里, 我再把他们按顺序排列好就行了. 而这个按顺序排列的操作就需要专门开辟内存空间来保存收到的数据包了, 当握手成功后, 我就会为你留下用于保存数据包的内存空间及其他一些系统资源.

而如果没有三次握手呢? 客户端发送的数据包, 可能因为某些原因(比如路不好走), 在网络中待的久了一些, 客户端因为没有收到回复, 已经放弃连接了, 但这时候, 服务器收到了这个数据包, 开辟系统资源, 返回确认包, 然后就没有然后了. 客户端已经放弃了, 根本不搭理你的回复. 系统的相关资源就白白浪费了.

3. 测试超时时间

上面说了, 当我长时间没有收到你的回复时, 我就认为你没有收到我发出的数据, 那我就需要重新发送了. 那这个长时间是多久呢? 可以在握手期间进行测试, 测量请求包的往返时间,并依此计算重传的超时时间.

4.安全性

这个确实是我没有想到的. 因为 TCP 会将数据拆分后发送, 为了保证数据的有序, 就要给每个数据包进行编号. 然后接收方根据编号的顺序对收到的包进行重组, 保证了数据的有序.

如果只是简单的123456, 那大家都知道了, 我黑客小黑, 也给你发一个编号为1的数据包, 不就把你真实的数据包给偷偷替换了么? 为了防止序列号被猜到, 就要让每次发送数据的序列号不同, 在进行握手的时候会对数据的初始序列号进行交换. 客户端第一次发送握手信息的时候, 会连着自己的初始序列号一起发过去, 服务器收到之后, 返回第二个握手信息的时候, 除了返回握手确认, 也会连着自己的初始序列号一起发回来. 这在一定程度上保证了数据的安全传输. 当然这种防护措施很弱.

这个随机的序列号其实还有另外一个作用, 我觉得这才是它最主要的作用. 如果我们上一次连接的其中一个数据包3, 在网络中傲游了一会, 连接已经断开了, 我们又开始了新的一次数据连接, 这个时候我收到了数据包3, 就会导致生成了错误的数据序列, 而随机序列号则避免了这个问题,

四次挥手的意义

三次握手确实是有些作用, 那四次挥手有什么用呢?

1.释放系统资源

在三次握手的时候, 为了接收数据并进行序列重组, 开辟了一些系统资源, 当数据发送完了, 就不用一直占着了, 早些释放, 留给别人.

额, 应该还有其他作用吧...

总结

综上, 你说如果没有握手挥手的过程, 能不能实现一个可靠的连接呢? 可以, 只不过会有问题. 个人简单将握手的作用总结为以下几点:

  1. 为了对数据进行顺序重组, 势必需要开辟系统资源. 如果没有握手的过程, 所有的请求, 都要占用资源. 而没有挥手的过程, 这些资源就不能及时释放.
  2. 为了数据的高效传输, 选用一个合理的超时重传时间是十分有必要的. 时间短了, 会导致频繁重传, 浪费网络资源. 时间长了, 就会导致整体的数据传输时间变长.
  3. 为了保证对方能够正常接收数据, 否则对方关机了, 我总不能在这一直超时重传吧.
  4. 为了保证多次连接的数据包不会引发数据错误. 通过随机的序列号, 保证了两次连接的数据包不会互相影响.

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

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

相关文章

随机数是如何生成的

引出 在现实中, 会有抛硬币猜正反的操作, 硬币要么是正, 要么是反, 在揭晓之前, 我们谁也不知道它现在的状态. 而这, 是因为其中存在着很大的不确定因素, 如抛硬币的力度、抛硬币的角度、接硬币的力度和角度、硬币的重量、当前风速等等. 但是在计算机中, 要想生成一个随机数,…

GO 文档笔记

前言 最开始写 GO 的时候, 发现方法的注释并不支持param, return等参数, 搞得我都不知道该如何给自己的方法写文档说明了. 而且网上搜了搜也没有搜到教程, 甚是郁闷. 今天找到了GO内置的文档工具: godoc. (我用的1.14.3版本貌似不是自带工具了, 需要安装(配置代理): go get g…

长整数的乘法运算

概述 都知道, 计算机中存储整数是存在着位数限制的, 所以如果需要计算100位的数字相乘, 因为编程本身是不支持存储这么大数字的, 所以就需要自己实现, 当然了, 各个编程语言都有大数的工具包, 何必重复造轮子, 但我还是忍不住好奇他们是如何实现的, 虽然最终没有翻到他们的底层…

如何将数字转换成口语中的文本串

概述 今天突发奇想, 写一个将数字转换成中文字符串的函数. 并不是将 1234 转成 1234 , 而是将 1234 转成 一千二百三十四. 本来以为很简单, 写下来之后发现还是有些坑的. 尝试 因为我是在写完最终版本, 回过头来整理的这篇文章, 所以中间很多尝试的步骤会有所遗漏. 以下简单…

设计模式故事会

想着用讲故事的方式对各个设计模式进行一个稍微形象一些的描述. 当然, 某些因为没有想到特别好的例子, 可能比喻的并不是那么的恰当, 望赐教. 绞尽脑汁啊, 尽力贴近设计模式其本来的意图. 主要为了加强一些自己对设计模式的理解. 进入正题: 小王是一名心怀梦想的90后青年, 他独…

有了 elseif 为什么还要 switch case

引出 你有没有想过既然有了if elseif, 为什么还要设计一个switch case的语法出来呢? 按理说, 一个语言的设计角度来说, 关键词越少越好吧, 而且多出来一种选择分支也没有看出太大用处. 以下几种switch case均可以写成if else的形式(java 代码): // 形式一 switch(a){case 1:…

计算矩阵中全1子矩阵的个数

前言 最近被我大哥安利了一道算法题, 这道题说难, 还不至于我做不出来, 说简单吧, 我还想不到最优解, 等把最优解告诉我之后, 我还正好能理解. 我甚至曾经怯怯的认为, 这题就是我哥专门给我找的, 嘿嘿, 心中说不出的小欢喜. 题来了, 此题出自力扣, 原题链接: https://leetco…

GO 的方法集

前言 之前在写 GOdemo 的时候, 写了这么一段程序(大概意思): package maintype Test struct { }func (test *Test) print() {println("test fun") }func main() {Test{}.print() }结果一编译就报错了: cannot call pointer method on Test literal 差不多意思是不…

GO 切片实力踩坑

概述 GO 语言的切片这两天用了用, 可以支持切割数组的中间部分. 但今天使用中, 出了 bug, 查了半天, 发现是切片的问题, 简单写个 demo 复现当时的情况: package mainimport "fmt"func main() {a : []int{1, 2, 3, 4, 5}b : a[2:4]b[0] 9fmt.Println(a) }你以为输…

搭建本地 HTTPS 环境

前言 之前写自己的网站时, 申请过免费的https证书. 最近想在自己本地搭一个, 结果忘了当初证书是怎么来的了. 本来想着去申请个免费的证书, 但想了想, 我只需要在自己本地能使用就行了, 我自己的环境, 那当然是我说了算了. 只要能够将证书构造出来, 安装到本地就可以识别了. 搜…

《人人都是产品经理》读后感

之前读书, 一般看的都是技术、算法、数学、哲学这些方面的, 为什么突然想看这本书呢? 身为一个开发人员, 每天打交道最多的, 除了开发和测试, 就是产品了. 还记得我刚来公司的时候, 在和产品的沟通过程中, 听到最多的话估计就是: 你要懂产品, 你懂了就不会做错. 确实有很多需求…

nginx 端口转发

概述 这两天在写 go 项目, 一个 HTTP 服务器. 之前写的是 php 项目, nginx 监听80端口, 根据域名将请求分配给不同项目. 现在换了 go, 自然也想延续这个操作, 毕竟都是跑在同一台服务器上. 那么问题来了, 我的nginx 监听80端口的同时, go 服务器是无法同样监听80端口的. 这该如…

2020-08-20

前段时间有些迷茫, 有时看着自己写的代码感觉就像一坨屎, 有时写了一段逻辑总是感觉有问题, 测了一遍又一遍, 逻辑也要理好几次. 就像是进入了一种不可描述的状态, 这感觉让我很不自在. 敲代码的时间说长不长, 说短不短, 从大二开始, 到现在也小四年了, 还没有碰到过这种情况. …

beego 优雅重启

前言 最近在写 go 的项目, http 用的 beego 框架. 因为 go 不想 php, 每次代码改动都需要重启服务, 所以代码发上线之后, 如何重启服务就成了一个问题. 如果强行重启的话, 不光在重启期间的所有访问都被拒绝了, 而且在杀掉进程的时候处理中的请求也挂了. 对于一个向用户正常提…

由 go orm 引发的探索

前言 今天遇到了一个 bug, 是 golang 的orm导致的. 使用了gorm框架. 通过实现Scan与Value可以将数据库中的 json 内容解析出来, 免除了 字符串再解码的步骤. 当时报错的代码大概是这样的: type TestContent struct {Id intContent Content // 数据库中的 json 结构 }type Con…

MySQL 数据恢复

MySQL 数据恢复 前言 前两天因为没注意的误操作, 直接把某个数据表清掉了, 心慌慌. 怪自己学艺不精, 当时整了一下午也没把数据找回来. 当晚回来闭关研究, 终于在凌晨1点多整出来了, 特此记录, 以备不时之需. 对于 MySQL 数据的备份, 主要有两种: 全量备份和增量备份. 全量…

码云 Pages 搭建

因为一直在写博客, 就向着搭个 GithubPages 来展示, 一直都听说别人用它来搭建个人博客, 但一直停留在听说的阶段. 最近想着没事搞一搞, 也看看它到底是个什么东东. 不过咱一个写中文博客的, 就想着在码云上搭一个, 顺便还能被百度收录, 嘿嘿. 说干就干, 开搞. (Pages 服务只能…

crontab 指令笔记

前言 之前有个定时任务, 需要每天的9点和21点定时跑. 当时我是这么写的: 0 9 * * * XXX 0 21 * * * XXX当时经过高人指点, 发现这两条命令可以写成一条: 0 9,21 * * * XXX好羞耻… 于是, 我决定整理一篇crontab的语法. 防止下次再出现这尴尬的场景… 指令 命令行 crontab…

golang 多协程的同步方法总结

之前用 go 写一个小工具的时候, 用到了多个协程之间的通信, 当时随手查了查, 结果查出来一大坨, 简单记录一下. golang中多个协程之间是如何进行通信及数据同步的嘞. 共享变量 一个最简单, 最容易想到的, 就是通过全局变量的方式, 多个协程读写同一个变量. 但对同一个变量的更…

golang chan 探究

前言 之前在看golang多线程通信的时候, 看到了go 的管道. 当时就觉得这玩意很神奇, 因为之前接触过的不管是php, java, Python, js, c等等, 都没有这玩意, 第一次见面, 难免勾起我的好奇心. 所以就想着看一看它具体是什么东西. 很明显, 管道是go实现在语言层面的功能, 所以我以…