原文https://juejin.im/post/5cf671e4e51d45590a445b00
TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。
TCP
TCP通信需要经过创建连接、数据传送、终止连接三个步骤。
TCP通信是面向连接并且可靠的。通信双方在正式进行通信之前必须先建立可靠一对一的连接,并且分配一定的系统内核资源,双发的数据通过这个连接进行,而且当通信结束之后双发必须断开连接以释放系统资源。
TCP采用发送应答机制,每次传输的成功都依赖于发送和回应。如果在一定时间没有收到回应,则会进行重发。并且为了不丢包,每个数据包中都有一个序号,同时序号也保证了传送到接收端实体的包的按序接收。
TCP客户端的实现代码:
from socket import *
# 创建socket
tcp_client_socket = socket(AF_INET, 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("gbk"))
# 接收对方发送过来的数据,最大接收1024个字节
recvData = tcp_client_socket.recv(1024)
print('接收到的数据为:', recvData.decode('gbk'))
# 关闭套接字
tcp_client_socket.close()
复制代码
TCP的服务端代码:
from socket import *
# 创建socket
tcp_server_socket = socket(AF_INET, SOCK_STREAM)
# 本地信息
address = ('', 7890)
# 绑定
tcp_server_socket.bind(address)
# 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了
tcp_server_socket.listen(128)
# 如果有新的客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务
# 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()
复制代码
TCP的三次握手
image.png当客户端要跟服务端通信的时候会通过三次握手来进行资源的准备。
下文中的SYN和ACK是为了区分请求(SYN)和应答(ACK)。J、K仅代表随机标识,实际并不会发送J、K。
-
首先客户端发起请求并将带有带有一个随机标记SYN J的数据包给服务端。为了标记响应在前面添加SYN。
-
服务端接受到之后,revc会解堵塞,并分配一个新的套接字来对数据进行收发。由于TCP要求每次请求都要有一次应答,所以服务端将客户端发送过来的随机标记加一 J+1发送给客户端告诉它服务端已经准备好资源了。为了标记响应在前面添加ACK。
-
此时服务端也需要确认客户端是否是可靠的,同时客户端也需要进行资源准备,所以服务端会向客户端发送一个同样带有随机标记的数据包(SYN K)。
-
当客户端收到服务端的包的时候也需要给服务端发送确认的包,所以把服务端发送过来的K加一将(ACK K+1)发送给服务端。
由于服务器应答和服务器确认客户端都是发往客户端的,并且中间不会有延时或者不发的情况,所以服务端确认客户端的包就跟服务端相应客户端连接的包一起发送给客户端了。两步合为一步,所以叫三次握手。
当这三次握手完成之后双方便确认对方是可靠的,就可以进行通信了。
TCP四次挥手
image.png当客户端和服务端双方通信结束的时候,为了保证系统资源的释放,双方需要通过四次挥手来断开连接,释放资源。由于套接字socket是全双工的,也就是收发可以同时进行,所以当关闭一个套接字的时候需要关闭发送和接收两个通道。
数据包的发送同三次握手一样同样会发送一个随机标记,在响应的时候对其进行加一操作。
-
断开连接通常也是由客户端首先发起的,当客户端的socket调用close关闭连接的时候会发送数据包给服务端。
-
服务端收到客户端的数据包的时候会回复给客户端一个数据包,表示确认收到客户端关闭连接的请求。
-
服务端的recv解堵塞,当服务端的套接字调用close的时候会给客户端发送一个数据包通知客户端将要关闭发送。但是此时服务端会保留资源,直到收到客户端的确认包。
-
当客户端接收到数据包的时候再给服务端发送确认包,此时客户端的资源也会被保留一段时间。
正常情况下,客户端和服务端都会收到两次响应,从而关闭socket的收发两个通道。
服务端资源保留是因为如果到服务端TCP数据包发送的超时时间还没有收到客户端的回应,那么会再次给客户端发送数据包。而客户端资源保留也是因为如果服务端没有收到数据包的时候回再次发送过来数据,所以需要进行接收。如果是客户端发起的关闭请求,那么客户端的资源通常会保留2msl也就是接受两次数据包的时间,增加接收到服务端重发数据包的概率,如果过时也会关闭套接字释放资源。
四次挥手中服务端发送给客户端的关闭请求的响应和给客户端发送确认关闭的数据包,没有像三次握手那样合为一起,是因为服务端调用close的时机不确定,而如果是客户端先发起的close,那么客户端就要等待两倍的数据包发送时长。服务端是绑定端口的,如果没有释放资源是不能再次进行服务的,而客户端是随机端口的,不会受此限制。
总结
通过TCP进行通信的时候首先通过三次握手准备资源,然后进行通信。通信过程也是有来有回的,每次请求都会对应一次响应。当通信结束的时候,会通过四次挥手关闭连接,释放系统资源。
网友评论