源端口号与目的端口号: 源端口号与目的端口号, 加上IP首部的源IP地址和目的IP地址惟一肯定一个TCP链接
序号: 一次TCP通讯(从TCP链接创建到断开)过程当中某一个传输方向上的字节流编号
确认号: 仅当ACK标志位1时有效. 表示指望下一个字节的序号
头部长度: 标识TCP头部有多少个32bit(4字节). 由于4位最大能表示15, 因此TCP头部长度是60字节
保留位: 6位, 必须全为0html
6个标志位:node
16位窗口大小: 经过窗口大小来达到流量控制git
检验和: 由发送端填充, 接收端对TCP报文执行CRC算法以检验TCP报文在传输过程当中是否损坏.github
TCP特色:web
选项与填充(选项为4字节整数倍, 不然用0填充)
最多见的可选字段是最长报文大小MSS(Maximum Segment Size), 每一个链接方一般都在通讯的第一个报文段中指明这个选项. 它指明本端所能接受的最大长度的报文. 若是该选项不设置, 默认为536(20+20+536=576字节的IP数据报)面试
若是两边同时断链接, 那就会就进入到 CLOSING状态, 而后到达TIME WAIT状态算法
CLOSED: 表示初始状态 LISTEN: 表示服务器端的某个socket处于监昕状态, 能够接受链接 SYN_SENT: 在服务端监昕后, 客户端socket执行CONNECT链接时, 客户端发送SYN报文, 此时客户端就进入SYN_SENT状态, 等待服务端的确认 SYN_RCVD: 表示服务端接收到了SYN报文, 在正常状况下, 这个状态是服务器端的socket在创建TCP链接时的3次握手会话过程当中的一个中间状态, 很短暂 ESTABLISHED: 表示链接已经创建了 FIN_WAIT_1: 这个是已经创建链接以后, 其中一方请求终止链接, 等待对方的FIN报文. FIN_WAIT_l状态是当socket在ESTABLISHED状态时, 它想主动关闭链接, 向对方发送了FIN报文, 此时该socket即进入到FIN_WAIT_l_状态. 而当对方回应ACK报文后, 则进入到FIN_WAIT_2状态, 在实际的正常状况下, 不管对方处于何种状况, 都应该立刻回应ACK报文因此FIN_WAIT_l状态通常是比较难见到 FIN_WAIT_2: 实际上FIN_WAIT_2状态下的socket, 表示半链接, 即有一方要求关闭链接, 但另外还告诉对方: 我暂时有数据须要传送给你, 请稍后再关闭链接 TIME_WAIT: 表示收到了对方的FIN报文, 并发送出了ACK报文, 就等2MSL. 后便可回到CLOSED可用状态了. 若是在FIN_WAIT_l状态下收到了对方同时带FIN标志和ACK标志的报文时, 能够直接进入到TIME_WAIT状态, 而无需通过FIN_WAIT_2状态 CLOSING: 这种状态比较特殊实际状况中应该是不多见属于一种比较罕见的例外状态. 正常状况下, 当发送FIN报文后, 按理来讲是应该先收到(或同时收到)对方的ACK报文, 再收到对方的FIN报文. 可是 CLOSING 状态表示你发送FIN报文后, 并无收到对方的ACK报文, 反而收到了对方的FIN报文. 为何会出现此种状况呢?其实细想一下, 也不可贵出结论:那就是若是双方几乎在同时关闭一个 socket 的话, 那么就出现了双方同时发送 FIN 报文的状况, 就会出现 CLOSING 状态, 表示双方都正在关闭 socket 链接. CLOSE_WAIT: 这种状态的含义实际上是表示在等待关闭. 怎么理解呢?当对方关闭一个socket后发送FIN报文给本身时, 系统将毫无疑问地会回应一个ACK报文给对方, 此时则进入到CLOSE_WAIT状态. 接下来呢, 实际上你真正须要考虑的事情是察看你是否还有数据发送给对方, 若是没有, 那么你也就能够关闭这个 socket 了, 发送 FIN 报文给对方, 即关闭链接. 在 CLOSE WAIT 状态下, 须要完成的事情是等待你去关闭链接. LAST_ACK: 这个状态仍是比较好理解的, 它是被动关闭一方在发送FIN报文后, 最后等待对方的ACK报文 . CLOSED: 当收到ACK报文后, 也便可以进入到 CLOSED 可用状态了
当TCP主动关闭一端调用了close()来执行链接的彻底关闭时会执行如下流程, 本端发送FIN给对端, 对端回复ACK, 本端进入FIN_WAIT_2状态, 此时只有对端发送了FIN, 本端才会进入TIME_WAIT状态, 为了防止对端不发送关闭链接的FIN包给本端, 将会在进入FIN_WAIT_2状态时, 设置一个FIN_WAIT_2定时器, 若是该链接超过必定时限, 则进入CLOSE状态;chrome
注意:上述是针对close调用彻底关闭链接的状况, shutdown执行半关闭不会启动FIN_WAIT_2定时器;数据库
从上图可知, 客户端链接在收到服务器的结束报文以后, 并无直接进入CLOSED状态, 而是转义到TIME_WAIT状态. 在这个状态, 客户端链接要等待一段长为2MSL(Maximum Segment Life, 报文段最大生存时期)的时间, 才能彻底关闭. MSL是TCP报文段在网络中的最大生存时期, 标准文档建议是2min
TIME_WAIT状态的存在缘由有两点:
第一个缘由很好理解. 假设图中用于确认服务器接收报文段6的TCP报文段7丢失, 那么服务器将从新发结束报文段(报文段6). 所以客户端须要停留在某个状态以处理重复收到的结束报文(即向服务器发送确认报文段). 不然, 客户端将以复位报文段回应服务器, 服务器认为这是一个错误, 由于它指望的是一个想TCP报文段7那样的确认报文段.
在Linux系统上, 一个TCP端口不能同时被打开屡次(两次及以上). 当一个TCP链接处于TIME_WAIT状态时, 将没法当即使用该链接占用着的端口来创建一个新链接. 反过来思考, 若是TIME_WAIT状态不存在, 则应用程序可以当即创建一个和刚关闭的链接类似的链接(这里说类似, 是指他们具备相同的IP和端口号). 这个新的和原来类似的链接被称为原来的链接的化身. 新的化身可能接受属于原来的链接, 携带应用程序数据的TCP报文段(迟到的报文段), 这显然是不该该发生的. 这就是TIME_WAIT状态存在的第二个缘由
另外, 由于TCP报文段的最大生存时间是MSL, 因此坚持2MSL时间的TIME_WAIT状态可以确保网络上两个传输方向上还没有被接收到的, 迟到的TCP报文段都已消失(被中转路由丢弃). 所以一个链接的新的化身能够在2MSL时间以后安全地创建, 而绝对不会接受到属于原来链接的应用程序数据, 这个是TIME_WAIT状态要持续2MSL时间的缘由
有时但愿避免TIME_WAIT状态, 由于当程序退出后, 但愿可以当即重启它. 但因为出在TIME_WAIT状态的链接还占用着端口, 程序没法启动(直到2MSL超时时间结束). 对客户端程序来讲一般不须要担忧重启问题. 由于客户端通常使用系统自动分配的临时端口号来创建链接, 而因为随机性, 临时端口号通常和抽象上一次使用的端口号(还处于TIME_WAIT状态的那个链接使用的端口号)不一样, 因此客户端程序通常能够当即重启
可是若是是服务器主动关闭链接后异常终止, 则由于它老是使用同一个知名服务端口号, 因此链接的TIME_WAIT状态将致使它不能当即重启. 不过, 能够经过socket选项SO_REUSERADDR来强制进程当即使用处于TIME_WAIT状态的链接占用的端口
TCP链接时全双工的, 因此它容许两个方向的数据传输被独立关闭. 换而言之, 通讯的一端能够发送结束报文段给对方, 告诉他本端已经完成了数据的发送, 但容许继续接收来自对方的数据, 直到对方也发送结束报文段以关闭链接. TCP链接的这种状态称为半关闭状态
图中服务器和客户端应用程序判断对方是否已经关闭链接的方法是: read系统调用返回0(收到结束报文段). Linux还提供其余检验链接是否被对方关闭的方法
socket网络编程接口经过shutdown函数提供了对半关闭的支持. 这里强调一下, 虽然介绍了半关闭状态, 可是使用半关闭的应用程序不多见
在某些特殊条件下, TCP链接的一端会向另外一端发送携带RST标志的报文段, 即复位报文段, 以通知对方关闭链接或从新创建链接
由于复位报文的接收通告窗口大小为0, 因此能够碰见: 收到复位报文段的一端应该关闭该链接或者从新链接, 而不能回应这个报文段
TCP的滑动窗口主要有两个做用: 一是提供TCP的可靠性; 二是提供TCP的流控特性. 同时滑动窗口机制还体现了 TCP 面向字节流的设计思路.
对于TCP会话的发送方, 任什么时候候在其发送缓存内的数据均可以分为4类:
对于TCP的接收方, 在某一时刻在它的接收缓存内存在3种状态:
TCP 是双工的协议, 会话的双方均可以同时接收、发送数据. TCP会话的双方都各自维护一个“发送窗口”和一个“接收窗口”. 其中各自的“接收窗口”大小取决于应用、系统、硬件的限制(TCP 传输速率不能大于应用的数据处理速率). 各自的“发送窗口”则要求取决于对端通告的“接收窗口”, 要求相同
滑动窗口实现面向流的可靠性来源于“确认重传”机制. TCP 的滑动窗口的可靠性也是创建在“确认重传”基础上的. 发送窗口只有收到对端对于本段发送窗口内字节的ACK确认, 才会移动发送窗口的左边界. 接收窗口只有在前面全部的段都确认的状况下才会移动左边; 在前面还有字节未接收但收到后面字节的状况下, 窗口不会移动, 并不对后续字节确认. 以此确保对端会对这些数据重传
滑动窗口协议: 用于流量控制, 便可用于应用层也能够用于传输层, 前者以帧为单位进行确认, 后者以字节为单位进行确认. 发送端发送的数据不能超过对方接收窗口的缓冲区大小, 若是超过接收窗口大小就会致使数据的丢失
《计算机网络》第四版中讲“三次握手”的目的是“为了防止已失效的链接请求报文段忽然又传送到了服务端, 于是产生错误”, 书中的例子是这样的, “已失效的链接请求报文段”的产生在这样一种状况下:client发出的第一个链接请求报文段并无丢失, 而是在某个网络结点长时间的滞留了, 以至延误到链接释放之后的某个时间才到达server. 原本这是一个早已失效的报文段. 但server收到此失效的链接请求报文段后, 就误认为是client再次发出的一个新的链接请求. 因而就向client发出确认报文段, 赞成创建链接. 假设不采用“三次握手”, 那么只要server发出确认, 新的链接就创建了. 因为如今client并无发出创建链接的请求, 所以不会理睬server的确认, 也不会向server发送数据. 但server却觉得新的运输链接已经创建, 并一直等待client发来数据. 这样, server的不少资源就白白浪费掉了. 采用“三次握手”的办法能够防止上述现象发生. 例如刚才那种状况, client不会向server的确认发出确认. server因为收不到确认, 就知道client并无要求创建链接. ”. 主要目的防止server端一直等待, 浪费资源.
链接创建3次握手
为何建链接要3次握手, 断链接须要4次挥手?
- 超时重传:发送端发送报文后若长时间未收到确认的报文则须要重发该报文。可能有如下几种状况:
发送的数据没能到达接收端,因此对方没有响应。
接收端接收到数据,可是ACK报文在返回过程当中丢失。
接收端拒绝或丢弃数据。
RTO:从上一次发送数据,由于长期没有收到ACK响应,到下一次重发之间的时间。就是重传间隔。
一般每次重传RTO是前一次重传间隔的两倍,计量单位一般是RTT。例:1RTT,2RTT,4RTT,8RTT......
重传次数到达上限以后中止重传。
RTT:数据从发送到接收到对方响应之间的时间间隔,即数据报在网络中一个往返用时。大小不稳定。
流量控制属于通讯双方协商;拥塞控制涉及通讯链路全局。
流量控制须要通讯双方各维护一个发送窗、一个接收窗,对任意一方,接收窗大小由自身决定,发送窗大小由接收方响应的TCP报文段中窗口值肯定;拥塞控制的拥塞窗口大小变化由试探性发送必定数据量数据探查网络情况后而自适应调整。
实际最终发送窗口 = min{流控发送窗口,拥塞窗口}
目的是接收方经过TCP头窗口字段告知发送方本方可接收的最大数据量,用以解决发送速率过快致使接收方不能接收的问题。因此流量控制是点对点控制。
TCP是双工协议,双方能够同时通讯,因此发送方接收方各自维护一个发送窗和接收窗。
发送窗:用来限制发送方能够发送的数据大小,其中发送窗口的大小由接收端返回的TCP报文段中窗口字段来控制,接收方经过此字段告知发送方本身的缓冲(受系统、硬件等限制)大小。
接收窗:用来标记能够接收的数据大小。
TCP是流数据,发送出去的数据流能够被分为如下四部分:已发送且被确认部分 | 已发送未被确认部分 | 未发送但可发送部分 | 不可发送部分,其中发送窗 = 已发送未确认部分 + 未发但可发送部分。接收到的数据流可分为:已接收 | 未接收但准备接收 | 未接收不许备接收。接收窗 = 未接收但准备接收部分。
发送窗内数据只有当接收到接收端某段发送数据的ACK响应时才移动发送窗,左边缘紧贴刚被确认的数据。接收窗也只有接收到数据且最左侧连续时才移动接收窗口。
拥塞控制
拥塞控制一般表示的是一个全局性的过程,它会涉及到网络中全部的主机、
全部的路由器和下降网络传输性能的全部因素
流量控制
流量控制发生在发送端和接收端之间,只是点到点之间的控制
TCP之 流量控制(滑动窗口)和 拥塞控制(拥塞控制的工做过程)
防止发送方发的太快,耗尽接收方的资源,从而使接收方来不及处理
(1)接收端抑制发送端的依据:接收端缓冲区的大小
(2)流量控制的目标是接收端,是怕接收端来不及处理
(3)流量控制的机制是丢包
使用滑动窗口
滑动窗口
1.滑动窗口是什么?
滑动窗口是相似于一个窗口同样的东西,是用来告诉发送端能够发送数据的大小或者说是窗口标记了接收端缓冲区的大小,这样就能够实现
ps:窗口指的是一次批量的发送多少数据
2.为何会出现滑动窗口?
在确认应答策略中,对每个发送的数据段,都要给一个ACK确认应答,收到ACK后再发送下一个数据段,这样作有一个比较大的缺点,就是性能比较差,尤为是数据往返的时间长的时候使用滑动窗口,就能够一次发送多条数据,从而就提升了性能
3.滑动窗口的一些知识点
(1)接收端将本身能够接收的缓冲区大小放入TCP首部中的“窗口大小”字段,经过ACK来通知发送端
(2)窗口大小字段越大,说明网络的吞吐率越高
(3)窗口大小指的是无需等待确认应答而能够继续发送数据的最大值,即就是说不须要接收端的应答,能够一次连续的发送数据
(4)操做系统内核为了维护滑动窗口,须要开辟发送缓冲区,来记录当前还有那些数据没有应答,只有确认应答过的数据,才能从缓冲区删掉
ps:发送缓冲区若是太大,就会有空间开销
(5)接收端一旦发现本身的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端,发送端收到这个值后,就会减慢本身的发送速度
(6)若是接收端发现本身的缓冲区满了,就会将窗口的大小设置为0,此时发送端将再也不发送数据,可是须要按期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端
4.滑动窗口的优势
能够高效可靠的发送大量的数据
拥塞控制与流量控制的区别
拥塞控制是防止过多的数据注入到网络中, 能够使网络中的路由器或链路不致过载, 是一个全局性的过程. 目的: 避免网络拥塞
流量控制是点对点通讯量的控制, 是一个端到端的问题, 主要就是抑制发送端发送数据的速率, 以便接收端来得及接收. 目的: 防止接收方缓存溢出致使分组丢失
拥塞控制四个部分: 慢启动, 拥塞避免, 快速重传, 快速恢复
慢启动
1.慢开始不是指cwnd的增加速度慢(指数增加), 而是指TCP开始发送设置cwnd=1.
2.思路:不要一开始就发送大量的数据, 先探测一下网络的拥塞程度, 也就是说由小到大逐渐增长拥塞窗口的大小. 这里用报文段的个数的拥塞窗口大小举例说明慢开始算法, 实时拥塞窗口大小是以字节为单位的
3.为了防止cwnd增加过大引发网络拥塞, 设置一个慢开始门限(ssthresh状态变量)
(1)每收到一个ACK的回复报文, 窗口就加1MSS 假设每一个报文被单独确认. 发送方cwnd从1MSS开始, 发送1MSS后, 收到一个ACK. 此时cwnd=1+1=2;
(2)而后发送方发送2MSS报文段, 会收到2个ACK, 此时cwnd=2+2=4.....依次类推拥塞窗口指数增长
当cnwd<ssthresh, 使用慢开始算法
当cnwd=ssthresh, 既可以使用慢开始算法, 也能够使用拥塞避免算法
当cnwd>ssthresh, 使用拥塞避免算法
拥塞避免
1.拥塞避免并不是彻底可以避免拥塞, 是说在拥塞避免阶段将拥塞窗口控制为按线性规律增加, 使网络比较不容易出现拥塞.
2.思路:让拥塞窗口cwnd缓慢地增大, 即每通过一个往返时间RTT就把发送方的拥塞控制窗口加1.
拥塞发生: 当发生丢包进行数据包重传时, 表示网络已经拥塞. 分两种状况进行处理:
等到RTO超时, 重传数据包: sshthresh=cwnd/2; cwnd 重置为1; 进入慢启动过程
在收到3个duplicate ACK时就开启重传, 进入快速恢复算法
发送窗口增加到必定范围时, 可能出现网络空闲, 此时双方不会接收到对等方的确认信息, 拥塞窗口要不断的减少
AIMD算法:
乘法减少(Multiplicative Decrease):不论在慢开始阶段或拥塞避免阶段, 只要出现超时, 就把慢开始门限值减半(当前拥塞窗口的一半)
加法增大(Additive Increase):执行拥塞避免算法后, 使拥塞窗口缓慢增大, 以防止网络过早出现拥塞
拥塞避免不能彻底避免拥塞, 只是在拥塞避免阶段将拥塞窗口控制为线性增加, 使网络不容易出现拥塞
TCP的拥塞控制机制是什么?请简单说说.
答:咱们知道TCP经过一个定时器(timer)采样了RTT并计算RTO, 可是, 若是网络上的延时忽然增长, 那么, TCP对这个事作出的应对只有重传数据, 然而重传会致使网络的负担更重, 因而会致使更大的延迟以及更多的丢包, 这就致使了恶性循环, 最终造成“网络风暴” —— TCP的拥塞控制机制就是用于应对这种状况.
首先须要了解一个概念, 为了在发送端调节所要发送的数据量, 定义了一个“拥塞窗口”(Congestion Window), 在发送数据时, 将拥塞窗口的大小与接收端ack的窗口大小作比较, 取较小者做为发送数据量的上限.
拥塞控制主要是四个算法:
1.慢启动:意思是刚刚加入网络的链接, 一点一点地提速, 不要一上来就把路占满.
链接建好的开始先初始化cwnd = 1, 代表能够传一个MSS大小的数据.
每当收到一个ACK, cwnd++; 呈线性上升
每当过了一个RTT, cwnd = cwnd*2; 呈指数让升
阈值ssthresh(slow start threshold), 是一个上限, 当cwnd >= ssthresh时, 就会进入“拥塞避免算法”
2.拥塞避免:当拥塞窗口 cwnd 达到一个阈值时, 窗口大小再也不呈指数上升, 而是以线性上升, 避免增加过快致使网络拥塞.
每当收到一个ACK, cwnd = cwnd + 1/cwnd
每当过了一个RTT, cwnd = cwnd + 1
拥塞发生:当发生丢包进行数据包重传时, 表示网络已经拥塞. 分两种状况进行处理:
(1) 等到RTO超时, 重传数据包: sshthresh=cwnd/2; cwnd 重置为 1; 进入慢启动过程
(2) 在收到3个duplicate ACK时就开启重传, 而不用等到RTO超时:sshthresh = cwnd; cwnd = cwnd /2;进入快速恢复算法
3.进入慢启动过程
在收到3个duplicate ACK时就开启重传, 而不用等到RTO超时
sshthresh = cwnd = cwnd /2
进入快速恢复算法——Fast Recovery
4.快速恢复:至少收到了3个Duplicated Acks, 说明网络也不那么糟糕, 能够快速恢复.
而后启动快速恢复算法:
cwnd = sshthresh + 3 * MSS (3的意思是确认有3个数据包被收到了)
重传Duplicated ACKs指定的数据包
若是再收到duplicated Acks, 那么cwnd = cwnd +1, 而且在容许的条件下发送一个报文段, 若此后每接收到一个duplicated Ack, cwnd++
若是收到了新的Ack, 那么, cwnd = sshthresh , 而后就进入了拥塞避免的算法了
死锁问题出现
主机B的接收缓存满了, rwnd=0. 主机A知道了就会暂停数据发送, 等待主机B的接收缓存有空闲. 若是此时主机B没有数据发送给A那么A将不可能知道主机B会有缓存空闲, 这会致使A被阻塞(主机B仅当他有数据发送或者有确认时才会发送报文段给A)
解决死锁问题
当发送方A收到接收方B的窗口为0的通知, 便启动一个一个持续计数器, 每隔一段时间向B发送只有一个字节数据的零窗口探测报文段. 这些报文段将被接收方确认. 最终缓存将开始清空, 而且确认报文里包含一个非0的rwnd值.
中止等待协议
中止等待协议是tcp保证传输可靠的重要途径,”中止等待”就是指发送完一个分组就中止发送,等待对方的确认,只有对方确认过,才发送下一个分组.
中止等待协议的优势是简单,可是缺点是信道的利用率过低,一次发送一条消息,使得信道的大部分时间内都是空闲的,为了提升效率,咱们采用流水线传输,这就与下面两个协议有关系了
二:连续ARQ协议和滑动窗口协议
这两个协议主要解决的问题信道效率低和增大了吞吐量,以及控制流量的做用.
连续ARQ协议:它是指发送方维护着一个窗口,这个窗口中不止一个分组,有好几个分组,窗口的大小是由接收方返回的win值决定的,因此窗口的大小是动态变化的,只要在窗口中的分组均可以被发送,这就使得TCP一次不是只发送一个分组了,从而大大提升了信道的利用率.而且它采用累积确认的方式,对于按序到达的最后一个分组发送确认.
滑动窗口协议:之因此叫滑动窗口协议,是由于窗口是不断向前走的,该协议容许发送方在中止并等待确认前发送多个数据分组. 因为发送方没必要每发一个分组就停下来等待确认, 所以该协议能够加速数据的传输,还能够控制流量的问题.
累积确认:若是发送方发送了5个分组,接收方只收到了1,2,4,5,没有收到3分组,那么个人确认信息只会说我指望下一个收到的分组是第三个,此时发送方会将3,4,5,所有重发一次,当通讯质量不是很好的时候,连续ARQ仍是会带来负面影响.
RTT(Round Trip Time): 一个链接的往返时间, 即数据发送时刻到接收到确认的时刻的差值
RTO(Retransmission Time Out): 重传超时时间, 即从数据发送时刻算起, 超过这个时间便执行重传
TCP模块为每一个TCP报文段都维护一个重传定时器, 该定时器在TCP报文段第一次发送时启动. 若是超时时间内未收到接收方的应答, TCP模块将重传TCP报文段并重置定时器. 在达到必定次数尚未成功时放弃并发送一个复位信号
这里比较重要的是重传超时时间, 怎样设置这个定时器的时间(RTO), 从而保证对网络资源最小的浪费. 由于若RTO过小, 可能有些报文只是遇到拥堵或网络很差延迟较大而已, 这样就会形成没必要要的重传. 太大的话, 使发送端须要等待过长的时间才能发现数据丢失, 影响网络传输效率. 因为不一样的网络状况不同, 不可能设置同样的RTO, 实际中RTO是根据网络中的RTT(传输往返时间)来自适应调整的.
RTT和RTO的关系是: 因为网络波动的不肯定性, 每一个RTT都是动态变化的, 因此RTO也应随着RTT动态变化
重传定时器: 当TCP发送报文段时, 就建立这个特定报文段的重传定时器, 若在定时器超时以前收到对报文段的确认, 则撤销定时器; 若在收到对特定报文段的确认以前计时器超时, 则重传该报文, 而且进行RTO=2*RTO进行退避
超时重传有如下三种状况:
假设主机A向主机B发送5个连续的报文段, 主机B对每一个报文段进行确认, 其中第二个报文段丢失, 其他报文段以及重传的第二个报文段均被主机B正确接收, 主机A正确接收全部ACK报文段;报文段从1开始依次连续编号(即一、二、3……), 主机A的超时时间足够长. 请回答下列问题:
1).若是分别采用GBN、SR和TCP协议, 则对应这三个协议, 主机A分别总共发了多少个报文段?主机B分别总共发送了多少个ACK?它们的序号是什么?(针对3个协议分别给出解答)
2).若是对上述三个协议, 超时时间比5RTT长得多, 那么哪一个协议将在最短的时间间隔内成功交付5个报文段?
答
(1)当采用GBN协议时, 由GBN协议可得:
主机A共发送了9个报文段, 首先发送报文段1,2,3,4,5, 当报文2丢失后, 重发报文段2,3,4,5共9个;
主机B共发送8个ACK, 首先发送ACK1, 报文段2丢失, 所以对于3,4,5都发送ACK1共4个ACK1, 后对于重传的2,3,4,5, 则发送ACK2, ACK3, ACK4, ACK5, 一共8个ACK.
当采用SR协议时, 由SR协议可得:
主机A共发送了6个报文段, 首先发送报文段1,2,3,4,5, 当报文2丢失后, 重发报文段2共6个报文段;
主机B共发送5个ACK, 首先发送ACK1, ACK3, ACK4, ACK5, 对于重发的报文段2, 则发送ACK2共5个ACK.
当采用TCP协议时, 由TCP协议可得:
主机A共发送了6个报文段, 首先发送报文段1,2,3,4,5, 当报文2丢失后, 重发报文段2共6个报文段;
主机B共发送5个ACK, 首先发送4个ACK2, 重传后发送一个ACK6一共5个ACK.
(2)采用TCP协议可在最短的时间间隔内成功交付5个报文段, 由于TCP有快速重传机制, 即在未超时状况下就开始重传丢失的2号报文段.
GBN: 失序后不缓存, 直接丢弃, 重传失序后全部数据
SR: 缓存失序后数据, 只重传失序的部分
TCP收到乱序数据后会将其放到乱序序列中, 而后发送重复ACK给对端. 对端若是收到多个重复的ACK, 认为发生丢包, TCP会重传最后确认的包开始的后续包. 这样原先已经正确传输的包, 可能会重复发送, 下降了TCP性能.
为改善这种状况, 发展出SACK技术, 使用SACK选项能够告知发包方收到了哪些数据, 发包方收到这些信息后就会知道哪些数据丢失, 而后当即重传丢失的部分.
SACK --> 累计确认, 延迟确认(正常TCP断开是4次挥手, 可是抓包抓到大部分都是3次挥手. 就是延迟确认的结果吧. )
tcp不可靠表如今:
保证可靠性方法:
链路层MTU限制, 传输数据包超过MTU限制, 意味着在IP层会对数据包进行分片, MSS一般会取一个值保证不会超过MTU大小, 默认值536字节, 路由器MTU常常等于576, 576-20(IP头部)-20(tcp头部)=536, 传输的数据不超过536就不会致使IP层的分片, 因此上层的缓冲区不要超过536(避免IP层数据包的分组分片), 应用层缓冲区取512字节保证不会超过536字节(前提不去更改MSS的默认大小). 局域网MTU为46~1500, 广域网576
UDP 是一个简单的传输层协议. 和TCP相比, UDP有下面几个显著特性:
基于UDP的几个例子:
无论UDP仍是TCP,最终登录成功以后,QQ都会有一个TCP链接来保持在线状态。这个TCP链接的远程端口通常是80,采用UDP方式登录的时候,端口是8000。
DDoS是分布式拒绝服务(distributeddenial-of-service)的简称. DDoS攻击是指攻击者经过控制傀儡计算机, 大量持续地向攻击目标请求资源, 从而阻塞了正经常使用户的合法请求. DDoS攻击的对象主要是网站和域名(DNS)服务器, 大量消耗服务器的资源, 包括内存、CPU以及网络带宽等, 使其不能提供正常服务. 此外, DDoS也能够对网络基础设施进行攻击, 如路由器、交换机等, 经过巨大的攻击流量, 能够致使目标所在的网络性能大幅降低甚至瘫痪, 使目标主机不能对外提供服务.
使用syncookie的基本原理是:在第二次TCP握手的时候, Server端不为链接分配资源, Server端返回了带syncookies的syn,ack包, 在第三次握手的时候client端也要带上这个syncookie, 此时Server端才正式分配系统资源.
第一种是缩短SYN Timeout时间: 因为SYN Flood攻击的效果取决于服务器上保持的SYN半链接数, 这个值=SYN攻击的频度 x SYN Timeout, 因此经过缩短从接收到SYN报文到肯定这个报文无效并丢弃该链接的时间, 例如设置为20秒如下(太低的SYN Timeout设置可能会影响客户的正常访问), 能够成倍的下降服务器的负荷.
第二种方法是设置SYN Cookie: 就是给每个请求链接的IP地址分配一个Cookie, 若是短期内连续受到某个IP的重复SYN报文, 就认定是受到了攻击, 之后从这个IP地址来的包会被丢弃.
限流, 指纹学习
传送门
令使用udp访问dns服务器的客户端使用tcp从新访问
传送门
利用http报文重定向机制
传送门
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen); // level指定控制套接字的层次.能够取三种值: // 1)SOL_SOCKET:通用套接字选项. // 2)IPPROTO_IP:IP选项. // 3)IPPROTO_TCP:TCP选项.
得到套接字错误
若是fd上出现了错误, 那么第一次调用getsockopt会经过status返回错误缘由. 若是此时并无调用close(fd), 按理说这个错误在fd上依然存在, 可是若是再次调用上面的getsockopt, 则会告知用户此fd上没有任何错误. 这种状况常常会发生在函数之间传递fd时, 一个函数A里面作了getsockopt判断, 以后将fd传至别的函数B, 函数B不知道fd的状态, 再次调用getsockopt, 会误认为fd上没有错误了.
因此若是在fd上没有任何读写操做的话, fd上的getsockopt要只调用一次, 以后, 要将该次getsockopt的状态和fd一块儿传递给别的函数. 省得出现上面的问题.
int optval; socklen_t optlen = sizeof optval; if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) { return errno; } else { return optval; }
容许重用本地地址和端口
int optval = on ? 1 : 0; ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
进行长链接通讯时(如移动应用开发中手机端与服务端要维持一个稳定的链接, 进行实时消息传递), 须要在应用层定义心跳机制. 有人问, TCP自己就是可靠传输协议, 为何还须要应用层来实现心跳机制呢?
【然而】在使用TCP创建链接后
1) 不少防火墙等对于空闲socket自动关闭
2) 对于非正常断开, 服务器并不能检测到(如拔网线, 直接关机等).
【解决办法--keepalive属性】
套接字自己是有一套心跳保活机制的, 不过默认的设置并不像咱们一厢情愿的那样有效. 在双方TCP套接字创建链接后(即都进入ESTABLISHED状态)而且在两个小时左右上层没有任何数据传输的状况下, 这套机制才会被激活.
不少人认为两个小时的时间设置得很不合理. 为何不设置成为10分钟, 或者更短的时间?(能够经过SO_KEEPALIVE选项设置. )可是这样作其实并不被推荐. 实际上这套机制只是操做系统底层使用的一个被动机制, 原理上不该该被上层应用层使用. 当系统关闭一个由KEEPALIVE机制检查出来的死链接时, 是不会主动通知上层应用的, 只有在调用相应的IO操做在返回值中检查出来
所以, 忘记SO_KEEPALIVE, 在应用层本身写一套保活机制比较靠谱.
int optval = on ? 1 : 0; setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof optval);
int nNetTimeout=1000;//1秒 //发送时限 setsockopt(socket, SOL_S0CKET,SO_SNDTIMEO, (char *)&nNetTimeout,sizeof(int)); //接收时限 setsockopt(socket, SOL_S0CKET,SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int)); 在TCP链接中, recv等函数默认为阻塞模式(block), 即直到有数据到来以前函数不会返回, 而咱们有时则须要一种超时机制使其在必定时间后返回而无论是否有数据到来, 这里咱们就会用到setsockopt()函数: int setsockopt(int s, int level, int optname, void* optval, socklen_t* optlen); 这里咱们要涉及到一个结构: struct timeval { time_t tv_sec; time_t tv_usec; }; 这里第一个域的单位为秒, 第二个域的单位为微秒. struct timeval tv_out; tv_out.tv_sec = 1; tv_out.tv_usec = 0; 填充这个结构后, 咱们就能够以以下的方式调用这个函数: setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));(具体参数能够man一下, 或查看MSDN) 这样咱们就设定了recv()函数的超时机制, 当超过tv_out设定的时间而没有数据到来时recv()就会返回0值.
SO_RCVBUF和SO_SNDBUF每一个套接口都有一个发送缓冲区和一个接收缓冲区, 使用这两个套接口选项能够改变缺省缓冲区大小. 默认8K
// 接收缓冲区 int nRecvBuf=32*1024; //设置为32K setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int)); //发送缓冲区 int nSendBuf=32*1024;//设置为32K setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
注意: 当设置TCP套接口接收缓冲区的大小时, 函数调用顺序是很重要的, 由于TCP的窗口规模选项是在创建链接时用SYN与对方互换获得的. 对于客户, O_RCVBUF选项必须在connect以前设置;对于服务器, SO_RCVBUF选项必须在listen前设置.
Nagle算法能够必定程度上避免网络拥塞, Nagle算法: 若频发送小数据包, 会作些延迟, 等待后续数据包合并一块儿发生
TCP_NODELAY选项能够禁用Nagle算法. 禁用Nagle算法, 能够避免连续发包出现延迟(通常是200ms), 这对于编写低延迟的网络服务很重要
int optval = on ? 1 : 0; // 1: 禁用, 0: 不由用 ::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof optval);
默认状况下, 往一个读端关闭的管道或socket链接中写数据将引起SIGPIPE信号. 须要在代码中捕获处理该信号, 或者至少忽略它, 由于程序接收到SIGPIPE信号的默认行为是结束进程, 而绝对不但愿由于错误的写操做而致使程序的退出. 引发SIGPIPE信号的写操做将设置errno
向一个已经收到FIN的套接字中写是容许的, 接收到FIN仅仅表明对方再也不发送数据, 它并不能确认对方的进程是否已经消失. 若对方进程消失, 这时须要客户端调用write, 此次调用并不会产生断开的管道, 会发现对等方的进程不存在了, 对等方的TCP协议栈会发送RST链接重置的TCP协议段过来, 这意味着对等方读管道不存在
在收到RST报文段以后, 若是再调用write就会产生SIGPIPE信号, 对于这个信号的处理方式默认退出进程, 一般改变默认处理方式, 使用signal(SIGPIPE, SIG_IGN);
void echo_clie(int sock) { char sendbuf[1024] = { 0 }; char recvbuf[1024] = { 0 }; while (fget(sendbuf, sizeof(sendbuf), stdin) != NULL) { // 若此时服务器端异常终止, 客户端没有及时read此时的FIN报文段 write(sock, sendbuf, 1); // 向已经终止的进程发送数据会返回RST write(sock, sendbuf + 1, strlen(sendbuf) - 1); // 向返回RST的套接字继续发送数据会产生SIGPIPE信号 int ret = readline(sock, recvbuf, sizeof(recvbuf)); // 返回FIN, RST, SIGPIPE //... } }
int socket(int domain, int type, int protocol); int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int listen(int sockfd, int backlog); int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags); int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int close(int fd); int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); // 得到本地/对等端地址及端口 int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // 地址转换 int inet_pton(int af, const char *src, void *dst); const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); char *inet_ntoa(struct in_addr in); int inet_aton(const char *cp, struct in_addr *inp); // 两个宏用于定义地址转换后字符串的长度 #define INET_ADDRSTRLEN 16 /* for IPv4 dotted-decimal */ #define INET6_ADDRSTRLEN 46 /* for IPv6 hex string */ // 字节序转换 uint16_t htobe16(uint16_t host_16bits); uint32_t htobe32(uint32_t host_32bits); uint64_t htobe64(uint64_t host_64bits); uint16_t be16toh(uint16_t big_endian_16bits); uint32_t be32toh(uint32_t big_endian_32bits); uint64_t be64toh(uint64_t big_endian_64bits); // 下面转换函数没有上面强 uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort); // Linux 2.6.27以上的内核支持SOCK_NONBLOCK与SOCK_CLOEXEC int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP); int flags = ::fcntl(sockfd, F_GETFL, 0); flags |= O_NONBLOCK; int ret = ::fcntl(sockfd, F_SETFL, flags); // close-on-exec flags = ::fcntl(sockfd, F_GETFD, 0); flags |= FD_CLOEXEC; ret = ::fcntl(sockfd, F_SETFD, flags);
HTTP1.0: 无状态, 无链接. 无状态性能够借助cookie/session机制来作身份认证和状态记录
问题: 没法复用链接, 队头阻塞
队头阻塞(head of line blocking). 因为HTTP1.0规定下一个请求必须在前一个请求响应到达以前才能发送. 假设前一个请求响应一直不到达, 那么下一个请求就不发送, 一样的后面的请求也给阻塞了.
HTTP1.1
缓存处理, 在HTTP1.0中主要使用header里的If-Modified-Since, Expires来作为缓存判断的标准, HTTP1.1则引入了更多的缓存控制策略例如Entity tag, If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略
带宽优化及网络链接的使用, HTTP1.0中, 存在一些浪费带宽的现象, 例如客户端只是须要某个对象的一部分, 而服务器却将整个对象送过来了, 而且不支持断点续传功能, HTTP1.1则在请求头引入了Range头域, 它容许只请求资源的某个部分, 即返回码是206(Partial Content), 这样就方便了开发者自由的选择以便于充分利用带宽和链接.
错误通知的管理, 在HTTP1.1中新增了24个错误状态响应码, 如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除.
Host头处理, 在HTTP1.0中认为每台服务器都绑定一个惟一的IP地址, 所以, 请求消息中的URL并无传递主机名(hostname). 但随着虚拟主机技术的发展, 在一台物理服务器上能够存在多个虚拟主机(Multi-homed Web Servers), 而且它们共享一个IP地址. HTTP1.1的请求消息和响应消息都应支持Host头域, 且请求消息中若是没有Host头域会报告一个错误(400 Bad Request).
长链接, HTTP 1.1支持长链接(PersistentConnection)在HTTP1.1中默认开启Connection: keep-alive, 必定程度上弥补了HTTP1.0每次请求都要建立链接的缺点.
请求的流水线处理, 基于HTTP1.1的长链接, 使得请求管线化成为可能. 管线化使得请求可以“并行”传输. 举个例子来讲, 假如响应的主体是一个html页面, 页面中包含了不少img, 这个时候keep-alive就起了很大的做用, 可以进行“并行”发送多个请求. (注意这里的“并行”并非真正意义上的并行传输, 具体解释以下.)须要注意的是, 服务器必须按照客户端请求的前后顺序依次回送相应的结果, 以保证客户端可以区分出每次请求的响应内容, 一旦有某请求超时等, 后续请求只能被阻塞. 也就是说, HTTP管道化可让咱们把先进先出队列从客户端(请求队列)迁移到服务端(响应队列).
如: 客户端同时发了两个请求分别来获取html和css, 假如说服务器的css资源先准备就绪, 服务器也会先发送html再发送css. 换句话来讲, 只有等到html响应的资源彻底传输完毕后, css响应的资源才能开始传输. 也就是说, 不容许同时存在两个并行的响应.
缺点:虽然容许复用TCP链接, 可是同一个TCP链接里面, 全部的数据通讯是按次序进行的. 服务器只有处理完一个请求, 才会接着处理下一个请求. 若是前面的处理特别慢, 后面就会有许多请求排队等着. 这将致使“队头堵塞”
避免方式:一是减小请求数, 二是同时多开持久链接
HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法.
HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法.
HTTP/1.1 缺点
SPDY是Speedy的昵音,意为“更快”。它是Google开发的基于TCP协议的应用层协议。目标是优化HTTP协议的性能,经过压缩、多路复用和优先级等技术,缩短网页的加载时间并提升安全性。SPDY协议的核心思想是尽可能减小TCP链接数。SPDY并非一种用于替代HTTP的协议,而是对HTTP协议的加强。
SPDY能够说是综合了HTTPS和HTTP二者有点于一体的传输协议, 主要解决:
HTTP/2是HTTP协议自1999年HTTP1.1发布后的首个更新,主要基于SPDY协议。HTTP2.0的特色是:在不改动HTTP语义、方法、状态码、URI及首部字段的状况下,大幅度提升了web性能。
HTTP2.0能够说是SPDY的升级版(其实本来也是基于SPDY设计的), 可是, HTTP2.0 跟 SPDY 仍有不一样的地方, 主要是如下两点:
[][https://image-static.segmentfault.com/293/521/293521278-5a6e83af997f9_articlex]
HTTP2.0其实能够支持非HTTPS的, 可是如今主流的浏览器像chrome, firefox表示仍是只支持基于 TLS 部署的HTTP2.0协议, 因此要想升级成HTTP2.0仍是先升级HTTPS为好.
就是http1.1的请求的流水线处理是否适用于http2.0
首先, 答案是“没有必要”. 之因此没有必要, 是由于这跟HTTP2.0的头部压缩有很大的关系.
在头部压缩技术中, 客户端和服务器均会维护两份相同的静态字典和动态字典.
也就是说, 本来完整的HTTP报文头部的键值对或字段, 因为字典的存在, 如今能够转换成索引index, 在相应的端再进行查找还原, 也就起到了压缩的做用.
因此, 同一个链接上产生的请求和响应越多, 动态字典累积得越全, 头部压缩的效果也就越好, 因此针对HTTP/2网站, 最佳实践是不要合并资源.
另外, HTTP2.0多路复用使得请求能够并行传输, 而HTTP1.1合并请求的一个缘由也是为了防止过多的HTTP请求带来的阻塞问题. 而如今HTTP2.0已经可以并行传输了, 因此合并请求也就没有必要了.
这里额外给你们介绍一个协议, 是由Google基于UDP实现的同为传输层的协议, 目标是但愿替代TCP协议.
该协议支持多路复用, 虽说HTTP2.0也支持多路复用, 可是下层仍然是TCP, 由于TCP的重传机制, 只要一个包丢失就得判断丢包而且重传, 致使发生队头阻塞的问题, 可是UDP没有这个限制. 除此以外, 它还有以下特色:
1、获取证书
升级到 HTTPS 协议的第一步,就是要得到一张证书。
证书是一个二进制文件,里面包含通过认证的网站公钥和一些元数据,要从经销商购买。
2、安装证书
证书能够放在/etc/ssl目录(Linux 系统),而后根据你使用的Web服务器进行配置。
3、修改连接
下一步,网页加载的 HTTP 资源,要所有改为 HTTPS 连接。由于加密网页内若是有非加密的资源,浏览器是不会加载那些资源的。
4、301重定向
下一步,修改 Web 服务器的配置文件,使用 301 重定向,将 HTTP 协议的访问导向 HTTPS 协议。
HTTP协议一般承载于TCP协议之上, 在HTTP和TCP之间添加一个安全协议层(SSL或TSL), 这个时候, 就成了咱们常说的HTTPS.
HTTPS主要做用
HTTPS和HTTP的区别
HTTPS和HTTP的工做过程区别
HTTPS加密方式
HTTPS的优势
尽管HTTPS并不是绝对安全, 掌握根证书的机构、掌握加密算法的组织一样能够进行中间人形式的攻击, 但HTTPS还是现行架构下最安全的解决方案, 主要有如下几个好处:
(1)使用HTTPS协议可认证用户和服务器, 确保数据发送到正确的客户机和服务器;
(2)HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议, 要比http协议安全, 可防止数据在传输过程当中不被窃取、改变, 确保数据的完整性.
(3)HTTPS是现行架构下最安全的解决方案, 虽然不是绝对安全, 但它大幅增长了中间人攻击的成本.
(4)谷歌曾在2014年8月份调整搜索引擎算法, 并称“比起同等HTTP网站, 采用HTTPS加密的网站在搜索结果中的排名将会更高”.
HTTPS的缺点
(1)HTTPS协议握手阶段比较费时, 会使页面的加载时间延长近50%, 增长10%到20%的耗电;
(2)HTTPS链接缓存不如HTTP高效, 会增长数据开销和功耗, 甚至已有的安全措施也会所以而受到影响;
(3)SSL证书须要钱, 功能越强大的证书费用越高, 我的网站、小网站没有必要通常不会用.
(4)SSL证书一般须要绑定IP, 不能在同一IP上绑定多个域名, IPv4资源不可能支撑这个消耗.
(5)HTTPS协议的加密范围也比较有限, 在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么做用. 最关键的, SSL证书的信用链体系并不安全, 特别是在某些国家能够控制CA根证书的状况下, 中间人攻击同样可行.
对于客户端:
当其生成了Pre-master secret以后,会结合原来的A、B随机数,用DH算法计算出一个master secret,紧接着根据这个master secret推导出hash secret和session secret。
对于服务端:
当其解密得到了Pre-master secret以后,会结合原来的A、B随机数,用DH算法计算出一个master secret,紧接着根据这个master secret推导出hash secret和session secret。
在客户端和服务端的master secret是依据三个随机数推导出来的,它是不会在网络上传输的,只有双方知道,不会有第三者知道。同时,客户端推导出来的session secret和hash secret与服务端也是彻底同样的
那么如今双方若是开始使用对称算法加密来进行通信,使用哪一个做为共享的密钥呢?过程是这样子的:
接收方则先用session-secret解密数据,而后获得HTTP+加密后的MAC, 用公钥解密MAC,再用相同的算法计算出本身的MAC,若是两个MAC相等,证实数据没有被篡改。
MAC(Message Authentication Code)称为报文摘要,可以查知报文是否遭到篡改,从而保护报文的完整性。
信息安全中有三个须要解决的问题:
这三要素统称为CIA Triad。公钥密码解决保密性问题; 数字签名解决完整性问题和有效性问题
数字签名: 信息进行哈希计算取得哈希值, 而后用服务器的私钥进行加密
生成签名, 通常来讲,不直接对消息进行签名,而是对消息的哈希值进行签名,步骤以下。
验证签名
数字证书: 对服务器的公钥进行哈希计算取得哈希值, 使用CA的私钥进行加密. 证书实际上就是对公钥进行数字签名,它是对公钥合法性提供证实的技术。
如何生成证书?
如何验证证书?
对称加密的好处是:加密和解密的速度要比非对称加密快不少,所以经常使用非对称加密创建的安全信道进行共享密钥的分享,完成后,具体的加解密则使用对称加密。即混合加密系统。
发送HTTP请求: 请求行, 请求头, 空行, 请求正文
GET/sample.jspHTTP/1.1
Accept:image/gif.image/jpeg,/
Accept-Language:zh-cn
Connection:Keep-Alive
Host:localhost
User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0)
Accept-Encoding:gzip,deflate
链接结束: 断开涉及四次挥手
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址能够从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另外一个地址B)——这是它们的共同点。他们的不一样在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向以后的网址;302表示旧地址A的资源还在(仍然能够访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。
应用层:
HTTP:超文本传输协议, 基于TCP, 使用80号端口, 是用于从www服务器传输超文本到本地浏览器的传输协议.
SMTP:简单邮件传输协议, 基于TCP, 使用25号端口, 是一组用于由源地址到目的地址传送邮件的规则, 用来控制信件的发送、中转.
FTP:文件传输协议, 基于TCP, 通常上传下载用FTP服务, 数据端口是20号, 控制端口是21号.
TELNET:远程登陆协议, 基于TCP, 使用23号端口, 是Internet远程登录服务的标准协议和主要方式. 为用户提供了在本地计算机上完成远程主机工做的能力. 在终端使用者的电脑上使用telnet程序链接到服务器. 使用明码传送, 保密性差、简单方便.
DNS:域名解析, 基于UDP, 使用53号端口, 提供域名到IP地址之间的转换. 同时使用tcp 53端口
SSH:安全外壳协议, 基于TCP, 使用22号端口, 为创建在应用层和传输层基础上的安全协议. SSH 是目前较可靠, 专为远程登陆会话和其余网络服务提供安全性的协议.
GET和POST是HTTP协议中的两种发送请求的方法。而HTTP是是基于TCP/IP的关于数据如何在万维网中如何通讯的协议。HTTP的底层是TCP/IP。因此GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP连接。GET和POST能作的事情是同样同样的, 因此给GET加上request body,给POST带上url参数,技术上是彻底行的通的。
业界不成文的规定是,(大多数)浏览器一般都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。超过的部分,恕不处理。若是你用GET服务,在request body偷偷藏了数据,不一样服务器的处理方式也是不一样的,有些服务器会帮你卸货,读出数据,有些服务器直接忽略,因此,虽然GET能够带request body,也不能保证必定能被接收到哦。
因此GET和POST本质上就是TCP连接,并没有差异。可是因为HTTP的规定和浏览器/服务器的限制,致使他们在应用过程当中体现出一些不一样。
GET和POST还有一个重大区别,简单的说:GET产生一个TCP数据包;POST产生两个TCP数据包。
由于POST须要两步,时间上消耗的要多一点,看起来GET比POST更有效。所以Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为何?
Cookie
Cookie是存储在用户本地计算机上,用于保存一些用户操做的历史信息,当用户再次访问咱们的服务器的时候,浏览器经过HTTP协议,将他们本地的Cookie内容也发到我们服务器上,从而完成验证. Cookie又分为了会话Cookie与持久Cookie,要区分这两种类型,很是的简单,持久Cookie就是咱们设置了它的过时时间,而没设置过时时间的,都属于会话Cookie由于当咱们设置了Cookie的过时时间,那么这个Cookie就会存储在用户的硬盘中,而不是在内存中,由于不在内存中,无论用户是关闭浏览器,仍是关机重启,只要在有效时间内,这个Cookie都能用
Session
存储在咱们的服务器上,就是在咱们的服务器上保存用户的操做信息用户访问咱们的网站时,咱们的服务器会成一个Session ID,而后把Session ID存储起来,再把这个Session ID发给咱们的用户,用户再次访问咱们的服务器的时候,拿着这个Session ID就能验证了,当这个ID能与咱们服务器上存储的ID对应起来时,咱们就能够认为是本身人
Cookie与Session的不一样
二者结合使用
Cookie
Cookie是客户端保持状态的方法。
Cookie简单的理解就是存储由服务器发至客户端并由客户端保存的一段字符串。为了保持会话,服务器能够在响应客户端请求时将Cookie字符串放在Set-Cookie下,客户机收到Cookie以后保存这段字符串,以后再请求时候带上Cookie就能够被识别。
除了上面提到的这些,Cookie在客户端的保存形式能够有两种,一种是会话Cookie一种是持久Cookie,会话Cookie就是将服务器返回的Cookie字符串保持在内存中,关闭浏览器以后自动销毁,持久Cookie则是存储在客户端磁盘上,其有效时间在服务器响应头中被指定,在有效期内,客户端再次请求服务器时均可以直接从本地取出。须要说明的是,存储在磁盘中的Cookie是能够被多个浏览器代理所共享的。
Session
Session是服务器保持状态的方法。
首先须要明确的是,Session保存在服务器上,能够保存在数据库、文件或内存中,每一个用户有独立的Session用户在客户端上记录用户的操做。咱们能够理解为每一个用户有一个独一无二的Session ID做为Session文件的Hash键,经过这个值能够锁定具体的Session结构的数据,这个Session结构中存储了用户操做行为。
当服务器须要识别客户端时就须要结合Cookie了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用Cookie来实现Session跟踪的,第一次建立Session的时候,服务端会在HTTP协议中告诉客户端,须要在Cookie里面记录一个Session ID,之后每次请求把这个会话ID发送到服务器,我就知道你是谁了。若是客户端的浏览器禁用了Cookie,会使用一种叫作URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如sid=xxxxx这样的参数,服务端据此来识别用户,这样就能够帮用户完成诸如用户名等信息自动填入的操做了。
GET 请求指定的页面信息,并返回实体主体。 HEAD 相似于get请求,只不过返回的响应中没有具体的内容,用于获取报头 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会致使新的资源的创建和/或已有资源的修改。 PUT 从客户端向服务器传送的数据取代指定的文档的内容。 DELETE 请求服务器删除指定的页面。 OPTIONS 容许客户端查看服务器的性能。 COPY 请求服务器将指定的页面拷贝至另外一个网络地址。
分类 分类描述
1** 信息,服务器收到请求,须要请求者继续执行操做
2** 成功,操做被成功接收并处理
3** 重定向,须要进一步的操做以完成请求
4** 客户端错误,请求包含语法错误或没法完成请求
5** 服务器错误,服务器在处理请求的过程当中发生了错误
Expires(响应)/Cache-Control(请求+响应): 前者指定过时绝对时间, 后者指定过时相对时间
Last-Modified(响应)/If-Modified-Since(请求): 修改时间
ETag(响应)/If-None-Match(请求): 把时间换成编码
假设对一个资源:
浏览器第一次访问,获取资源内容和cache-control: max-age:600,Last_Modify: Wed, 10 Aug 2013 15:32:18 GMT
因而浏览器把资源文件放到缓存中,而且决定下次使用的时候直接去缓存中取了。
浏览器url回车
浏览器发现缓存中有这个文件了,好了,就不发送任何请求了,直接去缓存中获取展示。(最快)
下面我按下了F5刷新
F5就是告诉浏览器,别偷懒,好歹去服务器看看这个文件是否有过时了。因而浏览器就胆胆襟襟的发送一个请求带上If-Modify-since:Wed, 10 Aug 2013 15:32:18 GMT
而后服务器发现:诶,这个文件我在这个时间后还没修改过,不须要给你任何信息了,返回304就好了。因而浏览器获取到304后就去缓存中欢欢喜喜获取资源了。
可是呢,下面咱们按下了Ctrl+F5
这个但是要命了,告诉浏览器,你先把你缓存中的这个文件给我删了,而后再去服务器请求个完整的资源文件下来。因而客户端就完成了强行更新的操做...
浏览器缓存主要分为强缓存(也称本地缓存)和协商缓存(也称弱缓存)。
强缓存
强缓存是利用 http 头中的 Expires 和 Cache-Control 两个字段来控制的,用来表示资源的缓存时间。
强缓存中,普通刷新会忽略它,但不会清除它,须要强制刷新。浏览器强制刷新,请求会带上 Cache-Control:no-cache 和 Pragma:no-cache。
一般,强缓存不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的network选项中能够看到该请求返回200的状态码。分为 from disk cache 和 from memory cache。
有缓存命中和缓存未命中状态:
协商缓存就是由服务器来肯定缓存资源是否可用,因此客户端与服务器端要经过某种标识来进行通讯,从而让服务器判断请求资源是否能够缓存访问。
普通刷新会启用弱缓存,忽略强缓存。只有在地址栏或收藏夹输入网址、经过连接引用资源等状况下,浏览器才会启用强缓存,这也是为何有时候咱们更新一张图片、一个js文件,页面内容依然是旧的,可是直接浏览器访问那个图片或文件,看到的内容倒是新的。
这个主要涉及到两组 header 字段: Etag 和 If-None-Match、 Last-Modified和 If-Modified-Since。
向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存。
有缓存命中和缓存未命中状态:
MAC地址是一个硬件地址, 用来定义网络设备位置, 主要由数据链路层负责. 而IP协议提供一种统一的地址格式, 为互联网上的每个网络和每一台主机分配一个逻辑地址, 以此来屏蔽物理地址的差别
网络层等到数据链路层用mac地址做为通讯目标, 数据包到达网络等准备往数据链路层发送的时候, 首先会去本身的arp缓存表(存放着ip-mac对应关系)去查找该目标ip的mac地址, 若是查到了, 就标明目标ip的mac地址封装到链路层数据包的包头. 若是缓存中没有找到, 会发起一个广播: who is ip XXX tell ip xxx, 全部受到广播的机器看这个ip是否是本身的, 若是是本身的, 则以单播形式将本身的mac地址回复给请求的机器
ICMP(Internet Control Message Protocol)因特网控制报文协议。它是IPv4协议族中的一个子协议,用于IP主机、路由器之间传递控制消息。控制消息是在网络通不通、主机是否可达、路由是否可用等网络自己的消息。这些控制消息虽然不传输用户数据,可是对于用户数据的传递起着重要的做用。
ICMP协议与ARP协议不一样, ICMP靠IP协议来完成任务, 因此ICMP报文中要封装IP头部. 它与传输层协议(如TCP和UDP)的目的不一样,通常不用来在端系统之间传送数据,不被用户网络程序直接使用,除了想Ping和Tracert这样的诊断程序。
类型八、代码0:回射请求
类型0、代码0:回射应答
IP数据包经由路由转发的时候源ip,目的ip,源MAC,目的mac是否发生改变,如何改变?
A—–(B1-B2)—–(C1-C2)——-E
如上拓扑图为例,B1和B2是路由器B上的两个接口,C1和C2是路由器C上的两个接口,A和E是PC,由主机A向主机E发送数据包,那么在主机A造成的数据包的目的IP就是E的IP,源IP就是主机A的IP地址,目标MAC地址就是B1的MAC地址,源MAC地址就是A的MAC地址
由A发给路由器B,B通过重封装后,源IP和目标IP是不变的,源MAC地址变成B2的MAC地址,目标MAC地址变成C1的MAC地址,封装完成发送给路由器C,路由器C接收到数据包后和B作的操做是同样的,源IP和目标IP的不变的,源MAC地址变成C2的MAC地址,目标MAC地址变成主机E的MAC地址,而后发送给主机E,这样E就收到了这个数据包,当恢复数据包的时候就是把收到的数据包的源IP地址(主机A的IP地址)和源MAC地址(接口C2的MAC地址)做为他的目标IP和目标MAC地址
ping同一网段(主机A ping主机B)
ping不一样网段(主机A ping 主机C)(假设拓扑结构为A-端口E1-端口E2-C,E1和E2为路由器的两个端口)
ping域名
整个过程到此结束。在整个这个过程当中,源ip地址和目的ip地址是不变的(内网到路由器段不算在内)而mac地址是变的。
套接字寻址系统使得TCP和UDP可以执行传输层另外一个重要任务:多路复用和多路分解。多路复用是指把多个来源的数据导向一个输出,而多路分解是把从一个来源接收的数据发送到多个输出。
多路传输/多路分解让TCP/IP协议栈较低层的协议没必要关心哪一个程序在传输数据。与应用程序相关的操做都由传输层完成了,数据经过一个与应用程序无关的管道在传输层与网际层之间传递。
多路复用和多路分解的关键就在于套接字地址。套接字地址包含了IP地址与端口号,为特定计算机上的特定应用程序提供了一个惟一的标识。例如,FTP服务器:全部客户端计算机使用熟知的TCP端口21链接到FTP服务器,但针对每台我的计算机的目的套接字是不一样的。相似地,运行于这台FTP服务器上所有网络应用程序都使用服务器的IP地址,但只有FTP服务程序使用由IP地址和TCP端口号21组成的套接字地址。
域名解析是把域名指向网站空间IP,让人们经过注册的域名能够方便地访问到网站的一种服务。IP地址是网络上标识站点的数字地址,为了方便记忆,采用域名来代替IP地址标识站点地址。域名解析就是域名到IP地址的转换过程。域名的解析工做由DNS服务器完成。
解析顺序:
域名的层级: www.example.com
真正的域名是www.example.com.root
,简写为www.example.com.
。由于,根域名.root对于全部域名都是同样的,因此平时是省略的。
根域名的下一级,叫作"顶级域名"(top-level domain,缩写为TLD),好比.com、.net;再下一级叫作"次级域名"(second-level domain,缩写为SLD),好比www.example.com
里面的.example,这一级域名是用户能够注册的;再下一级是主机名(host),好比www.example.com
里面的www,又称为"三级域名",这是用户在本身的域里面为服务器分配的名称,是用户能够任意分配的。
递归查询和迭代查询的区别
网络地址转换(Network Address Translation,缩写:NAT)在计算机网络中是一种在IP数据包经过路由器或防火墙时重写来源IP地址或目的IP地址的技术。这种技术被广泛使用在有多台主机但只经过一个公有IP地址访问因特网的私有网络中。它是一个方便且获得了普遍应用的技术。固然,NAT也让主机之间的通讯变得复杂,致使了通讯效率的下降。
动态主机设置协议(Dynamic Host Configuration Protocol,缩写:DHCP)是一个用于局域网的网络协议,位于OSI模型的应用层,使用UDP协议工做,主要有两个用途:
1、IGP:内部网关协议,即在一个自治系统内部使用的路由选择协议,如RIP和OSPF
一、RIP是一种分布式的基于距离向量的路由选择协议,要求网络中的每个路由器都要维护从它本身到其余每个目的
网络的距离向量。距离便是跳数,路由器与直接相连的网络条数为1,之后每通过一个路由器跳数加1。RIP容许一条路
径最多包含15个路由,所以当距离为16时认为不可达,这由于如此限制了网络的规模,说明RIP只能工做到规模较小的
网络中。RIP的三个要点:仅和相邻路由器交换信息;交换的信息是当前路由器知道的所有信息,即路由表;按固定的
时间间隔交换路由信息,如30秒,RIP协议使用运输层的用户数据报UDP进行传送, 所以RIP协议的位置位于应用层,
可是转发IP数据报的过程是在网络层完成的。RIP是好消息传播的快,坏消息传播的慢。
二、OSPF:最短路径优先,三个要点:采用洪泛法向本自治系统的路由器发送信息;发送的信息就是与本路由器相邻的
全部路由器的链路状态,可是只是路由器所知道的部分信息;只有当链路状态发生变化时,路由器才用洪泛发向全部路
由器发送此信息。OSPF直接使用IP数据包传送,所以OSPF位于网络层
2、EGP:外部网关协议,若源站和目的站处于不一样的自治系统中,当数据报传到一个自治系统的边界时,就须要使用
一种协议将路由信息传递到另外一个自治系统中,如EGP
一、三次握手,若是前两次有某一次失败,会从新从第一次开始,重来三次。
二、三次握手,若是最后一次失败,服务器并不会重传ack报文,而是直接发送RTS报文段,进入CLOSED状态。这样作的目的是为了防止SYN洪泛攻击。
三、发起链接时若是发生TCP SYN丢包,那么系统默认的重试间隔是3s,这期间不会返回错误码。
四、如何模拟tcp挥手失败?答案是iptables命令能够过滤数据包,丢弃全部的链接请求,导致客户端没法获得任何ack报文。
答:TCP协议栈自己是可靠,不会丢包,不会乱序,失败会重发。UDP须要应用层作协议来保证可靠性。视频能够用UDP。必须使用udp的场景:广播。
答:不能够。当网络异常时,select函数能够检测到可读事件,这时候用read函数读取数据,会返回0.
答:
recv:
阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:链接关闭,>0接收到数据大小,
特别:非阻塞模式下返回 值 <0时而且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的状况 下认为链接是正常的,继续接收。
只是阻塞模式下recv会阻塞着接收数据,非阻塞模式下若是没有数据会返回,不会阻塞着读,所以须要 循环读取。
write:
阻塞与非阻塞write返回值没有区分,都是 <0:出错,=0:链接关闭,>0发送数据大小,
特别:非阻塞模式下返回值 <0时而且 (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的状况下认为链接是正常的, 继续发送。
只是阻塞模式下write会阻塞着发送数据,非阻塞模式下若是暂时没法发送数据会返回,不会阻塞着 write,所以须要循环发送。
read:
阻塞与非阻塞read返回值没有区分,都是 <0:出错,=0:链接关闭,>0接收到数据大小,
特别:非阻塞模式下返回 值 <0时而且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的状况 下认为链接是正常的,继续接收。
只是阻塞模式下read会阻塞着接收数据,非阻塞模式下若是没有数据会返回,不会阻塞着读,所以须要 循环读取。
send:
阻塞与非阻塞send返回值没有区分,都是 <0:出错,=0:链接关闭,>0发送数据大小,
特别:非阻塞模式下返回值 <0时而且 (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的状况下认为链接是正常的, 继续发送。
只是阻塞模式下send会阻塞着发送数据,非阻塞模式下若是暂时没法发送数据会返回,不会阻塞着 send,所以须要循环发送
socket选项TCP_NODELAY
答:通常来讲,应用层send函数不会马上把数据发出去,而是先给到网卡缓冲区。网卡缓冲区须要等待数据积累到必定量以后才会发送数据,这样会致使必定的延迟。
默认状况下,发送数据采用Nagle算法。这样虽然提升了网络吞吐量,可是实时性却下降了,在一些交互性很强的应用程序来讲是不容许的,使用TCP_NODELAY选项能够禁止Nagle算法,避免连续发包出现延迟,这对低延迟网络服务很重要。 此时,应用程序向内核递交的每一个数据包都会当即发送出去。须要注意的是,虽然禁止了Nagle 算法,但网络的传输仍然受到TCP确认延迟机制的影响。
如何检测对端已经关闭socket
答:根据ERRNO和recv结果进行判断
在UNIX/LINUX下,非阻塞模式SOCKET能够采用recv+MSG_PEEK的方式进行判断,其中MSG_PEEK保证了仅仅进行状态判断,而不影响数据接收
对于主动关闭的SOCKET, recv返回-1,并且errno被置为9(#define EBADF 9 /* Bad file number /)或104 (#define ECONNRESET 104 / Connection reset by peer /)
对于被动关闭的SOCKET,recv返回0,并且errno被置为11(#define EWOULDBLOCK EAGAIN / Operation would block /)
对正常的SOCKET, 若是有接收数据,则返回>0, 不然返回-1,并且errno被置为11(#define EWOULDBLOCK EAGAIN / Operation would block */)
所以对于简单的状态判断(不过多考虑异常状况):
recv返回>0, 正常
答:阻塞模式时须要设置超时时间,不然会卡死。
答:socket 多进程中的shutdown, close使用
当全部的数据操做结束之后,你能够调用close()函数来释放该socket,从而中止在该socket上的任何数据操做:close(sockfd);
你也能够调用shutdown()函数来关闭该socket。该函数容许你只中止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你能够关闭某socket的写操做而容许继续在该socket上接受数据,直至读入全部数据。
int shutdown(int sockfd,int how);
Sockfd是须要关闭的socket的描述符。参数 how容许为shutdown操做选择如下几种方式:
SHUT_RD:关闭链接的读端。也就是该套接字再也不接受数据,任何当前在套接字接受缓冲区的数据将被丢弃。进程将不能对该套接字发出任何读操做。对TCP套接字该调用以后接受到的任何数据将被确认而后无声的丢弃掉。
SHUT_WR:关闭链接的写端,进程不能在对此套接字发出写操做。
SHUT_RDWR:至关于调用shutdown两次:首先是以SHUT_RD,而后以SHUT_WR。
服务器若是要主动关闭链接,能够这么执行:先关本地“写”端,等对方关闭后,再关本地“读”端。
服务器若是要被动关闭链接,能够这么执行:当read函数返回值是0时,先关本地“写”端,等对方关闭后,再关本地“读”端。
设置非阻塞, connect返回值判断是否链接创建, 失败加入select/poll/epoll, 关注可写事件,
A) 当链接创建成功时,套接口描述符变成 可写(链接创建时,写缓冲区空闲,因此可写)
B) 当链接创建出错时,套接口描述符变成 既可读又可写(因为有未决的错误,从而可读又可写)
进一步判断
方法一:
A)若是链接创建是成功的,则经过getsockopt(sockfd,SOL_SOCKET,SO_ERROR,(char *)&error,&len) 获取的error 值将是0
B)若是创建链接时遇到错误,则errno 的值是链接错误所对应的errno值,好比ECONNREFUSED,ETIMEDOUT 等
方法二:
再次调用connect,相应返回失败,若是错误errno是EISCONN,表示socket链接已经创建,不然认为链接失败。