Linux——(十一)网络基础(二)

一、应用层

前置知识

  1. 再谈 “协议”

协议是一种 “约定”. socket api的接口, 在读写数据时, 都是按 “字符串” 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢?

  • 应用层协议:
    应用层:负责应用程序之间的数据沟通,而应用程序是程序员自己写的,应用程序之间如何沟通,用什么协议程序自己定义——自定制协议(程序员自己定制的数据格式约定。)
  • 举例:网络版计算器
    客户端将两个需要计算的数据,以及-一个运算符发送给服务器,服务器进行数学运算,然后将结果返回;
    三个数据对象: num1, num2, op
    这时候订立协议(订立数据格式)多个数据对象按照指定格式进行组织,对端根据指定格式进行解析得到对象。
  1. 专业名词

序列化:按照指定的格式将多个数据对象进行组织成为可持久化存储或者数据传输的二进制数据串。
反序列化:将二进制数据串,能够根据指定格式进行解析得到多个数据对象。

  • 数据格式如何定义最优:传输性能+解析性能
    传输性能:按照指定格式组织好的二进制数据串要尽可能短小。
    解析性能:将二进制数据串进行解析得到数据对象要尽可能简单快速。
  • 结构体的二进制序列化:
    因为结构体很容易完成多个数据对象在内存中的数据组织与解析
    struct cal t{ int num1; int num2; char op;} tmp;
    结构体成员变量的赋值:其实就是在内存中组织数据的过程,把一个数据放到内存指定位置。
    结构体成员变量的访问:其实就是对一块内存访问指定位置指定大小指定格式的数据。

在这里插入图片描述
结构化的二进制序列化中存在的缺陷:位段的使用(平台不一致性),字节对齐(平台不一致性)。
序列化方式有很多:json序列化。protobuf序列化等等。

  1. 典型知名协议
    HTTP——超文本数据传输,ftp——文件传输,SMTP——邮件传输,dns——域名解析…等等。

1. HTTP协议

HTTP协议:超文本传输协议(最早是用来传输网页数据的),是一种简单的请求-响应协议

  • 请求-响应协议:客户端发送一个请求,服务器针对请求进行处理响应,完毕后结束通信
  • http协议是一个明文传输协议:以字符串组织数据格式
  • http协议是一种应用层的数据格式,在传输层用的是tcp协议(http的服务器客户端其实就是tcp的服务器客户端,只不过上层组织数据格式用的是http协议格式)

1.1 HTTP请求头部格式

1. 请求首行

在这里插入图片描述

  • 请求首行:请求中的第一行数据,其中包含三个要素(【方法】+【URL】+【版本】),以空格间隔,以\r\n作为结尾, 就是请求中的第一 行——method url version\r\n
  • Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • 空行
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个
    Content-Length属性来标识Body的长度;

(1)请求方法

表示想服务器发送一个什么样的请求,(是请求资源还是提交数据)

  • GET:获取一个实体资源,也可以提交部分数据给服务器,但是提交的数据不在正文中,而是在URL中,长度受到限制,并因为数据在URL中,因此不太安全
  • HEAD:目的与GET类似,但是响应不要正文数据(只要头部描述不要实际数据)
  • POST:向服务器提交表单数据,提交的数据在正文中,没有长度限制。
    在这里插入图片描述

(2)URL

俗称的网址(统一资源定位符),用于在网络中唯一定位一个资源。如何定位网络中的资源:需要了解URL中所包含的要素。

  • URL中所包含的要素。
    协议方案名称://用户名:密码@服务器地址或域名:服务器端口/资源路径?查询字符串#片段标识符
    eg:http://username:password@www.baidu.com@ip或域名/资源路径?查询字符串#片段标识符
    (1)http:描述通信所使用的协议,
    (2)用户名:密码:身份认证信息-用户名和密码,存在安全隐患,因此现在很少用了
    (3)域名:服务器别名baidu.com,一个便于记忆的字符串,最终需要进行域名解析得到服务器IP地址
    (4)端口:域名或者IP和端口就定位描述了网络.上的一台主机上的某个进程,http服务默认使用80端口,https默认443端口
    (5)/资源路径:描述定位指定计算机上的指定路径下的某个实体资源, /是一-个相对根目录,对应服务器上的指定目录
    **注意:**域名,端口,资源路径:三个定位了网络上指定服务器上的指定资源,由指定进程处理,
    (6)查询字符串:客户端提交给服务端的少量数据,格式- key=val&key=v…

  • urlencode:ur|编码,如果提交的数据中具有特殊字符,就有可能会与urI中的间隔符造成歧义,因此如果提交的数据(资源路径,查询字符串)中有特殊字符,则需要进行转义——ur|编码遇到特殊字符,则将特殊字符的每个字节转换为16进制的数字字符,例:+ —> 2B。并且转换之后,为了表示这是编码后的数据,因此前缀%用以标识。例:C++—> C%2B%2B

  • urldecode: url解码,对ur|编码后的数据进行解析,得到原始数据。遇到%则将紧随其后的两个字符转换为16进制的数字,%2B —> 2 B,第一个数字乘以16 (也可以左移4位)加上第二个数字得到特殊字符ascii值。

