网络编程之硬核程序员应该掌握的TCP理论知识

日期:2020年4月10日
作者:Commas
励志:当你的才华撑不起你的野心的时候,你就该静下心来好好学习!
注释:您的每一个小小举动,将会是技术人大家庭的福泽,非常感谢……
如果您觉得这篇博客对您有所帮助,那么不妨给我点个赞;
如果您觉得这篇博客有哪里写的不好的地方,那么不妨给我点个拨。
此篇主要讲解的是TCP理论知识,后面附带了一个Python的示例,
如果您想了解更多有关Python的知识,那么请点《Python学习的奇妙之旅》



一、TCP的定义

TCPTransmission 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报文出错的依据有两个,如下:

  1. 发送端设置重传定时器,重传定时器溢出,还没有收到确认应该,视为需要重传; 在这里插入图片描述
  2. 连续接收到4个确认序号(ACK)相同的确认应答,视为报文丢失,需要重传;
    在这里插入图片描述

五、TCP拥塞控制机制

TCP拥塞控制机制的本质就是根据网络的状况决定发送窗口的发送窗口值,减少发送数据量,从而规避网络拥塞。而发送窗口值有一个很重要的公式,如下:

  • 发送窗口值 =min(CWND,接收端公告的窗口字段值),其中,CWND为拥塞窗口。

从上面的公式,我们不难发现,发送端调整流量就是改变拥塞窗口CWND的值,那我们该何时改变,该如何改变呢?这是我们接下来要讨论并解决的问题。

(1)何时改变CWND值

何时改变?很简单,应该是网络发生拥塞的时候,就是需要改变的时候。但是发送端是如何知道网络拥塞的呢?这个就需要摸着石头过河,具体流程如下:
在这里插入图片描述

(2)如何改变CWND值

既然TCP报文丢失程度各有不同,那么我们就需要有不同流量调整策略应对不同程度丢失报文的情况,具体策略有两种,如下:

1) 慢启动

  1. TCP连接刚建立时,发送1个(即:20)TCP报文;
  2. 收到确认应答后,发送2个(即:21)报文;
  3. 再收到确认应答后,发送4个(即:22)报文;
  4. 依次成倍增大,直至达到接收端公告的窗口值或发生报文丢失为止;

假设拥塞点=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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章