TCP概述
什么是TCP?
TCP(Transmission Control Protocol)是面向连接的通信协议,通过三次握手建立连接,然后才能开始数据的读写,通讯完成时要拆除连接,由于TCP是面向连接的所以只能用于端到端的通讯。
TCP提供的是一种可靠的数据流服务,数据有可能被拆分后发送,那么采用超时重传机制是和应答确认机制是组成TCP可靠传输的关键设计。
而超时重传机制中最最重要的就是重传超时(RTO,Retransmission TimeOut)的时间选择,很明显,在工程上和现实中网络环境是十分复杂多变的,有时候可能突然的抽风,有时候可能突然的又很顺畅。在数据发送的过程中,如果用一个固定的值一直作为超时计时器的时长是非常不经济也非常不准确的方法,这样的话,超时的时长就需要根据网络情况动态调整,就需要采样统计一个数据包从发送端发送出去到接收到这个包的回复这段时长来动态设置重传超时值,这个时长就是为RTT,学名往返时延,round-trip time,然后再根据这个RTT通过各种算法和公式平滑RTT值后,最终确定重传超时值。
而IP层进行数据传输时,是不能保证数据包按照发送的顺序达到目的机器。当IP将把它们向‘上’传送到TCP层后,TCP将包排序并进行错误检查。TCP数据包中包括序号和确认,所以未按照顺序收到的包可以被排序,而损坏的包可以被重传。
TCP还采用一种称为“滑动窗口”的方式进行流量控制,所谓窗口实际表示接收能力,用以限制发送方的发送速度。
同时TCP还允许在一个TCP连接上,通信的双方可以同时传输数据,也就是所谓的全双工。
面向连接的服务(例如Telnet、FTP、rlogin、X Windows和SMTP)需要高度的可靠性,所以它们使用了TCP。DNS在某些情况下使用TCP(发送和接收域名数据库),但使用UDP传送有关单个主机的信息。
TCP的基本特性?
可靠性,面向连接,往返时延,超时重连,数据排序,流量控制,全双工
TCP的三次握手
TCP 提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作。
所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发。
第一次握手由客户端将标志位SYN=1, 随机产生一个值seq=j,并将该数据包发送给服务端 此时客户端进入SYN_SENT状态,等待服务器确认。
第二次握手由服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。
第三次握手客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。
为什么TCP握手需要三次?
TCP是可靠的传输控制协议,而三次握手是保证数据可靠传输又能提高传输效率的最小次数。为什么?RFC793,也就是TCP的协议RFC中就谈到了原因,这是因为:
为了实现可靠数据传输, TCP协议的通信双方,都必须维护一个序列号, 以标识发送出去的数据包中,哪些是已经被对方收到的。
举例说明:发送方在发送数据包(假设大小为 10 byte)时, 同时送上一个序号( 假设为 500),那么接收方收到这个数据包以后, 就可以回复一个确认号(510 = 500 + 10) 告诉发送方 “我已经收到了你的数据包, 你可以发送下一个数据包, 序号从 511 开始” 。
三次握手的过程即是通信双方相互告知序列号起始值,并确认对方已经收到了序列号起始值的必经步骤。
如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。
至于为什么不是四次,很明显,三次握手后,通信的双方都已经知道了对方序列号起始值,也确认了对方知道自己序列号起始值,第四次握手已经毫无必要了。
TCP的三次握手的漏洞-SYN洪泛攻击
但是在TCP三次握手中是有一个缺陷的,就是如果我们利用三次握手的缺陷进行攻击。这个攻击就是SYN洪泛攻击。三次握手中有一个第二次握手,服务端向客户端应答请求,应答请求是需要客户端IP的,攻击者就伪造这个IP,往服务器端狂发送第一次握手的内容,当然第一次握手中的客户端IP地址是伪造的,从而服务端忙于进行第二次握手但是第二次握手当然没有结果,所以导致服务器端被拖累,死机。
当然我们的生活中也有可能有这种例子,一个家境一般的IT男去表白他的女神被拒绝了,理由是他家里没矿,IT男为了报复,采用了洪泛攻击,他请了很多人伪装成有钱人去表白那位追求矿的女神,让女生每次想交往时发现表白的人不见了同时还联系不上了。
面对这种攻击,有以下的解决方案,最好的方案是防火墙。
无效连接监视释放
这种方法不停监视所有的连接,包括三次握手的,还有握手一次的,反正是所有的,当达到一定(与)阈值时拆除这些连接,从而释放系统资源。这种方法对于所有的连接一视同仁,不管是正常的还是攻击的,所以这种方式不推荐。
延缓TCB分配方法
一般的做完第一次握手之后,服务器就需要为该请求分配一个TCB(连接控制资源),通常这个资源需要200多个字节。延迟TCB的分配,当正常连接建立起来后再分配TCB则可以有效地减轻服务器资源的消耗。
使用防火墙
防火墙在确认了连接的有效性后,才向内部的服务器(Listener)发起SYN请求,
TCP四次挥手(分手)
四次挥手即终止TCP连接,就是指断开一个TCP连接是需要客户端和server端发送四个包以确认连接的断开,在socket编程中,这一过程由客户端或者服务端任意一方执行close来触发。
由于TCP连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当甲方完成数据发送任务后,发送一个FIN给乙方来终止这一方向的连接,乙方收到一个FIN只是意味着不会再收到甲方数据了,但是乙方依然可以给甲方发送数据,直到这乙方也发送了FIN给甲方。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
由此可见,TCP建立一个连接需3个分节,终止一个连接则需4个分节。
(1)某个应用进程首先调用close,我们称该端执行主动关闭(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕,应用进程进入FIN-WAIT-1(终止等待1)状态。
(2)接收到这个FIN的对端执行被动关闭(passive close),发出确认报文。这个FIN由TCP确认。因为FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。接收端进入了CLOSE-WAIT(关闭等待)状态,这时候处于半关闭状态,即主动关闭端已经没有数据要发送了,但是被动关闭端若发送数据,主动关闭端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。主动关闭端收到确认报文后进入FIN-WAIT-2(终止等待2)状态。
(3)一段时间后,被动关闭的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。
(4)接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN发出一个确认ACK报文,并进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命/最长分节生命期 max segement lifetime,MSL是任何IP数据报能够在因特网中存活的最长时间,任何TCP实现都必须为MSL选择一个值。RFC 1122[Braden 1989]的建议值是2分钟,不过源自Berkelcy的实现传统上改用30秒这个值。这意味着TIME_WAIT状态的持续时间在1分钟到4分钟之间)的时间后,当主动关闭端撤销相应的TCB(传输控制块)后,才进入CLOSED状态。
(5) 被动关闭端只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,被动关闭端结束TCP连接的时间要比主动关闭端早一些。既然每个方向都需要一个FIN和一个ACK,因此通常需要4个分节。我们使用限定词“通常”是因为:某些情形下步骤1的FIN随数据一起发送;另外,步骤2和步骤3发送的分节都出自执行被动关闭那-一端,有可能被合并成一个分节。
为什么TCP的挥手需要四次?
TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。
所以对全双工模式来说,为了彻底关闭,就需要通信两端的4次交互。
为什么需要TIME-WAIT状态?
TIME_WAIT状态存在的原因有两点
1、可靠的终止TCP连接。
2、保证让迟来的TCP报文有足够的时间被识别并丢弃。
根据前面的四次握手的描述,我们知道,客户端收到服务器的连接释放的FIN报文后,必须发出确认。如最后这个ACK确认报文丢失,那么服务器没有收到这个ACK确认报文,就要重发FIN连接释放报文,客户端要在某个状态等待这个FIN连接释放报文段然后回复确认报文段,这样才能可靠的终止TCP连接。
在Linux系统上,一个TCP端口不能被同时打开多次,当一个TCP连接处于TIME_WAIT状态时,我们无法使用该链接的端口来建立一个新连接。反过来思考,如果不存在TIME_WAIT状态,则应用程序能过立即建立一个和刚关闭的连接相似的连接(这里的相似,是指他们具有相同的IP地址和端口号)。这个新的、和原来相似的连接被称为原来连接的化身。新的化身可能受到属于原来连接携带应用程序数据的TCP报文段(迟到的报文段),这显然是不该发生的。这是TIME_WAIT状态存在的第二个原因。
HTTP
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
HTTP协议
我们使用http来访问Web上某个资源,比如html/文本、word、avi电影、其他资源。官方协议网站:https://tools.ietf.org/html/rfc2608
HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息。
URL,全称是UniformResourceLocator, 中文叫统一资源定位符,是互联网上用来标识某一处资源的地址。
URI和URL的区别:
URI是个纯粹的句法结构,用于指定标识Web资源的字符串的各个不同部分。URL是URI的一个特例,它包含了定位Web资源的足够信息。其他URI,比如
mailto:cay@horstman.com
则不属于定位符,因为根据该标识符无法定位任何资源。
URI 是统一资源标识符,而 URL 是统一资源定位符。因此,笼统地说,每个 URL 都是 URI,但不一定每个 URI 都是 URL。这是因为 URI 还包括一个子类,即统一资源名称 (URN),它命名资源但不指定如何定位资源。上面的 mailto就是一个URN 的示例。
URL是uniform resource locator,统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
一个完整的URL
包括以下几部分:
http://www.enjoyedu.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name
1.协议部分:该URL的协议部分为“http:”,这代表网页使用的是HTTP协议。在Internet中可以使用多种协议,如HTTP,FTP等等本例中使用的是HTTP协议。在"HTTP"后面的“//”为分隔符
2.域名部分:该URL的域名部分为“www.enjoyedu.com”。一个URL中,也可以使用IP地址作为域名使用
3.端口部分:跟在域名后面的是端口,域名和端口之间使用“:”作为分隔符。端口不是一个URL必须的部分,如果省略端口部分,将采用默认端口
4.虚拟目录部分:从域名后的第一个“/”开始到最后一个“/”为止,是虚拟目录部分。虚拟目录也不是一个URL必须的部分。本例中的虚拟目录是“/news/”
5.文件名部分:从域名后的最后一个“/”开始到“?”为止,是文件名部分,如果没有“?”,则是从域名后的最后一个“/”开始到“#”为止,是文件部分,如果没有“?”和“#”,那么从域名后的最后一个“/”开始到结束,都是文件名部分。本例中的文件名是“index.asp”。文件名部分也不是一个URL必须的部分,如果省略该部分,则使用默认的文件名
6.参数部分:从“?”开始到“#”为止之间的部分为参数部分,又称搜索部分、查询部分。本例中的参数部分为“boardID=5&ID=24618&page=1”。参数可以允许有多个参数,参数与参数之间用“&”作为分隔符。
7.锚部分:从“#”开始到最后,都是锚部分。本例中的锚部分是“name”。锚部分也不是一个URL必须的部分
一次完整http请求的过程
1、首先进行DNS域名解析(本地浏览器缓存、操作系统缓存或者DNS服务器),首先会搜索浏览器自身的DNS缓存(缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存)
b)如果浏览器自身的缓存里面没有找到,那么浏览器会搜索系统自身的DNS缓存
c)如果还没有找到,那么尝试从 hosts文件里面去找
d)在前面三个过程都没获取到的情况下,就去域名服务器去查找,
2、三次握手建立 TCP 连接
在HTTP工作开始之前,客户端首先要通过网络与服务器建立连接,HTTP连接是通过 TCP 来完成的。HTTP 是比 TCP 更高层次的应用层协议,根据规则,只有低层协议建立之后,才能进行高层协议的连接,因此,首先要建立 TCP 连接,一般 TCP 连接的端口号是80;
3、客户端发起HTTP请求
4、服务器响应HTTP请求
5、客户端解析html代码,并请求html代码中的资源
浏览器拿到html文件后,就开始解析其中的html代码,遇到js/css/image等静态资源时,就向服务器端去请求下载
6、客户端渲染展示内容
7、关闭 TCP 连接
一般情况下,一旦服务器向客户端返回了请求数据,它就要关闭 TCP 连接,然后如果客户端或者服务器在其头信息加入了这行代码 Connection:keep-alive ,TCP 连接在发送后将仍然保持打开状态,于是,客户端可以继续通过相同的连接发送请求,也就是说前面的3到6,可以反复进行。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
DNS劫持和HTTP劫持
DNS是Domain Name System的简写,即域名系统,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网。简单地说,如果我们想访问DNS.COM的官网,本来需要输入网站主机的IP地址,但是DNS可以将www.dns.com 解析成对应的IP地址,我们就不需要记住复杂的IP地址了。
DNS劫持就是通过劫持了DNS服务器,通过某些手段取得某域名的解析记录控制权,进而修改此域名的解析结果,导致对该域名的访问由原IP地址转入到修改后的指定IP,其结果就是对特定的网址不能访问或访问的是假网址,从而实现窃取资料或者破坏原有正常服务的目的。DNS劫持通过篡改DNS服务器上的数据返回给用户一个错误的查询结果来实现的。
一般来说DNS劫持有这三种情况:
1.错误域名解析到纠错导航页面,导航页面存在广告。判断方法:访问的域名是错误的,而且跳转的导航页面也是官方的,如电信的114,联通移网域名纠错导航页面。
2.错误域名解析到非正常页面,对错误的域名解析到导航页的基础上,有一定几率解析到一些恶意站点,这些恶意站点通过判断你访问的目标HOST、URI、 referrer等来确定是否跳转广告页面,这种情况就有可能导致跳转广告页面(域名输错)或者访问页面被加广告(页面加载时有些元素的域名错误而触发)这种劫持会对用户访问的目标HOST、URI、 referrer等会进行判定来确定是否解析恶意站点地址,不易被发现。
3.直接将特点站点解析到恶意或者广告页面,这种情况比较恶劣,而且出现这种情况未必就是运营商所为,家里路由器被黑,或者系统被入侵,甚至运营商的某些节点被第三方恶意控制都有可能。
DNS劫持常见于使用自动的DNS地址,所以,不管有没有被劫持,尽量不要使用运营商默认的DNS。
HTTP劫持:
在运营商的路由器节点上,设置协议检测,一旦发现是HTTP请求,而且是html类型请求,则拦截处理。后续做法往往分为2种,1种是类似DNS劫持返回302让用户浏览器跳转到另外的地址,还有1种是在服务器返回的HTML数据中插入js或dom节点(广告)。比如访问GitHub,出现了“我是渣渣辉,是兄弟就来砍我”之类的小弹窗。
有两种HTTP劫持防劫持思路
1.采用https协议加密请求。但是https多了ssl握手的过程,会耗费一定的时间和性能。
2.隐藏http请求的特征,例如使用对称加密算法加密整个url。市面上的各大手机应用商店都采用了这种做法。相较于https,url加密的兼容性更好,并且对CDN节点带来的解密额外负担可以忽略,但是url加密需要CDN节点的密切配合。