(7)片段标识符:HTML网页中的一个标签ID,可以让网页直接滑动到指定位置。
eg:http://username:password@ip:port/path?key=val&key=val#ch

(3)协议版本

0.9:不算成熟的版本,只有GET进行超文本数据传输,协议格式不完善。
1.0:规范了协议格式,新增支持GET/HEAD/POST方法, 支持了多媒体数据流的传输
1.1:支持更多请求方法以及头部字段,有了长连接管理,以及缓存管理
2.0:基于http协议的臃肿,重新进行一些设计,解决了一些典型问题以及性能问题

1.0版本相较于0.9版本:主要规范了协议格式,支持了更多的功能以及数据传输方式
1.1版本相较于1.0版本:主要在于性能上的改进(更多的缓存控制),以及其他一些特殊功能的添加。

  • 缓存控制:一些资源在没有改变的情况下不需要重新传输。
  • 长连接的改进:
    短连接: http实际上是基于tcp的,短连接是建立连接,发送请求,得到响应,断开连接。非常简单清晰的请求-响应规范,但是性能跟不上。
    长连接:在一次连接中可以进行多次请求,且1.1版本的长连接具有管线化管理思想

在这里插入图片描述
长连接相较于短连接,节省了大量的连接建立的时间。
管线化相较于传统长连接,请求连续发送同时处理,节省处理时间,按序响应。
管线化缺陷:响应必须与请求顺序一致, 存在队头阻塞问题(队头阻塞:如果第一个资源处理时间长,就算后边的资源准备好了也不能响应)

  • 2.0版本中的长连接多路复用,解决了队头阻塞问题,每个响应中包含对应的请求描述,因此哪个资源准备好了就可以直接响应。
    2.0版本相较于1.1版本做的改动更大:
    1.1版本做了很多性能提升的操作,比如长连接,缓存控制,分块传输等等,但是这种改进是一个阶段性的改进,很多字段功能都冗余了(比如觉得以前的缓存控制不太完善,因此新增一个字段进行新的缓存控制)。因此在这种阶段性改进中,http协议变得臃肿庞大复杂。所以2.0版本如果在原来基础上再做改进就更复杂臃肿了,不合适。所以改进了下面这些:
    1.从明文字符串传输改为二进制传输。
    2.支持服务端主动推送依赖数据(以前是一次请求一次响应) ,一次请求,可以响应多个数据。
    3.多路复用在响应头部中添加请求信息,不用按序响应,解决队头阻塞问题。
    4.保存以前传输中没有出现的头部字段,相同的头部字段不用每次都重新传输了。节省了部分传输资源。

GET http://www.baidu.com:9090/ path?key=val&key=val#ch HTTP/1.1\r\n

2. 头部字段

以key: val形式的键值对组成,每个键值对以\r\n结尾,关于请求或者正文等等的关键描述信息

  • Host:服务器域名或地址信息
  • Connection:长短连接的管理,keep-alive表示长连接,close表示短连接
  • User- Agent:客户端浏览器以及系统版本信息
  • Accept:客户端告诉服务器自己能够接收什么样的响应数据
  • Referer:里边包含一个连接,表示的是当前请求的来源页面连接
  • Content-Length:正文描述,描述正文有多长
    请求或者响应必须完整才能处理,不完整就会出问题。多个请求或响应连续发送,就会存在粘包问题(多个数据连接到一起无法区别每个数据的起始和结尾)这个字段就是用于解决http粘包问题的。根据格式先取出头部,根据头部中的content-length字段确定正文长度然后取出指定长度正文,解决粘包。
  • Content-Type:正文类型描述,决定了对端如何处理正文数据

3. 空行

\r\n, 用于间隔头部与正文。头部最后一个字段会以r\n结尾,加上空行\r\n会组成一个连续的 \r\n\r\n,这就是头部结束的标识。或者当头部字段逐个取出,而其中某个字段只有单纯\r\n没有其他信息,则表示这就是空间就是头部结束标识

4. 正文

提交给服务器的数据(注意: GET请求没有正文,POST提交的数据放在正文中)
在这里插入图片描述

