日期:2020年4月10日
作者:Commas
励志:当你的才华撑不起你的野心的时候,你就该静下心来好好学习!
注释:您的每一个小小举动,将会是技术人大家庭的福泽,非常感谢……
如果您觉得这篇博客对您有所帮助,那么不妨给我点个赞;
如果您觉得这篇博客有哪里写的不好的地方,那么不妨给我点个拨。
此篇主要讲解的是TCP理论知识,后面附带了一个Python的示例,
如果您想了解更多有关Python的知识,那么请点《Python学习的奇妙之旅》
文章目录
一、TCP的定义
TCP
是Transmission Contol Protocol
的缩写,即传输控制协议,是一种面向连接的
、可靠的
、基于字节流的
传输层通信协议。
二、TCP的报文格式以及功能与服务特性
三、三次握手与四次挥手
(1)建立连接过程分析(三次握手)
(2)释放连接过程分析(四次握手)
(3)发送窗口与接收窗口
四、TCP差错控制机制
差错控制总共有序号、确认序号、ACK和检验和这四个字段共同参与工作,
另外,以上ACK指Acknowledgement Number(确认序号),而非Acknowledge Character(确认字符)
(1)数据分段
TCP
是面向字节流的服务
,也就是应用层提供给传输层是一连串的字节流。传输层在发送端发送数据之前,会对这些字节流进行分段,按规则编号,封装成一些列的报文;而在接收端接收数据之后,会将接收到的报文,按照约定的规则,还原成之前的一连串的字节流,提交给应用层。
如上图所示,一个3000字节长度的字节流,传输层接收到应用层的字节流之后,将其分成3段,每段为1000个字节,那么这1000个字节加上首部,即形成了TCP报文
,图中共有3个报文,分别为①②③。在首部控制信息中有一个序号
,它表示报文序列当前的第一个字节序号,如②中的1001,表示第二段报文的第一个字节,是从字节流1001位置开始的,这样方便了接收端无论哪一个报文接收先,最终都可以准确无误的还原发送端发送的数据字节流。
(2)确认应答
确认应答有以下四种方式:
- 逐个确认:一发一收一确认;
- 累计确认:多发多收一确认;
- 时延重复确认:重复确认,直到接收到“没有接收到的”TCP数据报为止;
- 丢失重复确认:重复确认,直到接收到“没有接收到的”TCP数据报为止;
(3)重传
TCP差错控制机制
的本质就是出错重传
,检测到错误的数据或者丢失的数据就需要重新传输,让接收端可以收到正确且完整的数据,从而完成自己的使命。
发送端确认某个TCP报文出错的依据有两个,如下:
- 发送端设置重传定时器,重传定时器溢出,还没有收到确认应该,视为需要重传;
- 连续接收到4个确认序号(ACK)相同的确认应答,视为报文丢失,需要重传;
五、TCP拥塞控制机制
TCP拥塞控制机制
的本质就是根据网络的状况
决定发送窗口的发送窗口值
,减少发送数据量,从而规避网络拥塞。而发送窗口值有一个很重要的公式,如下:
发送窗口值 =min(CWND,接收端公告的窗口字段值)
,其中,CWND为拥塞窗口。
从上面的公式,我们不难发现,发送端调整流量就是改变拥塞窗口CWND的值,那我们该何时改变,该如何改变呢?这是我们接下来要讨论并解决的问题。
(1)何时改变CWND值
何时改变?很简单,应该是网络发生拥塞的时候,就是需要改变的时候。但是发送端是如何知道网络拥塞的呢?这个就需要摸着石头过河,具体流程如下:
(2)如何改变CWND值
既然TCP报文丢失程度各有不同,那么我们就需要有不同流量调整策略
应对不同程度丢失报文的情况,具体策略有两种,如下:
1) 慢启动
- TCP连接刚建立时,发送1个(即:20)TCP报文;
- 收到确认应答后,发送2个(即:21)报文;
- 再收到确认应答后,发送4个(即:22)报文;
- 依次成倍增大,直至达到接收端公告的窗口值或发生报文丢失为止;
假设拥塞点=16,慢启动如下图所示:
2)拥塞避免
拥塞避免是在超过慢启动阈值时,不再采用2的指数增长方式,而是采用线性增长,逐步接近拥塞点,如下图所示:
3)慢启动和拥塞避免示例
- ①之前,TCP连接刚建立,【慢启动】探测网络状态;
- ①处,到达拥塞点;
- ①②之间,网络状态良好;
- ②处,
重传定时器溢出
,流量降至为1; - ②③之间,采用【慢启动】进行流量增长;
- ③处,慢启动阈值(即当前拥塞窗口的一半);
- ③④,一旦超过慢启动阈值,开始【拥塞避免】,线性增长,逐步接近拥塞点;
- ④处,
连续4次相同确认序号的确认应答
,流量降至为当前流量的一半,14÷2=7; - ④之后,继续采用【拥塞避免】线性增长,逐步接近拥塞点。
六、基于TCP的socket类
socket(中文名:套接字)
,是进程间通信的一种方式。我们只要地址(IP+Port),就可以完成同一个终端
或者不同终端
两个进程之间的通信,其中:
- IP:是Internet Protocol(网际互连协议)的缩写,IP地址全网统一编址,标识互联网中
不同的终端
。换而言之,通过IP,我们可以精准定位对应的终端; - Port:端口号,是终端中统一编址,标识终端中
不同应用进程
。换而言之,通过Port,我们可以精准定位对应的进程;
接下来我们借助python中的socket类,来实现两个进程之间的通信……
首先,我们需要实现TCP服务端,文件名为tcp_server.py,代码如下:
import socket
# 服务端的地址(ip+port)
ip_port = ("127.0.0.1", 9000)
# 创建一个套接字
tcp_sk = socket.socket()
# 将该套接字绑定服务器的地址(IP+port)
tcp_sk.bind(ip_port)
# 监听连接
tcp_sk.listen(1)
# 接受客户的连接
conn, addr = tcp_sk.accept()
# 设置接收1024个字节,并接收客户端的消息和服务端的地址(IP+port)
ret = conn.recv(1024).decode("utf-8")
print(ret)
# 服务器发送消息给客户端(消息必须转换成字节传输)
conn.send(b"Hello,I'm server,I am really glad to serve you!")
# 关闭客户端连接(客户端套接字)
conn.close()
# 关闭服务器套接字
tcp_sk.close()
接下来,我们需要实现TCP客户端,文件名为tcp_client.py,代码如下:
import socket
# 服务端的地址(ip+port)
ip_port = ("127.0.0.1", 9000)
# 创建一个套接字
tcp_sk = socket.socket()
# 尝试连接服务器
tcp_sk.connect(ip_port)
# 客户端发送消息给服务端
tcp_sk.send(b"Hello,I'm client!")
# 设置接收1024个字节,并接收服务端的消息和服务端的地址(IP+port)
ret = tcp_sk.recv(1024)
print(ret.decode("utf-8"))
实现了TCP服务端和客户端之后,我们首先要启动服务端(即运行tcp_server.py文件),服务端在等待客户端连接,所以控制台并无消息输出。
然后再运行客户端(即运行tcp_client.py文件),控制台顺出结果如下(按先后次序):
- 服务端控制台:
Hello,I'm client!
- 客户端控制台:
Hello,I'm server,I am really glad to serve you!
版权声明:本文为博主原创文章,如需转载,请给出:
原文链接:https://blog.csdn.net/qq_35844043/article/details/105385186