建立链接: 请求发起方主动打开链接,接收方被动打开; 三次握手: 请求端发送一个SYN报文段请求链接,由CLOSE状态转为SYN_SEND状态,此时接收端被动打开,由CLOSE状态切为LISTEN状态并保持,当接收到SYN报文段之后,接收端会发送一个确认报文段,告诉发送端我收到链接请求,此报文段SYN=1,ACK=1,还有窗口大小,协商好的序号;状态切换到SYN_RCVD; 发送端 收到SYN的确认之后,会对这个SYN确认报文进行确认,发送一个ACK=1,此时SYN=0,状态切为ESTABLISED开始收发数据; 接收端 接到最后一个ACK之后才会切换状态为ESTABLISED收发数据; 此时三次握手完成!
取消链接: 可以是任意一方发起,以client(客户端)为例,当client端的数据发送完毕后,client要求取消链接,最后一个报文段置FIN为1,并状态转换为FIN_WAIT_1;当 server(服务器) 收到client端的FIN报文段后,会切换状态为CLOSE_WAIT,告诉上层应用,client关闭了链接,并且向client发送ACK确认,如果此时server的数据还没有发送完毕,依然会继续向client发送数据,client 收到server发送的ACK之后,也就是不再发送数据,状态切换为FIN_WAIT_2;当 server 数据发送完毕,发送一个FIN=1的报文段给client,告诉client数据发送完毕,可以关闭链接,等待client的确认,并且状态由CLOSE_WAIT转为LAST_ACK;client 接收到server的FIN之后,会对这个FIN做出ACK回应,状态转换为TIME_WAIT,这里有个终止等待计时器,等待2MSL的时间(MSL报文最大生存时间),然后CLOSE,如果在这个期间收到重复的FIN,则会触发ACK重传,并将终止等待计时器重置;server 收到最后一个ACK之后,CLOSE;至此,四次挥手完毕!
三次握手四次挥手相关问题:
为什么是三次握手?两次可不可以?
答: 先假设是两次握手,当client发出同步SYN请求,server收到请求之后,发送ACK确认给client,(假设)此时server认为链接建立完毕,开始收发数据,如果一些原因导致client端关闭或者没有收到server的回应,而server一直在等待client的数据,client又在等server(是否会超时重传),造成死锁;
另外一种情况是,因为一些网络原因,存在已经失效的链接请求(即上一次的链接请求),而client端此时没有向server发送请求,而server收到了这个失效的或者说是超时的链接请求,以为要建立链接,发送一个ACK之后就进入数据收发状态(是在两次握手的假设下),浪费了资源(一系列的数据结构,比如等待确认的已经确认的队列);
为什么是三次握手,而挥手却是4次?
答: 三次握手经上面的推理验证是最有效且成本最低的链接次数,而且,链接是client发起的,server端是被动接受的,而挥手就不是一个主动,一个被动的问题了,挥手即断开链接的问题(所谓请佛容易送佛难),是建立链接双方的问题,client数据发送完了,想要断开了,它就得小心翼翼的问问server的意见,server觉得可以就给client说,我同意你断开我,但是我断开你必须由我发起,所以,client和server都有发起断开链接FIN和收到回答的权利,即四次挥手;
FIN发起方最后的TIME_WAIT状态是为了什么?为什么要等2MSL?
答: 首先,我们很明确的一个问题就是TCP建立的可靠地数据传输链接,那么可靠体现在哪些地方呢?数据的完整性,有序性,可靠地重传机制,活动窗口,拥塞窗口,拥塞避免机制等,那么这和TIME_WAIT有什么关系?既然TCP这么可靠,那么链接的建立和关闭必然也是很可靠的,哪怕是最后一次client发出了last_ACK之后,也需要确认这个ACK是否被server端收到,但是这个确认不是通过继续让server发送ACK(如果这样,就无穷尽了),而是通过一个计时器,就是前面说的终止等待计时器,当最后一个ACK被发送之后,终止等待计时器被置为2MSL,什么是MSL即报文段在传输过程中的最大生存时间,一般为30s到2分钟不等,2MSL即可以保证一段报文从client发送到server再从server到client的最长时间,为什么是这段时间,因为在这个时间内,我们的last_ACK就算丢失,client仍然能够收到server重传的FIN,那么此时TIME_WAIT恢复初值,知道计时器结束都没有收到server的重传,client才会CLOSE;
还有一个原因:即前面的为什么要三次握手提到的失效的链接请求报文,即在这个2MSL时间内,网络中关于本次链接发送的数据都会消失,从而避免出现失效的报文段的现象;
TIME_WAIT所带来的影响?
答: 处于TIME_WAIT状态的链接所占用的端口不能被使用(绑定),编写客户/服务器通信程序中,如果server突然断开链接,立马重新./server时会报错:已经被占用的端口地址;就是因为TIME_WAIT引起的,试想,如果百度服务器突然挂掉(上个星期还真挂了不到五分钟),那么后果是多么严重,并且服务器在2MSL还不能启动起来,真是糟透了;解决这个问题的就是套接字选项SOL_REUSEADDR,使用setsockopt系统调用在监听套接字绑定之前设置,就可以重复绑定端口了;
扩展: close和shutdown系统调用的区别:close采用的是引用计数器的方式来管理套接字,即多个进程使用同一个套接字的话,其中一个close,并不会影响其它进程,shutdown则不同,shutdown的参数有三个选项,可以选择关闭读端/写端/all;shutdown其中一个进程的套接字,其它进程一样会受影响;
扩展: 对比UDP和TCP协议
TCP是面向连接的,即必须建立和取消链接,提供可靠的传输服务,即保证数据按序发送,按序到达,提供超时重传等机制,而UDP是面向无连接的,不提供可靠地传输服务,只是尽最大努力,即即使按序发送,也不能保证按序到达,如果数据丢失或者出错,会返回ICMP,但是不提供处理;
TCP的首部最大60个字节,而UDP是固定的8个字节;
TCP面向的是字节流的服务,UDP面向的是报文的服务
TCP只支持一对一服务,而UDP支持多对多;
扩展: 那些应用使用TCP而那些又使用UDP? 为什么?
参考上一个扩展,多播采用udp,因为tcp只支持一对一;
如果一个应用场景中大多是简短的信息,适合用udp实现,因为udp是基于报文段的,它直接对上层应用的数据封装成报文段,然后丢在网络中,如果信息量太大,会在链路层中被分片,影响传输效率。
如果一个应用场景重性能甚于重完整性和安全性,那么适合于udp,比如多媒体应用,缺一两帧不影响用户体验,但是需要流媒体到达的速度快,因此比较适合用udp
如果要求快速响应,那么udp听起来比较合适
如果又要利用udp的快速响应优点,又想可靠传输,那么只能考上层应用自己制定规则了。
常见的使用udp的例子:ICQ,QQ的聊天模块。
登陆采用TCP协议和HTTP协议,你和好友之间发送消息,主要采用UDP协议,内网传文件采用了P2P技术。总来的说:
1.登陆过程,客户端client 采用TCP协议向服务器server发送信息,HTTP协议下载信息。登陆之后,会有一个TCP连接来保持在线状态。
2.和好友发消息,客户端client采用UDP协议,但是需要通过服务器转发。腾讯为了确保传输消息的可靠,采用上层协议来保证可靠传输。如果消息发送失败,客户端会提示消息发送失败,并可重新发送。