1.2 HTTP响应头部格式

在这里插入图片描述

  • 首行: [版本号] + [状态码] + [状态码解释]
  • Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个
    Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在 body中.

1.响应首行

首行:明确针对请求的处理结果, 包含三个要素,以空格间隔,以\r\n结尾

  • 协议版本: 0.9、1.0、 1.1、 2.0…
  • 响应状态码:处理结果的简单明确表示,以数字表示不同的处理结果
    在这里插入图片描述
    1xx:一些描述和协商信息。101 协议切换的响应状态码
    2xx:响应成功处理。200—OK(成功处理),206—断点续传
    3xx:表示资源已经重定向,一个资源从原先的位置移动到另一个位置,但是保证原先的链接依然可用。通常与Location响应字段配合使用: http://zhang.com/a.png —> http://zhang.com/image/a.png。301 —>永久重定向,下次请求直接请求新链接。302—>临时重定向,每次请求先请求原链接
    4xx:表示客户端请求错误; 400—>服务器不理解请求的语法是由于明显的客户端错误(例如,格式错误的请求语法,太大的大小,无效的请求消息或欺骗性路由请求),服务器不能或不会处理该请求。404—>请求的资源不存在,没有这个资源。
    5xx:表示服务器错误: 500—>内部错误; 502—>代理请求错误;504—>代理请求超时。
  • 状态码描述:对于状态码的文字描述

在这里插入图片描述

2. 头部

头部:与请求相同也是由key: val键值对组成,每个键值对以以n结尾——用于描述正文或者响应的关键信息。

  • 关键字段
    Content-Type:描述正文类型;
    Content-Length: 描述正文长度
    Location:与3xx搭配重定向,描述重定向新链接;
    Set-Cookie:与请求中的Cookie搭配使用, 用于实现http的cookie机制
    (1)Cookie:cookie就是用于在http协议中持续维护客户端通信状态的机制
    http协议是一个无状态协议,早期版本中短连接,一次通信连接就会断开,但是随着协议的发展客户端的标识状态越来越重要(比如购物:登录会告诉对方客户端的身份信息,但是当你本次通信完毕链接就断开了,下次买东西的时候依然需要重新发送客户端认证信息,因为新的连接,服务端已经不识别了。)
    因此就有cookie机制,服务端将客户端的一些状态信息通过Set-Cookie字段发送给客户端,客户端将这些信息保存到cookie文件中,在下次请求服务器的时候将cookie信息从cookie文件中读取出来,通过Cookie字段发送给服务器,服务器收到之后,就能了解客户端的身份状态信息了。cookie机制其实就是不断的在客户端与服务器之间传输客户端的身份以及状态信息,但是这样存在安全隐患——因此有了session机制,也就是会话机制。
    (2)session机制:当客户端发送认证信息到服务器,服务器会为每个客户端创建一个会话, 会话信息中包含有客户端的身份以及各项状态信息,然后将其保存在服务器数据库中,每个会话都有一个唯一ID, 然后将session_ id作为Set-Cookie的字段值传输给客户端,客户端在下次请求服务器的时候就会把session_ id发送给服务器,服务器通过session id就能在数据库中找到会话信息进而获取到客户端的状态信息。这样的话客户端的敏感信息就不用在网络上持续传输了。
    cookie是客户端状态信息保存在客户端的,session是客户端状态信息保存在服务端。

3. 空行

\r\n用于间隔头部与正文

4. 正文

响应给客户端的数据
eg:
在这里插入图片描述

2. HTTPS协议

本质上来说还是http协议,只不过进行了一层加密:SSL加密(针对tcp协议的)。与http协议的不同点: 1. 安全度更高; 2. http使用的端口是80,而https的端口是443。

  • 问题:httpst协议的加密流程 、ssl加密流程
  • 加密传输:分为两个方面,一个是加密数据,一个是身份验证(数据就算加密了,但是对方身份不对,加密就失去了意义)

前置知识

  1. 身份验证

