FTP是文件传输协议,一般上传和下载用FTP服务,数据端口为20H,控制端口为21H.
(5)面向字节流。(指的是虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据仅仅看成是一串无结构的字节流,TCP并不知道字节流的含义)
(3)面向报文(发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界)
(4)没有拥塞控制。
(5)支持一对一,一对多和多对多的交互通信。
(6)首部开销小,只有8个字节。
TCP和UDP的区别:
1.TCP面向连接(如打电话要先拨号建立连接)。UDP是无连接的,即发送数据之前不需要建立连接。
2.TCP提供可靠的服务;UDP尽最大努力的交付,即不保证可靠交付。TCP通过检验和,重传机制,序号标识,滑动窗口,确认应答实现可靠传输。如丢包时的重传控制,还可以对次序乱掉的分包进行顺序控制。
3.UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性较高的通信或广播通信。但TCP会有延时,实时性差。
4.每一条的TCP连接只能是点对点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
5.TCP对系统资源要求较多,UDP对系统资源要求较少。
6.在传输相同大小的数据时,TCP的首部开销是20字节;UDP的首部开销是8个字节,所以TCP比UDP的的报头更复杂,实际传输的数据更少一些。
7.TCP的逻辑通信是全双工可靠信道,UDP是不可靠信道。
8.TCP是面向字节流的,实际上是TCP把数据看成一连串的无结构的字节流;UDP是面向报文的。
TCP和UDP的应用场景:
我们知道TCP数据传输可靠但传输速度慢,实时性差,延时高,UDP传输尽最大努力交付,数据传输不可靠但速度快,实时性好。
所以如果在数据的完整性上有要求,则选用TCP协议(如文件传输、重要状态的更新);反之,对实时性高,要求数据的传输速度上,选用UDP(如视频传输,实时通信)
UDP应用场景:
1.面向数据报方式
2.网络数据大多为短消息
3.拥有大量Client
4.对数据安全性无特殊要求
5.网络负担非常重,但对响应速度要求高。
如何使UDP实现可靠传输?(基于自动重传请求ARQ的实现)
答:传输层无法保证数据可靠传输,只能通过应用层来实现。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。
不考虑拥塞控制处理,可靠UDP的简单设计:
(1)添加seq/ack机制,确保数据发送到对端。
(2)添加发送和接受缓冲区,主要用于用户的超时重传。
(3)添加超时重传机制。
详细说明:发送端发送数据时,生成一个随机的seq=x,然后每一片按照数据大小分配seq。当发送端发送完一个分组后,必须暂时保存分组的副本。数据到达接收端之后进入接收端缓存,并发送一个ack=x的包,表示对方已经收到了数据。发送方收到确认之后,删除缓冲区对应的副本。如果发送方在设定超时重传的时间内没有收到确认,就重传这个分组。
为什么说UDP比TCP更有优势?
UDP以其简单、传输快的优势,在越来越多的场景下取代了TCP,如实时游戏。
(1)网速的提升给UDP的稳定性提供了可靠的网络保障,丢包率很低,如果使用应用层重传,能够确保传输的可靠性。
(2)TCP为了实现网络通信的可靠性,使用了复杂的拥塞控制算法,建立了繁琐的握手过程,由于TCP内置的系统协议栈中,极难对其进行改进。
(3)采用TCP,一旦发生丢包,TCP会将后续的包缓存起来,等前面的包重传并接受到后再继续发送,延时会越来越大,基于UDP对实时性要求较为严格的情况下,采用自定义重传机制,能够把丢包发生的延迟降到最低,尽量减少网络问题对游戏等应用造成影响。
为什么像即时通讯会选用UDP?而不是采用TCP进行实现?
因为UDP的端口通信机制。TCP连接的端口一般是有限的,而即时通讯,向视频转播需要对很多人进行视频的转发,而TCP的端口是不够的,UDP可以一对一,一对多。
连续ARQ协议。
位于发送窗口内的5个分组都可以连续发送出去,而不需要等待对方的确认,这样信道的利用率就提高了。
接收方一般都是采用累积确认的方式。这就是说:接收方不必对收到的分组逐个发送确认,而是在收到几个分组之后,对按序到达的最后一个分组发送确认,这就表示:到这个分组为止的所有分组都已经正确收到了。
优缺点:
优点:易于实现,即使确认丢失也不必重传。
缺点:不能向发发送方反映出接收方已经正确收到的所有分组信息。
TCP如何保证可靠传输
1.校验机制
校验和,占两个字节,16位。检验和字段检验的范围包括首部和数据两部分。
2.确认应答与序列号。
序列号:在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。
确认应答:TCP传输过程中,每次接收方收到数据后,都会对传输方进行确认应答。也就是发送ACK报文。这个报文当中带有对应的确认序列号,告诉发送方,接收到了哪些数据,下一次数据从哪里发。
3.超时重传:
Tcp的发送方在规定的时间内没有收到确认就要重传已发送的报文段。
4.流量控制(滑动窗口机制)
当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。
5.拥塞控制:
当网络拥塞时,减少数据的发送。
滑动窗口机制的实现:
为什么要使用滑动窗口?
因为为了提高传输效率,发送方可以连续的发送多个分组,不必每发完一个分组就停下来等接收方的确认。实现流水线传输方式,获得高的信道利用率。比如说,在收到0号报文的确认前还发送了1-3号报文,这样提高了信道的利用率。但这些报文有可能会丢失发生重传,所以需要一个缓冲区维护这些报文,就有了窗口。
窗口是什么?
窗口是以字节为单位的。
发送窗口:
发送窗口根据什么来构造?
根据接收窗口的报文段中的窗口和确认号。假定A收到了B发来的确认报文段,窗口大小为20字节,而确认号为31.(这就表明B期望收到的下一个序号是31,而序号30为止的数据都已经收到了)。
发送窗口表示:在没有收到接受窗口的确认的情况下,发送窗口可以连续的把窗口内的数据发送出去。凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。
要描述一个发送窗口的状态需要三个指针:P1,P2,P3 。指针都指向字节的序号。
P3-P1=A的发送窗口。
P3-P2=允许发送但尚未发送的字节数。
P2-P1=已发送但未收到确认的字节数。
当指针P1与P2重合时,窗口中都是允许发送但尚未发送的字节数。
当指针P2与P3重合时,发送窗口内的序号已用完,但还没有再收到确认。由于发送窗口已满,可用窗口减小到零,必须停止发送。
接收窗口:
接收窗口的大小取决于应用(比如说tomcat:8080端口的监听进程)、系统、硬件的限制。
后方表示已发送确认并向主机交付。
黑色表示已收到收到的,白色表示允许接收但还未收到的数据。
假定B收到了序号为31的数据,并把序号为31~33的数据交付给主机,然后B删除这些数据,把接收窗口向前移动3个序号,同时给A发送确认,窗口大小仍然为20,但确认号为34.
A的p1指针向前移3个数据,p2不变,p3也向前移3个数据,表明A的可用窗口增大了。
超时重传:
Tcp的发送方在规定的时间内没有收到确认就要重传已发送的报文段。
问题:发送方发送出一个报文段,设定的重传时间到了,还没有收到确认。于是重传报文段。经过一段时间后,收到了确认报文段。现在的问题是:如何判定此确认报文段是对先发送的报文段的确认,还是对后来重传的报文段的确认?
方法是:报文段每重传一次,就把超时重传时间RTO增大一些。典型的做法是取新的重传时间为旧的重传时间的2倍。当不再发生报文段的重传时,才根据正确的公式计算超时重传时间。
TCP的流量控制:
利用滑动窗口实现流量控制
所谓的流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。是一个点对点通信量的控制,端到端的问题。
简单来说就是,接收方处理不过来数据的时候,把窗口缩小,并把窗口值告诉发送方。
在连接建立时,B告诉A:我的接收窗口rwnd=400,因此发送方的发送窗口不能超过接收方给出的接收窗口的数值。
有这样一种情况,B向A发送零窗口报文段不久之后,B的接收缓存又有了一些空间。于是B向A发送rwnd=400的报文段,但是报文丢失,A一直等B,B也会一直等A,陷入死锁?
解决的办法是:当窗口值为0时,就启动TCP连接的持续计数器,若时间超过设置的时间,就发送一个零窗口探测报文段,而对方在确认这个探测报文段时给出现在的窗口值。如果仍然是0,就重新设置持续计数器,一段时间后重新发送探测。
拥塞控制:(基于窗口的拥塞控制)
所谓的拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不至过载。前提是,网络能够承受的现有的网络负荷。
方法:
判断网络拥塞控制的依据就是出现了超时。
慢开始:当主机开始发送数据时,由于并不清楚网络的负荷情况,所以如果立即把大量的数据字节注入到网络中的话,就有可能引起网络发生拥塞。较好的方法是先探测一下,由小到大逐渐增大拥塞窗口数值。
cwnd=min(rwnd,cwnd)
rwnd为通告窗口是接收方使用的流量控制,cwnd是发送方的拥塞窗口。
过程:当与一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段。每收到一个ACK,拥塞窗口就增加一个报文段。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。发送方开始发送一个报文段,然后等待ACK。当收到该ACK时,就将拥塞窗口从1增加到2,即可发送两个报文段,当收到这两个报文段的ACK之后,就将拥塞窗口调至为4.这是一种指数增长的关系。
为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量,用法如下:
当cwnd<ssthresh时,使用慢开始算法。
当cwnd>ssthresh时,使用拥塞避免算法。
当cwnd==ssthresh时,两者都可使用。
拥塞避免算法:
算法思路:是让拥塞窗口cwnd缓慢的增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是像慢开始算法那样加倍增长。因此在拥塞避免阶段就有“加法增大”的特点,在这个阶段拥塞窗口cwnd按线性规律缓慢增长。
一般慢开始和拥塞避免算法是一起实现的,基本过程如下:
当cwnd,也就是拥塞避免窗口<ssthresh慢开始门限值时,执行慢开始算法,发送方每收到一个确认ACK时,就把拥塞窗口加倍,因此拥塞窗口cwnd随着传输轮次按指数增长。
当cwnd=ssthresh慢开始门限时,就改为执行拥塞避免算法,拥塞窗口按线性规律增长,每次加1,使网络不容易出现拥塞。
当网络发生超时,发送方判断发生了拥塞,于是调整门限值为当前窗口的一半,将cwnd设置为1,重新执行慢开始算法。
当cwnd超过门限时,执行拥塞避免算法,拥塞窗口按线性增大。
快重传和快恢复:
这是数据丢包导致错误启动慢开始的一种修补机制。
有这么一种情况:个别报文段在网络中丢失,但网络实际并未阻塞。但发送方迟迟收不到确认就会产生超时,误认为网络拥塞,错误开启慢开始算法。
所以这种情况下采用快重传算法:可以让发送方尽早知道个别报文段的丢失。
什么意思呢?
意思就是一旦有报文丢失,接收方没有收到某个报文时,不要等发送方给他发送报文时才捎带确认,而是要立即发送确认。只要发送方收到三个重复确认的报文,就知道接收方确实没有收到某个报文,立即重传,这样就不会误认为网络拥塞。
快恢复是什么鬼呢?
如果出现连续三个ACK报文的确认,我们就知道不是网络拥塞而是报文丢失。于是不启动慢开始而是快恢复,立刻调整门限值ssthresh=cwnd/2=8,同时设置拥塞窗口cwnd=ssthresh,并开始执行拥塞避免算法。
聊一聊TCP三次握手。
一开始两端的TCP进程都处于CLOSED状态。
A主动打开连接,B被动打开连接。
B的TCP服务器进程先创建传输控制块TCB,准备接受客户进程的连接请求,然后服务器进程就处于LISTEN状态,等待客户的连接请求。
(1)第一次握手:A的TCP客户进程也是首先创建传输控制块TCB。然后向B发出连接请求报文段,(首部的同步位SYN=1,初始序号seq=x),SYN=1不携带数据但要消耗一个序号,此时TCP进入SYN-SENT(同步已发送状态)。
(2)第二次握手:B收到连接请求报文段后,如同意建立连接,则向A发送确认,在确认的报文段中(SYN=1,ACK(确认标志)=1,确认号ack=x+1,初始序号seq=y),(这个报文段也不能携带数据,但同样消耗一个序号),测试TCP服务器的进程进入SYN-RCVD(同步收到状态)。
(3)第三次握手:TCP客户进程收到B的确认之后,要向B给出确认报文段(ACK=1,确认号ack=y+1,序号seq=x+1).ACK报文段可以携带数据,但如果不携带数据则不消耗序号。TCP连接已经建立,A进入ESTABLISHED(已建立连接)。
当B收到A的确认之后,也进入ESTABLISHED状态。
为什么A还要发送一次确认呢?即二次握手不行吗?
答:这主要是为了防止已失效的连接请求突然又传送到B。
如果是两次握手,假定出现这样一种异常情况:A发出的第一个连接请求报文段并没有丢失,而是在某个网络节点长时间的滞留了,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的报文段。但B收到此失效的连接请求的报文段之后,就误认为是A又一次新的连接请求。于是向A发送确认报文段,同意建立连接。现在是二次握手,连接已经建立。但A呢?没有发送建立连接的请求也就不会理踩B,所以B的许多资源就浪费掉了。
什么是SYN攻击?
在三次握手中,server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接,此时server处于SYN_RCVD状态,当收到客户端的ACK报文之后就进入ESTABLISHED状态。
syn攻击就是在client端短时间伪造大量的不存在的ip地址,并向sever不断发送SYN包,server对其进行回复,并等待确认。这些伪造的SYN连接长时间占用未连接队列,导致正常请求因为队列满而无法进行连接请求,引起网络拥塞。
检测方法:netstat -nap| grep SYN_RECV
谈谈TCP的四次挥手。
(1)A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接,进入FIN-WAIT-1(终止等待1)状态,等待B的确认。
(2)B收到连接释放报文段之后即发出确认报文段,(ACK=1,确认号ack=u+1,序号seq=v),B进入CLOSE-WAIT(关闭等待状态),此时TCP处于半关闭状态,A收到B的连接释放。
(3)A收到B的确认后,进入FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段。
(4)B没有向A发出的数据,B发出连接释放报文段(FIN(结束标志)=1,ACK=1,序号seq=w,确认号ack=u+1),B进入LAST-ACK(最后确认)状态,等待A的确认。
(5)A收到B的连接释放报文段之后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),A进入TIME-WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,A才进入CLOSED状态。
为什么A在TIME-WAIT状态必须等待2MSL的时间呢?这有两个理由。
第一,为了保证A发送的最后一个ACK报文段能够到达B。
(这个ACK报文段可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,B会重传FIN+ACK这个报文段,而A就能在2MSL时间内收到重传报文段。接着A重传一次确认,重新启动2MSL计时器,能保证A和B都能进入CLOSED状态。如果不等待,则A进入CLOSED状态,B无法收到ACK报文段,也无法重传,就不能按照正常步骤进入CLOSED状态)。
第二,防止已失效的连接请求报文段。
(A在发送完最后一个ACK报文段之后,再经过2MASL,就可以使本连接持续时间内所产生的所有报文段都从网络中消失,就可以使下一次连接持续的时间内不会出现这种旧的连接请求报文段)。
所以说,虽然按道理四个报文都发送完毕了,但我们必须假设网络是不可靠的,可能会丢失报文。所以,等待2MSL是为了防止报文丢失,之后重发。
服务器端口大量处于close_wait是什么原因?
分析原因:首先colse_wait出现的时刻在一方首先发起链接释放报文之后,协议栈进行自动处理。如果之后没有继续发送FIN包和ACK包的话,会一直在Close_wait状态。
主要原因是某种情况下对方关闭了socket链接,但是我方忙与读或者写,没有关闭连接。代码需要判断socket,一旦读到0,断开连接,read返回负,检查一下,errno,如果不是EAGIN(read是非阻塞事件,如果事件没有发生就返回EAGIN),就断开连接。
time_wait占用哪些资源?如何避免time_wait状态?
答:在高并发短连接的TCP服务器上,当服务器处理完请求后立刻主动正常关闭连接.这个场景下会出现大量socket处于TIME_WAIT状态.
造成的资源浪费:
短时间内占用大量端口,而端口之后0-65535的范围,并不是很多.别的请求过来后,分配不了socket,没有空闲端口供TCP连接.
如何避免:
可以使用setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))
设置SO_REUSEADDR套接字来修改内核参数,快速回收被关闭的socket,从而使TCP连接根本就不进入TIME_WAIT状态.
除了时间等待计时器外,TCP还设有一个保活计时器。
设想有这样的情况:客户已主动与服务器建立了TCP连接。但后来客户端的主机突然出现故障。显然,服务器以后就不能再收到客户发来的数据。因此,应当有措施使服务器不能白白的等待下去。这既是保活计时器。服务器每收到一次客户的数据,就重新设置保活计时器,时间的设置通常就是两个小时。若两小时没有收到客户的数据,服务器就发送一个探测报文段,以后则每隔分钟发送一次。若一连发送10个探测报文段后仍然无客户的响应,服务器就认为客户端出了故障,接着就关闭了这个连接。
我们在编程时,可以直接使用keepalive的心跳包,对socket进行recv读取,如果读取的长度<=0的话,就认为对方断开连接。
答:TCP是全双工的,它允许两个方向的数据传输被独立关闭.当主动发起关闭的一方关闭连接报文后,服务器对其进行确认,此时进入半关闭状态,只关闭了客户端的输出流连接但是服务器端可能还有数据没有发完.
(糊涂窗口综合征就是接收方仅腾出一个空间就给发送方发送确认,使网络效率低下).
保护消息边界,就是指传输协议把数据当作一条独立的消息在网上传输,接收端只能接收独立的消息,也就是说存在保护消息边界,接收端一次只能接收发送端发出的一个数据包。而面向流是指无保护消息边界的,如果发送端连续发送数据,接收端有可能在一次接收动作中,会接收两个或者更多的数据包。