TCP/IP协议 -- 三次握手和四次挥手

TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)

​是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCPIP两个协议,而是指一个由FTPSMTP、TCP、UDP、IP等协议构成的协议簇,TCP/IP传输协议是严格来说是一个四层的体系结构,应用层、传输层、网络层和数据链路层都包含其中。 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。。

​ TCP/IP协议的四个层次。

  1. 应用层:TCP/IP协议的第一层,直接为应用进程提供服务。
    • 对不同种类的应用程序根据它们自己的需求来使用应用层的不同协议,例如邮件传输使用SMTP协议,万维网应用使用HTTP协议,远程登录服务应用使用TELNET协议;
    • 应用层还能加密,解密,格式化数据;
    • 应用层可以建立或解除与其他节点的联系,可以充分节省网络资源。
  2. 传输层:第二层,起中流砥柱的作用;
  3. 网络层:第三次,可以进行网络连接的建立和终止以及IP地址的寻找;
  4. 网络接口层:第四层,兼并了物理层和数据链路层,所以这一层为网络层提供了一条准确无误的线路。

TCP即传输控制协议,是一种面向连接的、可靠的、基于字节流的通信协议。位于传输层,简单来说TCP就是有确认机制的UDP协议,每发出一个数据包都要求确认,如果有一个数据包丢失,就收不到确认,发送方就必须重发这个数据包。为了保证传输的可靠性,TCP协议在UDP基础之上建立了三次对话的确认机制,即在正式收发数据前,必须和对方建立可靠的连接。TCP数据包和UDP一样,都是由首部和数据两部分组成,唯一不同的是,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不必再分割。

传输层的主要工作是定义端口,标识应用程序身份,实现端口到端口的通信,TCP协议可以保证数据传输的可靠性。

三次握手:

建立TCP连接,需要客户端和服务端总共发送三个包来确认连接的建立,这一过程由客户端执行connect触发,流程如下:

第一次握手:客户端(Client)发送连接请求报文段,将标志位SYN(同步标志)置为1,随机产生一个值Sequence Number=J, 并将该数据包发送给服务器(Server),客户端进入SYN_SENT状态,等待服务器确认。

第二次握手:服务器(Server)收到客户端(Client)报文段后,得到标志位SYN=1,服务器将标志位SYN和ACK(确认标志)都置为1,ack=J+1, 同时并随机产生一个值seq=K, 同时并将上述所有信息(SYN+ACK)放到一个报文段,一并发送给客户端,服务器进入SYN_RCVD状态。

第三次握手:客户端(Client)收到服务器SYN+ACK报文段后,检查ack是否=J+1, ACK是否=1, 如果正确则将标志位ACK置为1,ack=K+1,并将该报文段发送给服务器(Server),服务器检查报文段ack是否=K+1,ACK是否=1,如果正确则连接建立成功,客户端和服务器进入ESTABLISHEN状态,完成三次握手,随后客户端和服务器之间就可以开始传输数据了。

简单来说,就是

1、建立连接时,客户端发送SYN包(SYN=i)到服务器,并进入到SYN-SEND状态,等待服务器确认

2、服务器收到SYN包,必须确认客户的SYN(ack=i+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器进入SYN-RECV状态

3、客户端收到服务器的SYN+ACK包,向服务器发送确认报ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手,客户端与服务器开始传送数据

SYN攻击:

在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:

#netstat -nap | grep SYN_RECV

四次挥手:

当客户端和服务器通过三次握手建立了TCP连接以后,当数据传送完毕,肯定是要断开TCP连接的啊。那对于TCP的断开连接,这里就有了神秘的“四次分手”。如图:

第一次分手:主机1(可以是客户端,也可以是服务器端),设置Sequence Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;

第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number(确认号)为Sequence Number(标志号)加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;

第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK(最后一次确认)状态;

第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT(等待时间)状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。

SYN:同步标志。表明同步序列编号栏有效。该标志仅在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号,该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。

ACK:确认标志。表明确认编号栏有效。大多数情况下该标志位是置位的。TCP报头内的确认编号栏内包含的确认编号(w+1)为下一个预期的序列编号,同时提示远端系统已经成功接收所有数据。

FIN:结束标志
为什么要等待2MSL?

MSL:报文段最大生存时间,它是任何报文段被丢弃前在网络内的最长时间。原因有二:

  • 保证TCP协议的全双工连接能够可靠关闭
  • 保证这次连接的重复数据段从网络中消失

为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

TCP是全双工模式,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。。

优点:

  1. 协议标准是完全开放的,可以供用户免费使用,并且独立于特定的计算机硬件操作系统
  2. 独立于网络硬件系统,可以运行在广域网,更适合于互联网
  3. 网络地址统一分配,网络中每一设备和终端都具有一个唯一地址。
  4. 高层协议标准化,可以提供多种多样可靠网络服务。

缺点:

  1. 该模型没有明显地区分服务、接口和协议的概念。因此,对于使用新技术来设计新网络,TCP/IP模型不是一个太好的模板。
  2. TCP/IP模型完全不是通用的,并且不适合描述除TCP/IP模型之外的任何协议栈
  3. 链路层并不是通常意义上的一层。它是一个接口,处于网络层和数据链路层之间。接口和层间的区别是很重要的。
  4. TCP/IP模型不区分物理层和数据链路层。这两层完全不同,物理层必须处理铜缆、光纤和无线通信的传输特征;而数据链路层的工作是确定帧的开始和结束,并且按照所需的可靠程度把帧从一端发送到另一端。

代码:

代码演示:

import socket
'''
	创建一个sockey对象
	AF_INET : IPV4
    AF_INET6 : IPV6
    SOCK_STREAM : 表示TCP协议
'''
# 服务器端
server_socket
=	socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# cmd查看ip地址: ipconfig
# 端口号取值范围:1024-65535之间
# 绑定IP(ip)和PORT(端口号)
server_socket.bind(("本地ip地址","端口号"))

# 设置监听数(客户端连接数)
server_socket.listen(3)
# 等待客户端来连接我
print("服务器已启动,等待客户端连接......")

# 让程序暂停,等待连接(有返回值)
# client:客户端对象,addr:本机ip地址 加 客户端端口号
client,addr = server_socket.accept()

# 三次握手成功后开始传输数据
while True:
    # 接收数据,存在data中
    data = client.recv(1024)  # 需要等待客户端输入
    print("客户端对服务器说",data) # 控制台打印客户端输入内容
    
    # 发送数据给客户端
    client.send("收到".encode()) #编码:字符串转二进制



`

# 客户端
import socket
# 1、创建socket对象(客户端)
# socket.SOCK_STREAM TCP协议
client_socket = sockey.socket(socket.AF_INET,socket.socket.SOCK_STREAM)

# 2、主动连接服务器端,去服务器端查找
client_socket.connect("服务器ip",端口号)

# 3、发送和接收服务器数据
while True:
    # 编成二进制
    client_socket.send("你好,靓仔杰".encode())
    
    # 接收服务器端传来的数据
    data = client_socket.recv(1024)
    print("服务器收到你的话,并回复",data.decode()) 
    

posted @ 2020-11-02 10:58  Morpheus1024  阅读(160)  评论(0编辑  收藏  举报