引入一个第三方权威机构,必须是双方都信任的机构,双方到权威机构颁发一个身份证书, 在通信前将证书发送给对方。对方根据证书中的信息(我是谁,在哪里颁发的证书,有效期…判断对方的身份,并且去第三方权威机构进行认证。通过认证后则身份验证成功。然后再进行通信,
在这里插入图片描述
上图中的验证是一 种双向身份验证,在需要双方身份验证的需求的时候使用(比如银行)。平时我们的浏览器访问通常其实只是单向验证( 只进行客户端验证服务器)。
2. 数据加密
避免数据被劫持

  • 对称加密: 使用相同的秘钥进行加密解密
    通信前将秘钥交给对方,自己使用秘钥进行数据加密,对方使用秘钥进行数据解密。不能一直使用某种秘钥,或者提前约定,因为这很容易被破解,所以只能每次通信前动态协商。这也存在安全隐患:秘钥也容易被劫持;但是也有好处:加密解密通常效率比较高。
  • 非对称加密: 加密和解密使用的秘钥不同
    生成一对秘钥(公钥与私钥),其中公钥用于进行数据加密,私钥用于对公钥加密的数据进行解密(公钥加密的数据使用公钥是无法解密的),因此通信前,将公钥传递给对方,对方使用公钥进行数据加密,数据到达后自己使用私钥进行解密,得到数据安全度非常高:不怕公钥被劫持;但是也有缺陷:加密解密效率很低。
  • 混合加密
    先进行非对称加密,通信前,将公钥交给对方,对方拿着公钥加密对称秘钥。这样对称秘钥就无法被劫持了,协商完毕之后,使用协商出的对称秘钥进行通信。通过这种方式既保证了安全,也保证了效率。

2.1 httpst协议的加密流程

在tcp协议之上加一个ssl加密~~

2.1.1 ssl加密流程

在实际ssI加密中,将身份验证与混合加密进行融合,
1.服务器(需要被验证身份的一方),生成一对秘钥(公钥与私钥)
2.服务器带着公钥,去权威机构生成-个证书(我是谁,权威机构是谁,有效期…公钥)
3.通信时,在tcp建立连接成功后,将证书首先发送给客户端
4.客户端收到证书,进行解析,得到各项数据,然后到权威机构进行服务器身份验证
5.验证成功,使用公钥加密自己所支持的对称加密算法列表以及一个随机数发送给服务器
6.服务器收到公钥加密的信息后,使用私钥进行解密,得到了客户端支持的算法列表以及随机数
7.服务器将自己支持的算法列表以及一
个随机数发送给客户端
8.客户端与服务器各自根据算法列表以及自己和对方的随机数生成一个对称秘钥
9.往后通信使用对称秘钥进行加密通信。
在这里插入图片描述

二、传输层

负责应用程序之间的数据传输; UDP/TCP协议

前置知识

  1. 再谈端口号
    端口号(Port)标识了一个主机上进行通信的不同的应用程序;
    在这里插入图片描述
    在TCP/IP协议中, 用 “源IP”, “源端口号”, “目的IP”, “目的端口号”, “协议号” 这样一个五元组来标识一个通信(可以通过netstat -n查看);

在这里插入图片描述
2. 端口号范围划分

  • 0 - 1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的.
  • 1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的
  1. 知名端口号(Well-Know Port Number)
    有些服务器是非常常用的, 为了使用方便, 人们约定一些常用的服务器, 都是用以下这些固定的端口号:
  • ssh服务器, 使用22端口
  • ftp服务器, 使用21端口
  • telnet服务器, 使用23端口
  • http服务器, 使用80端口
  • https服务器, 使用443

执行下面的命令, 可以看到知名端口号

cat /etc/services

我们自己写一个程序使用端口号时, 要避开这些知名端口号.

  • netstat
    netstat是一个用来查看网络状态的重要工具.
    语法:netstat [选项]
    功能:查看网络状态
    常用选项:
    n 拒绝显示别名,能显示数字的全部转化成数字
    l 仅列出有在 Listen (监听) 的服務状态
    p 显示建立相关链接的程序名
    t (tcp)仅显示tcp相关选项
    u (udp)仅显示udp相关选项
    a (all)显示所有选项,默认不显示LISTEN相关
  • pidof
    在查看服务器的进程id时非常方便.
    语法:pidof [进程名]
    功能:通过进程名, 查看进程id

1. UDP协议

1.1 UDP协议格式

在这里插入图片描述

  • 16位源端端口:描述源端处理进程

  • 16位对端端口:描述对端处理进程,源端与对端端口描述了是哪两个进程之间在进行传输

  • 16位数据报长度:描述了数据报的长度,包含报头(8字节)在内, 实际的数据长度是报文长度-8;
    因为报文长度的保存只用了2个字节,意味着一个udp报文最大不超过64k。报文中的数据大小(上层所sendto给与的大小)必须小于64k-8。 否则就会丢弃数据报错。(一个字节(byte->B)是8个比特位(bit->b)。所以2个字节是16个比特位——2的16次方=64K(65535)。最后减去头部报文长度是8
    在这里插入图片描述

  • 16位校验和:采用二进制反码求和算法,校验收到的数据与对方发送的数据是否完全一致, 不一致则丢弃。
    二进制反码求和算法:
    发送方组织好报文后,将校验和字段置为0,然后从第0个字节开始(包含报头)对每个字节取反求和,超出16位的,则将高位截断与低1 6位继续求和,完成之后,将校验和填充到校验和字段中接收方收到数据,从头到尾进行反码求和,这时候如果数据没问题,则刚好得到的结果就是0.

1.2 UDP协议的特性

UDP传输的过程类似于寄信.

(1)无连接

知道对端的IP和端口号就直接进行传输, 不需要建立连接;

(2)不可靠

udp没有确认机制,没有任何的丢包检测机制,数据包丢了也没有重传机制,并且也没有包序管理机制,因此udp传输既不保证数据能够安全到达对端,也不保证数据有序到达对端。如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息。

(3)面向数据报

一种有最大长度限制的,数据块传输方式(应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并)。因为udp协议中限制了一个udp报文必须小于64k (即数据必须小于64k-8)。它不能够灵活的控制读写数据的次数和数量;
数据块传输方式:
上层sendto给与一 个数据则直接封装报头进行发送,数据到达对端会包括报头存放在接收缓冲区中,上层recvfrom时候每次只能刚好取出一条完整数据,不能取出半条或者多条数据(因为如果取出半条或者一条半, 缓冲区中剩余半边数据,下次取的时候就不知道该取多少了)。因此udp通信有一个严格的点: 整条传输整条交付!也意味着: recvfrom的时候如果你给的buf空间大小,没有一条数据长度大则会接收失败。

1.3 对于编程的影响

  • udp不保证数据安全有序到达,需要程序员自己在应用层进行丢包检测以及包序管理机制以及重传机制,才能保证安全。至少要在上层有包序管理。
  • udp限制了数据报长度(面向数据报的),必须保证sendto发送的数据长度必须小于64k-8个字节大小。
  • udp是整条交付的,必须保证recvfrom时,给与的空间大小足够大,否则有可能会接收失败。

1.4 UDP的缓冲区

  • UDP没有真正意义上的 发送缓冲区. 调用sendto会直接交给内核, 由内核将数据传给网络层协议进行后续的传输动作;
  • UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃;UDP的socket既能读, 也能写,即全双工通信。

1.5 基于UDP的应用层协议

  • NFS: 网络文件系统
  • TFTP: 简单文件传输协议
  • DHCP: 动态主机配置协议
  • BOOTP: 启动协议(用于无盘设备启动)
  • DNS: 域名解析协议

1.6 UDP使用注意事项

我们注意到, UDP协议首部中有一个16位的最大长度. 也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部).然而64K在当今的互联网环境下, 是一个非常小的数字.如果我们需要传输的数据超过64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装。

1.7 UDP如何实现可靠传输?

UDP既然在传输层没有实现可靠有序传输,如果要实现可靠传输则需要程序员在应用层进行处理:
1.包序管理:给每一个数据报进行编号
2.丟包检测重传:确认应答机制+超时重传机制

2. TCP协议

TCP全称为 “传输控制协议(Transmission Control Protocol”). 如其名, TCP对数据的传输进行了一个详细的控制;

2.1 TCP协议端格式

在这里插入图片描述

  • 16位源端端口+ 16位对端端口:描述通信两端
  • 32位序号+ 32位确认序号:实现tcp的包序管理,以及确认应答机制(丢包检测)
  • 4位报头长度
    以4字节为单位, 描述tcp报头长度(0~40字节大小的选项数据)最大是60字节,最小20字节。 在解析tcp报头时,先取出固定长度20字节,然后根据报头长度-20取出指定长度的选项数据,剩下的就是应用数据。
  • 6位保留:暂时没有使用
  • 6位标志位:用于标识报文类型
    URG: 紧急指针是否有效
    ACK: 确认应答
    PUSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
    RST: 当收到了非法请求,或者连接出现问题则向客户端端发送RST重置连接报文,要求客户端重新建立连接
    SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
    FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段
  • 16位窗口大小:用于实现滑动窗口机制-进行流量控制-防止发送方发送数据过多
  • 16位校验和:校验接收到数据与发送的数据是否一致
  • 16位紧急指针:描述紧急数据(带外数据)的结束位置,正常数据的起始位置
  • 0~40字节的选项数据:不一定有 ,通常是一些协商信息或者保存一些额外数据的作用。

2.2 TCP协议特性

面向连接,可靠传输,面向字节流。

2.2.1 面向连接
(1)tcp连接管理

tcp是一个全双工通信,在通信前必须保证双方都具有数据收发的能力

  • 三次握手:
    0.服务端进入listen监听状态;1.客户端发送SYN连接请求;3.服务端收到SYN请求后回复SYN+ACK报文;4.客户端收到ACK+SYN后回复ACK。
  • 四次挥手:
    1.主动方关闭方发送FIN请求;2.被动关闭方收到FIN请求后,回复ACK;3.被动关闭方可以继续发送数据,当其准备关闭时候。发送FIN请求;4.主动关闭方收到FIN请求后,回复ACK。
    注意:FIN包只能表示自己不在给对方发送数据了,不代表不能接收数据了。
    在这里插入图片描述
  • 握手为什么是三次?挥手是四次?
    握手三次是因为两次不安全,四次没必要,两次不安全主要指的tcp全双工通信,需要双方都确认对方在线才能通信,而四次没必要主要是因为请求其实就是个标志位,SYN+ ACK两个请求完全可以合为一个进行发送。
    挥手四次是因为FIN包的断开连接只能表示己方不再给对方发送数据了,不代表自己不接收数据,被动关闭方进行确认之后,还有可能会给主动关闭方发送数据,因此不会直接发送FIN,而是等待上层确认关闭连接,不再发送数据的时候,才会发送FIN包给主动关闭方。
(2)状态管理

tcp是一个有状态的协议,在特定状态下只能干特定的事情

  • 三次握手
    客户端:SYN_SENT->ESTABLISHED;服务端:LISTEN->SYN_RECV->ESTABLISHED

在这里插入图片描述
SYN_ RCVD状态就是等待ACK,收到到其他的请求则认为非法请求,回复RST。

  • 三次握手失败两端是如何处理的?
    (1)客户端发送SYN失败:服务端什么都没收到则什么都不做。客户端会重传SYN请求。
    (2)服务端收到SYN回复SYN+ ACK失败:服务端并不会重传,客户端等待超时后会认为syn丢包,重传SYN。
    (3)客户端最后发送ACK失败:服务端等待超时,则回复RST报文,然后释放新建连接。
    (4)syn泛洪攻击-synflood:恶意主机伪造IP地址发送大量SYN请求不进行回复,占据服务端资源。
  • 四次挥手
    主动方:FIN_WAIT->FIN_WAIT2->TIME_WAIT->CLOSED; 被动方:CLOSE_WAIT->LAST_ACK->CLOSED
    在这里插入图片描述
  1. 一台主机上出现大量的close_ wait状态是什么原因?
    closee_ wait是被动关闭方收到FIN包进行确认回复之后,等待上层处理的状态,因此一台住上如果出现了大量连接处于close_wait,意味着有可能代码中在连接断开后,没有进行close,也就是没有进行四次挥手的下一步操作。
  2. close_ wait有什么用?为什么主动关闭方已经进行了确认回复了 不立即释放套接字,还要等待一段时间?
    (1)假设没有time_wait 状态,回复ACK之后直接释放套接字,但是最后一次等待ACK丢失了,ACK一旦丢失了,被动关闭方就会重传FIN。这时候如果恰好主动关闭方启动了一个新的套接字,刚好使用之前释放的地址信息,则这时候这个新家连接什么都没干,先收到了一个FIN包,这就不合理。
    (2)包括新建的套接字给服务端发送SYN请求,但是服务端处于LAST_ACK等待连接状态,就会认为请求非法,这也不合理。
    (3)总结:如果没有time_wait直接释放套接字,有可能由于最后一次ACK丢失导致的重传会对新连接造成影响。
  3. time_wait等待多长时间比较合适呢?
    等待时间一般是两个MSL(最大报文生成时间)时间比较合理,力求本次通信的所有数据都消失在网络中,
  4. tinme_wait的作用是什么?
    time_wait的作用就是等待两个MSL时间,处理有可能因为最后一次ACK丢失锁导致的FIN报重传,让本次通信的所有数据消失在网络中,避免对后续新连接造成影响,MSL默认通常是60秒。
  5. 一台主机上出现大量time_wait状态是什么原因,如何处理呢?
    time_wait状态是主动关闭方,在进行最后一次ack之后进入的状态,一台主机上出现了大量的time_wait,原因是这条主机上大量的主动关闭了套接字。常见于爬虫服务器(创建大量的客户端去请求服务器然后关闭)。
    解决方案:
    (1)可以把time_wait等待时间调短一点
    (2)可以使用套接字选项,启动端口地址复用功能,
    int setsockopt(int sockfd, int level, int optname, void *optval, int optlen)
    level:选项等级—— SOL_ SOCKET
    optname:选项名称——SO_ REUSEADDR(允许套接口和一个已在使用中的地址绑定)
    eg:
int optval = 1;
setsockopt(sockfd, SOL_ SOCKET, SO_ REUSEADDR, &optval, sizeof(optval ));
  1. time_wait到底是保护客户端还是服务端?
    time_wait说是用来保护主动关闭方的,但是实际上更多的是保护了客户端,因为服务端通常需要绑定固定的地址端口,反而因为time_wait的原因,在主动关闭后无法立即重新启动。
2.2.2 可靠传输
  • 可靠传输:tcp是如何实现可靠传输的(可靠传输就是安全有序的将数据传输到对端)?
1 面向连接

确保双方都具有数据收发的能力。

2 丢包检测

确认应答机制(每发送一个数据都要求对方进行一个确认回复,收到确认回复才认为数据发送成功)。

在这里插入图片描述
TCP将每个字节的数据都进行了编号. 即为序列号.
在这里插入图片描述
每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发.
在这里插入图片描述

在连续传输中,如果先收到了后边的数据,这时候不能进行确认回复,因为每个确认序号都要保证之前的所有数据都已经收到了,这样是为了避免因为确认应答丢失而导致重传。

3 超时重传机制

超过一定时间没有收到确认回复,则认为数据丢失,进行重传。

在这里插入图片描述
主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B;如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发;但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了;
在这里插入图片描述

因此主机B会收到很多重复数据. 那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的丢弃掉。这时候我们可以利用前面提到的序列号, 就可以很容易做到去重的效果.

  • 如果超时了,那超时的时间如何确定?
    (1)最理想的情况下, 找到一个最小的时间, 保证 “确认应答一定能在这个时间内返回”.但是这个时间的长短, 随着网络环境的不同, 是有差异的.如果超时时间设的太长, 会影响整体的重传效率;如果超时时间设的太短, 有可能会频繁发送重复的包;
    (2)TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间.
    (3)Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍.如果重发一次之后, 仍然得不到应答, 等待 2500ms 后再进行重传.如果仍然得不到应答, 等待 4500ms 进行重传. 依次类推, 以指数形式递增.累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接.
4 协议字段中的序号+确认序号字段

实现包序管理.实现确认应答机制。

5 校验和字段

校验数据一致性, 如果不一致则丢弃, 要求对方进行重传。

6 其他机制

Tcp为了实现可靠传输,用了很多机制,损失了很多传输性能,但是有些性能是没必要损失的,因此要尽量挽回,所以产生了很多机制。比如延迟发送、快速重传机制、滑动窗口机制、延迟应答机制、捎带应答机制等等,

(1) 延迟发送

发送方延迟发送数据,因为每次发送数据都会涉及到硬件的操作,效率较低,如果在发送大量短小数据的时候就很不划算,因此延迟发送会把多个小数据在发送缓冲区中堆积成为一个大数据进行一次发送。

(2) 快速重传机制

如果一个发送的数据包丢了,发送方如果总是要等到超时才能重传,效率就比较低,这时候采用快速重传机制效率就能得到提高。
接收方接收数据的时候,接收到的数据不是从预期起始开始的,则认为前边的数据可能丢失了,则这时候回复,前边预期起始序号的确认应答作。为重传请求,并且间隔连续发送三次。

在这里插入图片描述
不用等完全超时,而是连续三次收到相同的重传请求则对应序号的数据进行重传。(三次的原因是为了避免因为网络延迟而导致的重传,三次期间如果收到数据则,不满三条就不需要进行重传)

(3)滑动窗口机制
  • 问题:
    发送方发送数据过多,而接收方接收的太慢了,导致接收方的接收缓冲区满了,则多出来的数据被丢弃,丢弃就会导致重传——>效率降低了。
  • 解决办法:
    主要是基于协议字段中的窗口大小字段实现流量控制,接收方每接收一条数据就会进行确认应答,在确认应答的时候就会通过窗口大小字段告诉对方,最多在发送多少数据就不要发送了(这个窗口大小不会大于接收缓冲区的剩余空间大小),这样就可以避免因为发送数据过多而导致丢包。
    MSS:最大数据段大小,通过下层的数据报大小限制所计算出来-个最大传输数据长度。Tcp在发送数据的时候,每个报文都不会大于mss大小,而是在发送缓冲区中截取合适长度数据发送。
    在这里插入图片描述
  • 实现细节
    实际上通信双方都会维护两个窗口,一个是发送窗口,一个是接收窗口。
    窗口有前沿和后沿。
    发送窗口后沿:发送的起始数据序号;发送窗口前沿:发送的结束序号;接收窗口后沿:接收的起始数据序号;接收窗口前沿:接收的结束序号;
    发送窗口大小不会大于对方回复的窗口大小字段值:前沿-后沿 <对方win大小。接收窗口大小不会大于缓冲区剩余空间大小:前沿-后沿<剩余空间大小
    在这里插入图片描述
  • 滑动窗口机制中的特殊协议:
    (1)停等协议:发送一条数据后,必须等收到确认应答才会发送下一条数据(针对网络极差的情况)
    (2)回退n步协议:如果一条数据丢失,则会将丢失数据序号开始的数据都进行重传。(针对网络状况一般情况)
    (3)选择重传协议:哪条丢了,重传哪条.(针对网络状况较好情况)
(4)拥塞窗口机制(拥塞机制)
  • 问题
    如果发送数据过程中,网络状态突然变差,这时候发送的数据越多,则丢失的数据越来越多。最终导致大量重传从而降低效率。
  • 解决办法
    发送方会维护一个拥塞窗口,即发送方限制发送数据的大小。拥塞窗口一开始很小,但是涨幅非常快(指数级的增长),以这种形式进行网络探测,当然也会有一个阈值(即窗口大小)。一旦丢包则会重新开始拥塞控制。
    在这里插入图片描述
(5)延迟应答机制
  • 问题:
    因为接收方接收到底 数据都会进行确认回复,如果立即进行回复,不可避免大概率窗口都会变小,则发送方的发送数据量就会变小,吞吐量小了,传输性能就变低了。

  • 解决:
    因此采用延迟应答,收到数据之后不立即进行回复,而是等待一段时间,而这段时间内,上层就有可能将数据从缓冲区取出,则窗口大小会不变甚至变大,保持吞吐量。

(6)捎带应答机制
  • 问题:
    接收方会为发送方发送的每个数据进行确认回复,而一个确认回复就是一个空报头,而空报头的传输会导致占用带宽,降低传输效率。

  • 解决:
    如果这个时候要给对方发送数据,那就将这个确认回复和要发送的数据合在一起进行发送,这样可以提高传输效率。

(7)tcp保活机制

tcp保活机制:长时间无通信,则每间隔一段时间发送保活探测数据包,多次无响应则认为连接断开。间隔时间默认: 7200s, 75s,9s;可配置, 并且可单独进行套接字选项设置。

2.2.3 面向字节流

字节流传输:不管是发送还是交付,都可以以字节为单位,不像UDP那么死板,
整条传输整条交付。发送了1000个字节,可以一次全部接收,也可以分成多次进行接收,传输比较灵活。
字节流传输比较灵活,数据可以咋缓冲区堆积,想要多少给多少,但是这样虽然灵活,但是又存在其他缺陷:tcp粘包——有可能把多条数据当做一条数据进行处理。

  • 解决方案:
    tcp在传输层对数据的边界不敏感,因此程序员在上层进行数据边界管理。
  • 以特殊字符作为数据头或者数据尾,间隔多条数据(比如http的做法,头部以\r\n作为结尾)缺陷是如果数据本身就有特殊字符,需要做特殊处理
  • 固定数据长度,设置足够长度,数据不够则补位,缺陷是有补位则性能低,因为传输的数据多了,有超长数据比较麻烦。
  • TLV格式数据,每个数据有个固定长度的应用层头部,头部中定义了数据长度(udp、http的做法),先取出头部,然后根据头部中的数据 长度,取出剩余的数据。
  • 面试常考:粘包的产生与解决
    tcp传输数据是面向字节流的,数据在缓冲区中可以堆积,传输比较灵活,但是有可能有多条数据被当成一条数据处理。(注意: UDP不存在粘包问题因为UDP是整条交付的)
TCP小结

为什么TCP这么复杂? 因为要保证可靠性, 同时又尽可能的提高性能.

  • 可靠性:
    安全有序传输:面向连接、确认应答、超时重传、序号+确认序号、校验和
    避免额外丢包:滑动窗口,拥塞控制。
  • 提高性能:
    快速重传
    延迟发送
    延迟应答
    捎带应答
  • 其他:
    定时器(超时重传定时器, 保活定时器, TIME_WAIT定时器等)

2.3 基于TCP应用层协议

HTTP
HTTPS
SSH
Telnet
FTP
SMTP

3 TCP/UDP对比

  • 我们说了TCP是可靠连接, 那么是不是TCP一定就优于UDP呢?
    TCP和UDP之间的优点和缺点, 不能简单、 绝对的进行比较。TCP用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景;UDP用于对高速传输和实时性要求较高的通信领域, 例如, 早期的QQ, 视频传输等. 另外UDP可以用于广播;归根结底, TCP和UDP都是程序员的工具, 什么时机用, 具体怎么用, 还是要根据具体的需求场景去判定.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值