第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
同时三次握手还完成了以下的准备工作:
当客户端向服务器端发送一个连接请求时,由于某种原因长时间驻留在网络节点中,无法达到服务器端,由于TCP的超时重传机制,当客户端在特定的时间内没有收到服务器端的确认应答信息,则会重新向服务器端发送连接请求,且该连接请求得到服务器端的响应并正常建立连接,进而传输数据,当数据传输完毕,并释放了此次TCP连接。
若此时第一次发送的连接请求报文段延迟了一段时间后,到达了服务器端,本来这是一个早已失效的报文段,但是服务器端收到该连接请求后误以为客户端又发出了一次新的连接请求,于是服务器端向客户端发出确认应答报文段,并同意建立连接。如果没有采用三次握手建立连接,由于服务器端发送了确认应答信息,则表示新的连接已成功建立,但是客户端此时并没有向服务器端发出任何连接请求,因此客户端忽略服务器端的确认应答报文,更不会向服务器端传输数据。而服务器端却认为新的连接已经建立了,并在一直等待客户端发送数据,这样服务器端一直处于等待接收数据,直到超出计数器的设定值,则认为服务器端出现异常,并且关闭这个连接。在这个等待的过程中,浪费服务器的资源。如果采用三次握手,客户端就不会向服务器发出确认应答消息,服务器端由于没有收到客户端的确认应答信息,从而判定客户端并没有请求建立连接,从而不建立该连接。
因为当处于LISTEN状态的服务器端收到来自客户端的SYN报文(客户端希望新建一个TCP连接)时,它可以把ACK(确认应答)和SYN(同步序号)放在同一个报文里来发送给客户端。但在关闭TCP连接时,当收到对方的FIN报文时,对方仅仅表示对方已经没有数据发送给你了,但是你自己可能还有数据需要发送给对方,则等你发送完剩余的数据给对方之后,再发送FIN报文给对方来表示你数据已经发送完毕,并请求关闭连接,所以通常情况下,这里的ACK报文和FIN报文都是分开发送的。
可靠的终止TCP连接。如果主动断开连接一方(假设其为客户端)发送的最后一个确认报文丢失,服务端会重新发送FIN报文,那么客户端就必须停留在某个状态以处理服务端重传的报文,否则客户端将以复位报文回复服务端,服务器认为这是一个错误,因为它期望的是一个确认报文。
保证迟来的TCP报文段有足够的时间被识别并且丢弃。在Linux系统中,一个TCP端口不能被同时打开多次,当一个tcp连接处于time_wait状态时,我们无法立即使用该连接占用的端口来建立一个新的连接。反过来考虑,如果不存在time_wait,我们可以立即用这个端口建立一个新的连接,那么这个新的连接可能受到属于原来连接的tcp报文段(迟到的报文段),这显然是错误的。tcp报文段最大生存时间是MSL,所以坚持2MSL能够确保两个方向上的尚未被接受的,迟到的报文都消失(被中转路由器丢弃)。
但是有时候需要取消time_wait状态,如果是服务器端主动关闭连接后异常终止,则因为它总是使用同一个知名端口号,所以time_wait导致其无法正常重启。不过我们可以通过SO_REUSEADDR选项来强制进程立即使用处于time_wait状态的连接占用端口。
1. 慢开始
慢开始的含义就是讲窗口先设置为1,每个传输轮次大小增长一倍,直到大道慢开始的门限(ssthresh),这时候慢开始阶段结束。
2.拥塞避免
慢开始结束后,接下来就是拥塞避免,这个阶段拥塞窗口在每个传输轮次数量加1,直到触发了网络拥塞,窗口大小和门限都变为拥塞时最大的值得一半,然后重新开始慢开始阶段。
**3.快重传和快恢复
快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。
快重传配合使用的还有快恢复算法,有以下两个要点:
a. 当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半。但是接下去并不执行慢开始算法。
b. 考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将cwnd设置为ssthresh的大小,然后执行拥塞避免算法
close一个套接字的默认行为是把套接字标记为已关闭,然后立即返回到调用进程,该套接字描述符不能再由调用进程使用,然而TCP将尝试发送已经排队等待发送到对端的任何数据,发送完毕后发生的是正常的TCP连接终止序列。
在多进程并发服务器中,父子进程共享着套接字,套接字描述符引用计数记录着共享着的进程个数,当父进程或某一子进程close掉套接字时,描述符引用技术会相应的减一,当引用计数仍大于0时,这个close调用就不会引发TCP的四路握手断连过程。
close与shutdown的区别:
①:close函数函数会关闭套接字,如果由其他进程共享着这个套接字,那么它仍然是打开的,这个连接仍然可以用来读和写。
②:shutdown会切断进程共享的套接字的所有连接,不管引用计数是否为0,由第二个参数选择断连的方式。
1.校验和
2.序列号
3.确认应答
4.重传
4.流量控制
5.拥塞控制
Tcp是个“流协议”,所谓流,就是没有界限的一连串数据,没有界限。TCP底层不了解业务数据的含义,它会根据TCP缓冲区的实际情况进行包的划分,所以业务上认为,一个完整的包可能被TCP拆分为多个包进行发送,也可能把多个小包封装成一个大的数据包进行发送,这就是所谓的TCP粘包和拆包问题。
原因:
解决方法:
1、发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
2、发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
3、可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。
1)TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2)TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3)UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4)每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
5)TCP对系统资源要求较多,UDP对系统资源要求较少。
6)若通信数据完整性需让位与通信实时性,则应该选用 TCP 协议(如文件传输、重要状态的更新等);反之,则使用 UDP 协议(如视频传输、实时通信等)。
7)UDP:DNS SNMP
8)TCP面向字节流,UTP面向数据包;
nagle算法的核心思想是允许网络中最多只能有一个小分组被发送,而待发送的其它小分组会被重新分组成一个”较大的”小分组,等收到上一个小分组的应答后再发送.
nagle算法可以减少网络中微小分组的数量,比如客户端需要依次向服务器发送大小为1,2,3,1,2字节的5个分组
在没有开启nagle算法的情况下,这些小分组会被依次发送(不需要等待上一个小分组的应答,因为没启动nagle),总共发送的报文段(分组)个数为5
当开启nagle算法时,客户端首先发送大小为1字节的第一个分组,随后其它分组到达发送缓冲区,由于上一个分组的应答还没有收到,所以TCP会先缓存新来的这4个小分组,并将其重新分组,组成一个大小为8(2+3+1+2)字节的”较大的”小分组。当第一个小分组的应答收到后,客户端将这个8字节的分组发送。总共发送的报文段(分组)个数为2
可以看到,当传输数据存在大量交互数据时,nagle算法可以有效减少网络中的报文段个数
Tcp提供TCP_NODELAY关闭nagle算法.