Socket是什么呢?
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。不过Socket在不同的语境中有不同的含义,如再说TCP连接的俩个端点时,Socket的含义是套接字,Socket=ip:port。我们本次主要讨论的是,Socket作为一组接口的含义。
应用层使用Socket接口进行进程间的通信,一般我们会将主动接收消息的一端称为服务器端,主动发送消息的一端称为客户端。先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
Socket 客户端与服务器段进行连接与释放链接要经过三次握手和四次挥手,以下基于TCP三次握手建立连接、四次挥手断开连接进行详解。
TCP报文首部内容:
位码即tcp标志位,有6种标示:
SYN(synchronous建立联机)
ACK(acknowledgement 确认)
PSH(push传送)
FIN(finish结束)
RST(reset重置)
URG(urgent紧急)
Sequence number(顺序号码)
Acknowledge number(确认号码)
名词详解:
序列号seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号;序列号seq就是这个报文段中的第一个字节的数据编号。
确认号ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号;序列号表示报文段携带数据的第一个字节的编号;而确认号指的是期望接收到下一个字节的编号;因此当前报文段最后一个字节的编号+1即为确认号。
确认ACK:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效
同步SYN:连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建立连接时才会被置1,握手完成后SYN标志位被置0。
终止FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接
PS:ACK、SYN和FIN这些大写的单词表示标志位,其值要么是1,要么是0;ack、seq小写的单词表示序号。
TCP三次握手
所谓三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3个包即交互三次。
三次握手的目的是确认交互双方的活动状态。在socket编程中,客户端执行connect()时。将触发三次握手。
1、 第一次,有客户端调用connect 触发。客户端发送一个报文,其中SYN=1,代表本次请求建立连接,同时发送了一个序列号seq = x。
2、 第二次,服务器端接收到客户端的请求,返回一个确认包。确认包包含 (SYN=1,AKC=1),表示服务端请求与客户端连接。ack(确认号码)的值为客户端请求的序列号+1。同时也发送一个序列号给客户端。此时客户端收到通知后,已经知道了服务器端可以正常接收和发送消息。但是服务器端并不知道客户端是否可以正常接收消息。所有就需要第三次握手。
3、 第三次,客户端向服务器端返回一个确认包(ACK=1),其中确认号码ack为服务器端发送的序列号码+1和一个序列号Z。服务器接收到后可以确认客户端有接收能力。此时,双方已经确认对方都具有发送和接收的能力,客户端和服务器已经完成了双向连接的建立。
客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回。
PS:注意字母大小写不同表示的含义不同
TCP 四次挥手
TCP的连接的拆除需要发送四个包即四次交互,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在Socket编程中,任何一方执行close()操作即可产生挥手操作。
由于TCP连接是全双工通信的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
客户端主动关闭连接,之后服务器端关闭连接:
1、 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2、 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3、 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4、 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5、 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
5.1 、 客户端为什么要等待2MSL
客户端返回最后的确认包,服务器端接收到确认包后进入关闭状态。但是确认包有可能丢失,如果服务器端长时间没有收到确认,会触发超时重传机制,所以在2MSL的时间段内,客户端必须还具有接收能力。这也是为什么客户端请求的时候不需要指定自身的端口,因为这个端口会被占用一端时间,如果再次发起请求,就回出现端口被占用的情况,所以使用随机端口。
6、 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
抓包样例:
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
本文并非原创内容,仅做了知识整理,便于自己理解与记忆
参考连接
https://blog.csdn.net/qq_38950316/article/details/81087809(清晰)
https://www.cnblogs.com/zmlctt/p/3690998.html
https://www.cnblogs.com/wangcq/p/3520400.html