应用层
负责应用程序之间的数据沟通
网络通信协议: 网络数据传输中的数据格式规定
自定义协议
序列化: 将数据对象按照持久化存储或者网络数据传输的格式来排布。
反序列化: 对持久化存储或者传输的数据以指定的协议进行解析的过程。
知名协议
HTTP ——超文本传输协议
URL —— 统一资源定位符(网址)
URL编码
因为URL中特殊字符都具有特殊含义,因此查询字符串中有特殊字符时,会造成二义性,因此需要对用户提交的数据进行编码。
编码规则:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%(进行标识),编码成%XY格式。
URL解码
在查询字符串中每遇到%,则表示之后的两个字符需要进行URL解码。
HTTP协议格式
HTTP请求
首行: [方法] + [URL] + [版本]
Header: 请求的属性,冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束。
Body: 空行后面的内容都是Body, Body允许为空字符串。如果Body存在,则在Header中会有一个 Content-Length属性来标识Body的长度。
GET所提交的数据在URL中(有长度的限制),POST所提交的数据在正文中。
HTTP响应
首行: [版本号] + [状态码] + [状态码解释]
Header: 请求的属性,,冒号分割的键值对,每组属性之间使用\n分隔,遇到空行表示Header部分结束。
一些重要头部: Content-Length、Content-Type、Cookie、Set_Cookie、Transfer-Encoding、Location
Body: 空行后面的内容都是Body,Body允许为空字符串。如果Body存在,则在Header中会有一个 Content-Length属性来标识Body的长度。如果服务器返回了一个html页面,那么html页面内容就是在body中。
传输层
负责数据能够从发送端传输接收端(进程与进程之间)
端口号
端口号(Port)标识了一个主机上进行通信的不同的应用程序(进程),在TCP/IP协议中, 用 “源IP”, “源端口号”, “目的IP”,“目的端口号”,“协议号” 这样一个五元组来标识一个通信(可以通过 netstat -n查看)。操作系统拿到网卡接受的数据之后,通过数据中的端口号知道数据被存放到哪一个socket的缓冲区中
查看知名端口号 cat /etc/services
传输层的传输协议
UDP
UDP协议包含字段
源端口/目的端口: 传输,确定数据应该那个端口处理
校验和: 二进制的反码求和
数据包长度: uint_16类型
- 意味着UDP数据报最大的长度是64k,若sendto给予的数据大于64k-8(前面的信息长度为8)则会报错,因为UDP在传输层不会进行数据分段。
- 若传输的数据大于64k,则用户需要在应用层将数据分割成一个一个小段进行传输。
- UDP传输并不保证数据包的有序到达,因此也需要用户在应用层进行包序管理,所以UDP要求数据整条接受,否则造成数据出错。
- UDP提供整条数据向应用层交付(也不会出现半个数据,因为头部中有报文长度标识),也正因为数据报长度在协议头中有标识,因此UDP不会产生粘报问题。
UDP协议端格式
UDP的特点
无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接。
不可靠: 没有确认机制, 没有重传机制;如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息。
面向数据报: 数据整条收发,灵活性低,但是不会造成粘包问题;不能够灵活的控制读写数据的次数和数量。
UDP传输速度快,实现了传输层广播数据包。
UDP的缓冲区
- UDP没有真正意义上的发送缓冲区(不会使用缓冲区,接受到数据后都会直接封装,然后直接交给网络层)。调用sendtod会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作。
- UDP具有接收缓冲区.,但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致;如果缓冲区满了,再到达的UDP数据就会被丢弃。
TCP协议
TCP的特点
可靠传输
提高性能: 滑动窗口机制、延迟应答机制、捎带应答机制
因为tcp为了实现可靠性传输牺牲了部分传输性能,并且有可能因为ACK确认应答丢失也要重传数据,因此提出了以下几种机制来避免大量丢包重传以及ACK丢失重传来保证性能不再降低。
滑动窗口机制
流量控制+快速重传机制+拥塞控制
流量控制
- tcp通过双方协商窗口大小进行流量控制,避免因为缓冲区数据塞满而大量丢包重传。
- 通过协议字段中的窗口大小,双方进行协商,一次可以能最多传输多少条数据,之后等待确认应答,不需要进行一一停留。
- 双方都维护一个窗口大小,发送方先框住一部分数据,接受到确认应答后,框开始向后移动发送数据,对方同样在窗口中使用框滑动的方式接受。
快速重传机制
- 每条数据的确认回复都必须按序回复,若前面的数据没有收到,则不会对后边的数据进行回复。意味着若收到一条回复,表示ACK确认序号之前的数据全部安全到达,不会因为前边的数据的ACK丢失而重传数据。
- 若前边的数据丢失,则接受方收到后发的数据立即发送重传请求,并且连发三次,若发送方连续收到三次重传请求,则认为数据丢失,进行重传。
- tcp对相同的数据包会丢弃,这样就算前面的数据因为延迟到达或者丢包,都不会对重传请求造成影响,因为无论是延迟到达还是重新发送的数据报到达,都可以减少等待确认前面数据丢包的时间。
- 如果数据次序发生错误,当前面的数据未收到,收到后面的数据,先将收到的后面的数据放入缓冲区,等待前面的数据到达后,再将缓冲区的数据取出。
拥塞控制
慢启动,快增长
- 双方虽然窗口可能很大,但是发送的数据并不大(因为刚开始时不知道网络状态),发送数据成功后,之后发送的数据按指数的增长形式,直到达到阈值,则不再增长。
- 发送端控制一个拥塞窗口,再进行数据传输时进行网络探测式的发送,若网络状态良好时发送的数据快速增长,达到阈值,则不再继续增长。若传输过程中出现丢包,则重新初始化拥塞窗口。
- 拥塞控制为了避免网络状态因为网络状态不好导致通信初始,大量数据包丢失导致重传的情况,降低性能。
延迟应答机制
接收方收到数据后并不立即确认回复,而是等待一段时间,因为这段延迟的时间内,有可能用户已经recv将缓冲区中的数据取走,窗口就可以尽可能的保证最大大小,保证传输吞吐量。
捎带应答机制
接收方对每一条数据的确认回复,都需要发送一个tcp数据报,但空包头的传输会降低性能,因此会在即将要发送的数据包头中包含确认信息(可以少发一个确认的空包头)。
面向字节流
传输灵活
- 发送方每次调用send都会将数据放到发送缓冲区中,然后内核选择合适的时机发送数据。
- 接收方网卡接受到数据,都会将数据放到接受缓冲区中,用户recv就是从接受缓冲区取数据。
粘报问题主要发生的位置
- 发送缓冲区中的数据堆积
- 接受缓冲区中的数据堆积
粘包本质原因: 数据之间没有明显的边界,tcp只管传输数据的字节流导致发送端/接受端因为数据的堆积在实际发送或recv时一次获取到半条或多条数据。
解决办法: tcp在传输层没有数据边界,但是用户可以在应用层进行边界处理
常见方法: 特殊字符间隔(HTTP),定长数据(UDP头中包含长度)
TCP协议段格式
TCP异常
发送端机器掉电/网线断开,接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了, 就会进行reset。即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在。如果对方不在,也会把连接释放。
另外,应用层的某些协议,也有一些这样的检测机制。例如HTTP长连接中,也会定期检测对方的状态。断线之后,也会定期尝试重新连接。
tcp协议栈有自身的保活机制: 长时间无数据通信,则发送保活探测包,进行链接探测,若多个探测包都没有响应,则认为断开链接。
链接断开的体现: recv返回0,send出发SIGPIPE异常。
注: 具体的TCP/UDP使用可参见博客《Linux — 网络套接字编程》
https://blog.csdn.net/lw13572259173/article/details/93165915