当我们在编写java代码的时候,书写如下代码:
url = new URL("https://github.com/");
URLConnection connecttion = url.openConnection();
执行上述语句之后到底发生了什么,显然先将连接包装成为一个URL对象,然后通过openConnection()打开连接,而打开连接的时候发生了什么呢,这里就用到了我们常说的TCP/IP协议的三次握手,具体如下:
第(1)握手、首先由代码执行者将标志位SYN置为1,并且发送seq序号,此序号占32位,用来标识从TCP源端向目的端发送的字节流,发送数据时对此进行标记,并进入SYN_SEND状态,等待服务器确认发起方。
第(2)握手、服务器收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器将标志位SYN和ACK都置为1,产生序号ack=seq+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务器进入SYN_RCVD状态。
第(3)握手、客户端收到确认后,检查ack是否为seq+1,标志位ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器,服务器检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器进入ESTABLISHED状态,完成三次握手,随后客户端与服务器之间可以开始传输数据了。
需要注意的是:
(A)不要将确认序号ack与标志位中的ACK搞混了。
(B)确认方Ack=发起方seq+1,两端配对。
又如以下语句:
connecttion.close();
这句话是用来终止一个TCP协议链接,因为这个关闭时全双工的,所以需要客户端和服务端总共发送4个包以确认连接的断开。当传输数据的某一方完成了数据的传送时,它可以单方面的来关闭数据的传送,这时候发生FIN来终止这个方向的连接,对方收到这个FIN就代表这个方向上结束数据的传输,当然另外一个方面有可能还没有结束。具体过程如下:
(1)第一次挥手:客户端发送一个FIN,用来关闭客户端到服务器的数据传送,客户端进入FIN_WAIT_1状态。
(2)第二次挥手:服务器收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),服务器进入CLOSE_WAIT状态。
(3)第三次挥手:服务器发送一个FIN,用来关闭服务器到客户端的数据传送,服务器进入LAST_ACK状态。
(4)第四次挥手:客户端收到FIN后,客户端进入TIME_WAIT状态,接着发送一个ACK给服务器,确认序号为收到序号+1,服务器进入CLOSED状态,完成四次挥手。
FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。(来自百科)