1.TCP介绍
TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。
TCP通信需要经过创建连接、数据传送、终止连接三个步骤。
TCP通信模型中,在通信开始之前,一定要先建立相关的连接,才能发送数据,类似于生活中,"打电话""。
2.TCP特点
2.1 面向连接
通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。
双方间的数据传输都可以通过这个连接进行。
完成数据交换后,双方断开此连接,以释放系统资源。
这种连接是一对一的。
因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。
2.2 可靠传输
1)TCP采用发送应答机制
TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功
2)超时重传
发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。
TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
3)错误校验
TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
4) 流量控制和阻塞管理
流量控制用来避免主机发送得过快而使接收方来不及完全收下。
3. TCP与UDP的不同点
1.面向连接
2.有序数据传输
3.重发丢失的数据包
4.舍弃重复的数据包
5.无差错的数据传输
6.阻塞/流量控制
4 .udp通信模型
udp通信模型中,在通信开始之前,不需要建立相关的连接,只需要发送数据即可,类似于生活中,"写信"。
5.TCP通信模型
tcp通信模型中,在通信开始之前,一定要先建立相关的连接,才能发送数据,类似于生活中,"打电话"。
6.tcp的客户端与服务端
tcp有严格的客户端和服务端之分。基于 TCP 传输方式的通信就必须事先建立连接, 则有一方是主动连接的,而另一方是被连接的; 主动连接的一方一般都是客户端, 被连接的一方一般都是服务端。
6.1.客户端构建流程:
示例代码:
import socket
# 创建socket
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 目的信息
server_ip = input("请输入服务器ip:")
server_port = int(input("请输入服务器port:"))
# 连接服务器
tcp_client_socket.connect((server_ip, server_port))
# 提示用户输入数据
send_data = input("请输入要发送的数据:")
tcp_client_socket.send(send_data.encode("utf-8"))
# 接收对方发送过来的数据,最大接收1024个字节
recvData = tcp_client_socket.recv(1024)
print('接收到的数据为:', recvData.decode('gbk'))
# 关闭套接字
tcp_client_socket.close()
6.2.服务端构建流程:
1. socket创建一个套接字
2. bind绑定ip和port
3. listen使套接字变为可以被动连接,即开启监听
4. accept等待客户端的连接
5. recv/send接收发送数据
示例代码:
import socket
# 创建socket
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 本地信息
address = ('',7788)
# 绑定IP和端口号
tcp_server_socket.bind(address)
# 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的连接请求
tcp_server_socket.listen(5)# 参数:backlog=5 表示积压的客户端连接请求最多5个, 超过5个,则拒绝连接
# 如果有新的客户端来连接服务器,那么就产生一个新的套接字专门为这个客户端服务
# client_socket用来为这个客户端服务
# tcp_server_socket就可以省下来专门等待其他新客户端的连接
client_socket, clientAddr = tcp_server_socket.accept()
# 接收对方发送过来的数据
recv_data = client_socket.recv(1024)# 最多接收1024个字节
print('接收到的数据为:', recv_data.decode('gbk'))
# 发送一些数据到客户端
client_socket.send("thank you !".encode('gbk'))
# 关闭为这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接
client_socket.close()
7.tcp注意点
1. tcp服务端一般情况下都需要绑定,否则客户端找不到这个服务端
2. tcp客户端一般不绑定,因为是主动连接服务端,所以只要确定好服务端的ip、port等信息就好,本地客户端可以随机
3. tcp服务端中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务端时必须要做的
4. 当客户端需要连接服务端时,就需要使用connect进行连接,udp是不需要连接的而是直接发送,但是tcp必须先连接,只有连接成功才能通信
5. 当一个tcp客户端连接服务端时,服务端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务
6. listen后的套接字是被动套接字,用来接收新的客户端的连接请求的,而accept返回的新套接字是标记这个新客户端的
7. 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够连接服务端,但是之前已经连接成功的客户端正常通信。
8. 关闭accept返回的套接字意味着这个客户端已经服务完毕
9. 当客户端的套接字调用close后,服务端会recv解堵塞,并且返回的长度为0,因此服务端可以通过返回数据的长度来区别客户端是否已经下线
8.tcp的三次握手与四次挥手
1. TCP的6个标志位
SYN:发起一个新连接
FIN:释放一个连接
ACK:确认
RST:重置连接
URG:紧急指针(urgent pointer)有效
PSH:传送(接收方应该尽快将这个报文交给应用层)
2. tcp的三次握手
第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;
第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包;
第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
3. tcp的四次挥手
由于TCP连接是双向的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。
第一次挥手: Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
第二次挥手: Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1,Server进入CLOSE_WAIT状态。
第三次挥手: Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
第四次挥手: Client收到FIN后,Client进入TIME_WAIT(TIME_WAIT等待2MSL时间,范围在1-4分钟)状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。