目的:重新梳理TCP,全局理解协议中的细节,知道是怎样实现的,理解为什么要这样做,了解可能会带来什么问题。
PS:图片有空了慢慢贴。
简要介绍:
TCP协议是基于网络层IP协议的传输层协议,提供一种面向连接的,可靠的字节流服务(byte stream service )。在TCP连接中, 仅支持两方进行彼此通信。
TCP的可靠性由以下方式 来提供:
1) 恰当的数据分段。即将字节流根据MSS来封包发送。
2) 确认机制、重传机制。
3) 首部的检验和。
4) 网络层的IP数据报可能会失序,因此TCP需要将数据进行重新排序。
5) 数据报可能会重复,必须恰当的丢弃重复的数据报。
6) TCP提供流量控制,可根据另一端的缓冲区情况发送恰当的数据(滑动窗口协议)。
7) TCP协议对字节流不作解释。由应用层对数据进行语义上的解释。
随便抓个包:
IP数据头
TCP数据头
头部中比较重要的数据结构
源端口,目的端口,序号,确认序号。 标志位,窗口大小。
URG:紧急指针,一般用不上,忽略。
ACK:经常用,接收端发给源端,确认前一个包已收到。
PSH:个人没怎么碰到过。
RST:可以理解为重置连接,普通情况下当目标端口未开放会发送此RST回来,此外,连接中间的防火墙等网络设备也会发。
SYN:发起连接的标志,SYN Flood是基于的一种DOS攻击手法。
FIN:shutdown 时发送,告诉对方,我这边完成了,要送掉连接了。
1、 TCP连接的建议,三步握手。
1) 源端发送SYN到服务器,表示喜娃怀与服务器的某个端口建立TCP连接,在TCP首部带上初始的序号(client ISN)。此报文中设置SYN=1;
2) 服务器返回SYN包,带上服务器的初始序号(server ISN),并且ACK=client ISN+1设置SYN=1,ACK=1;
3) 源端返回服务器ACK包, ack = server ISN+1;
PS:这边的Seq居然从0开始,之前都没注意过~~
关于ISN的选择,根据文献内容,应当随时间变化,避免网络中被延迟的分组被重新传递后导致的错误解释。
2、 TCP连接的终止,四步握手。
1) 首先关闭的一方(A)发送FIN包。FIN在应用层、开发者面前就是socket.read 将返回EOF。
2) 接受端(B)返回FIN的ACK包。
3) B关闭连接,发送FIN。
4) A发送ACK。
关闭阶段存在另外两衍生的流程。1) 2与3 两步可以合并, 当B端无数据发送时,无需发放两个包,可以在一个包里面同时设置FIN+ACK,也就是上面的截图。2)当仅一端调用shutdown,另一端还存在数据发送时,存在半关闭连接的情况。即第2步结束后,B端继续发送数据,A端对这些数据仍然发送ACK,一直到B端发送FIN。
以下是一个简单的client + server 测试代码,通过简单的Sleep可以看出, 当收到FIN包时,缓冲区的数据仍然存在,仅在后面多了一个EOF而已。
1 #!/usr/bin/env python
2 import socket
3 import time
4
5 host="192.168.5.106"
6 port=10000
7 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
8 s.bind((host,port))
9 s.listen(5)
10 sock,addr=s.accept()
11 print "got connection form ",sock.getpeername()
12 while 1:
13 data=sock.recv(1)
14 time.sleep(0.1)
15 if not data:
16 print("~~~~~")
17 break
18 else:
19 print data
20
1 #!/usr/bin/env python
2 import socket
3 host="192.168.5.106"
4 port=10000
5 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
6 s.connect((host,port))
7 s.send("hello from client")
8 s.close()