TCP学习指北
限于博主水平有限不敢说指南,但应该能够避免刚学TCP的同学出现找不着北的情况。
TCP与UDP的区别
区别:
- UDP是无连接的,而TCP是面向连接的,传数据前要先建立连接。
- UDP可以一对多,多对多通讯,而TCP只能一对一。
- UDP使用最大努力交付,即不保证可靠交付。而TCP是可靠交付数据,并且有拥塞控制和流量控制机制。
- UDP是面向报文的,适合一次性传输少量数据。而TCP是面向字节流的
总的来说UDP不太靠谱,自管把数据发出去,丢了也不管。就好像两个人说话,其实一个人只管自己说自己的。
相比之下,TCP就靠谱多了,它通过序列号和确认应答机制保证连接的可靠性,任何一方向对方发送消息(SYN),都要收到回应(ACK)。相当于两个人正常聊天,接下来的谈话内容会根据对方的反应做出调整,比如对方开小差没有听到你刚才讲什么,就重复一遍刚刚说的话。
上图写的只是相对序列号(相对于初始序列号,序列号如果一直是一个固定值,会增加被攻击的风险),初始序列号(ISN)的确定是在连接建立也就是三次握手阶段。
三次握手
同步序列号
握手的首要目的是告知对方自己的初始序列号,只有同步了序列号才能实现可靠传输,比如拥塞控制,消息重发等等。
虽然两次握手就能同步双方的序列号,但无法保证客户端是否接收到了服务端发过去的初始序列号,所以我们需要进行第三次握手来确认。
双方都需要确认一次自己发过去的序列号对方有没有收到,三次握手之后双方就都能明确自己和对方的收发能力是正常的。
实际上任意次的握手都不可靠,三次是双方互相明确对方收发能力的最低值。这 一点可以看看两军问题
第一次握手:客户端在给服务端数据通信之前,会发送连接请求并等待确认应答,此时TCP首部SYN为1,seq为随机生成的序列号(X)。
第二次握手:服务器接受到连接请求之后,会进行答复这时SYN仍为1,确认应答号ack为X+1,表示你发过来的请求我接收到了。同时服务器也会生成一个随机序列号y写入seq,等待客户端的确认应答。
第三次握手:前两次都是通过TCP首部发送SYN包作为建立连接的请求,并未携带数据。三次握手的最后一次握手可以在报文段负载中携带数据,并且因为连接已经建立,所以在之后的报文中SYN都为0(包括最后一次握手),双方正式开始通信。
前两次握手都不能携带数据,并且需要消耗掉一个序列号,也就是seq要加一。
第三次握手可以携带数据,如果不携带数据则不消耗序列号。也就是说三次握手结束后,客户端发送的第一个报文中的seq仍然为x+1
实际上,本来是四次握手的
Alice ---> Bob SYNchronize with my Initial Sequence Number of X
Alice <--- Bob I received your syn, I ACKnowledge that I am ready for [X+1]
Alice <--- Bob SYNchronize with my Initial Sequence Number of Y
Alice ---> Bob I received your syn, I ACKnowledge that I am ready for [Y+1]因为二三步的接收对象相同,所以可以合为一步
认识TCP首部
突然出现SYN,seq这些字母你可能感觉晕乎乎的。特别是ACK和ack,更是让人傻傻分不清楚。现在就来认识一下他们吧。
图中的序列号就是seq(发送数据的顺序编号),它的值是本报文段所发送数据的第一个字节的序号。比如这次发送的数据包是由文件的第1-100个字节,那么seq的值为1.
小写ack对应确认应答号,表示期望收到对方下一个报文段数据的第一个字节的序号。比如已经收到1-100个字节数据后,那么确认应答号就应为101,这样发送端下次就会发送101字节及其之后的数据过来。
需要注意的是只有控制位ACK为1时,确认应答号ack才有效。
防止历史连接扰乱通讯
如果你有过打游戏突然掉线或者异常卡顿的经历,我相信你对网络通讯并不像“如意如意,随我心意”那么顺利有着深刻的认识
假设我们对服务器发送了连接请求,但因为物理链路选择的问题,迟迟没有到达。超过了等待时间之后,重发了一次请求,这一次很快就顺利建立起连接了。但没过多久之前那个请求报文风尘仆仆的来到服务器这,也建立起了连接。
不过还好初始序列号生成的算法比较巧妙:
ISN=M+F(本地IP,本地端口,目的IP,目的端口)
M是过四毫秒就加一的计时器,F是根据上面四个参数进行映射的哈希函数
这样就能保证客户端能过凭借序列号判断这是否是一个过期连接(通过跟记住的最后接收到的seq进行比对)。
如果是,则发送RST报文给服务端,终止这个过期连接。不会让服务端还傻傻的等着客户端发数据,避免了资源的浪费。
从这里也可以看出,序列号seq是多么重要。因为TCP没有时间戳,所以通过同步双方的序列号来“校准双方的时间”就显得尤为重要。
TCP的可靠连接是通过序列号和确认应答来实现的
SYN洪水攻击
SYN洪水攻击(SYN flood attack)利用了TCP协议 3次握手的原理,攻击者发送大量建立连接的网络包,但却不实际建立连接(不进行第三次握手),导致服务器的连接资源耗尽。
这种攻击因为SYN cookie技术的产生,在现代网络已经不奏效了。
SYN cookie的具体做法是:
服务器只在握手完成之后分配资源,在这之前,它会根据特殊的算法生成一个cookie(seq序列号)发给对方。服务器本身并不记忆该cookie。
因为该cookie的生成与对方的IP和端口号有关,所以服务器可以凭此重新算出cookie,并与发过来的报文段中的ack进行比对。
如果对方是合法的连接,服务器就生成一个具有套接字的全开连接。如果不合法,那咱也不亏,服务器没有为它分配过任何资源。
一般SYN cookie只在半连接队列爆满的情况下才会启用。除此之外,常见的防范方式还有SYN Proxy防火墙,Syn Cache技术等。
多亏了序列号和确认应答
滑动窗口
如果传输比较大的数据,一般会将其分为小包,进行分段传输。如果每发送一个数据包都要等待对方确认之后再发下一个,这段时间内什么都不做,看起来太浪费了。
TCP采用滑动窗口来避免资源的浪费:在等待ACK号的这段时间,继续发送后面的数据包。如果收到了ACK号,那么就将该ACK之前的数据包从缓存中清除。
不过这样做有些问题,因为对方可能无法在短时间内处理大量的数据包,导致后来的都被丢弃(缓冲区溢出)。所以在连接的时候,双方应该要明确发送和接收能力。
在TCP首部的可选字段中,双方会协商好能够接收的最大数据量(一般称为窗口大小),它的值一般与接收方缓冲区大小相等。
连接的两端都有各自的接收缓存和发送缓存,无论哪方都能同时收发信息,因此TCP提供的是全双工服务。
拥塞控制
拥塞控制的目的是防止过多数据传输,使得传输链路过载,甚至造成瘫痪。在网络畅通的时候,我们就多发点,猛踩油门;网络差的时候,就别一个劲的发送大量报文添堵了。
TCP采取慢启动、拥塞避免、快速重传、快速恢复来进行拥塞控制。
- 慢启动:
刚建立起的连接,拥塞窗口大小从默认初始值开始,大胆进行试探性指数增长。 - 拥塞避免:
当达到慢启动阈值(ssthresh)时,再进行指数增长显得有点鲁莽了。这时候我们改变策略,采取加法的形式谨慎增加cwnd - 快速重传:
当发现收到四个相同的ACK时(检测到三个冗余ACK),进行快速重传。即立马发送这个丢失的报文段,无需等待超时重传定时器。 - 快速恢复:
发生快速重传之后就会进入快速恢复阶段,此时ssthresh=cwnd/2,cwnd=cwnd/2,之后迁移到慢启动阶段。
四次挥手
断开连接的步骤居然要比建立连接还多,这是怎么回事?难道不能像握手那样:
- 客户端告诉服务器我要断开连接了
- 服务端回复好的,并且告诉客户端我也准备断开了
- 客户端收到服务端的确认之后,再给服务端发送一个确认消息
这样三步不就解决了吗?
断开连接与建立连接还是有些不同的,因为建立连接之前,双方是没有任何通讯的。但在结束连接的时候,双方可能还存在着没有传输完成的数据包。
所谓上山容易下山难,多加一次挥手是为了防止出现误操作。
如果是三次挥手,这边客户端要求服务器重发的请求刚出去,就收到对面说断开连接的消息,实在是让人错手不及
所以我们得分四步进行。
并且连接关闭之后,并不会马上删除相关数据。这是为了防止乌龙事件的产生。
如果因为网络原因,最后一步ACK丢了。服务器迟迟未收到客户端对其断开连接的确认,重发了FIN。
但好巧不巧,客户端的新连接就建立在这个之前断开的端口号上。得,执行断开操作,全乱套了。
TCP学习指北的更多相关文章
- Git学习指北
learnGitBranching:一个可视化学习 git 的网站 learngitbranching.js.org,虽然项目有些悠久,如果学习 git 的话可以来玩下
- 后端API入门到放弃指北
后端API入门学习指北 了解一下一下概念. RESTful API标准] 所有的API都遵循[RESTful API标准]. 建议大家都简单了解一下HTTP协议和RESTful API相关资料. 阮一 ...
- Python 简单入门指北(二)
Python 简单入门指北(二) 2 函数 2.1 函数是一等公民 一等公民指的是 Python 的函数能够动态创建,能赋值给别的变量,能作为参传给函数,也能作为函数的返回值.总而言之,函数和普通变量 ...
- Python 简单入门指北(一)
Python 简单入门指北(一) Python 是一门非常容易上手的语言,通过查阅资料和教程,也许一晚上就能写出一个简单的爬虫.但 Python 也是一门很难精通的语言,因为简洁的语法背后隐藏了许多黑 ...
- 可能比文档还详细--VueRouter完全指北
可能比文档还详细--VueRouter完全指北 前言 关于标题,应该算不上是标题党,因为内容真的很多很长很全面.主要是在官网的基础上又详细总结,举例了很多东西.确保所有新人都能理解!所以实际上很多东西 ...
- [转] iOS开发者的Weex伪最佳实践指北
[From] http://www.cocoachina.com/ios/20170601/19404.html 引子 这篇文章是笔者近期关于Weex在iOS端的一些研究和实践心得,和大家一起分享分享 ...
- JavaScript 词法作用域不完全指北
在 JavaScript 作用域不完全指北 中,我们介绍了作用域的概念以及 JavaScript 引擎.编译器和作用域的关系.作用域有两种主要的工作模型:词法作用域和动态作用域.其中最为普遍的也是大多 ...
- [Android Studio] 2019年Android Studio配置指北
Android Studio是我学习Android开发路上的第一块绊脚石,新建一个项目,一行代码没动,直接编译不起来,我太难了,所以本文叫指北 本文讲解在9102年如何在国内网络不通畅的情况下流畅的使 ...
- Markdown 标记语言指北 - 源码
这是上一篇博客的源代码. 这是班刊约稿的一篇文章. 全文约6000字, 预计需要 60 分钟读完. # Markdown 标记语言指北 #### TOC 1. [什么是 Markdown?](#%E4 ...
随机推荐
- 2. import 与 from...import 导入模块
1. 导入整个模块 格式: import somemodule2. 从某个模块中导入某个函数 格式: from somemodule import somefunction3. 从某个模块中导入多个函 ...
- (1)为什么要使用webpack?
1.在网页中有哪些常见的静态资源? Js: .js .jsx .coffee .ts Css: .css .less .sass .scss Images: .jpg .png .gif .bmp . ...
- Websphere修改web.xml不生效的解决办法(转)
在websphere下部署了一个java工程后,如果修改了web.xml文件,重新启动这个java工程发现websphere并没有自动加载web.xml文件,即修改后的web.xml并不起作用,除非重 ...
- Ethical Hacking - NETWORK PENETRATION TESTING(15)
ARP Poisoning - arpspoof Arpspoof is a tool part of a suit called dsniff, which contains a number of ...
- 面试官:连Spring三级缓存都答不好,自己走还是我送你?
面试官:简历上写了精通Spring,那你回答一下Spring为什么用“三级缓存”去解决循环依赖? 我:.......应该有三个缓存的map结构 面试官:具体回答一下 我:平时没认真深入过 面试官:公司 ...
- JQuery如何在验证表单失败的情况下阻止表单提交
自定义验证时,使用了return false和event.preventDefault(),但是验证失败之后表单还是提交了 这个问题我也碰到了,尝试了多次也没有用,在调试的时候也发现确实return了 ...
- JAVA I/O基本操作
JAVA I/O基本操作 JAVA文件操作 JAVA字节流 JAVA字符流 JAVA缓存流 JAVA对象流 JAVA数据流 本文主要借鉴以下博客和网站: how2j.cn 深入理解java中的I/O ...
- 聊一聊Flutter的setState()
Flutter 里面包含两种widget 一种可变的,一种不可变的: 在可变的widget中可以使用 setstate(){} 函数. 官方也给出了例子: _onClick(){ setState() ...
- 一文了解JDK12 13 14 GC调优秘籍-附PDF下载
目录 简介 那些好用的VM参数 G1的变化 配置FlightRecorder RAM参数 JDK13中的ZGC RTM支持 总结 简介 想了解JDK12,13,14中的GC调优秘籍吗?想知道这三个版本 ...
- matplotlib绘制子图
fig,subs = plt.subplots(2,2) subs[0][0].plot(data_math_C1) subs[0][0].set_title('C_1 曲线') subs[0][1] ...