ISO七层模型与TCP/IP四层参考模型逐层解析

1 篇文章 0 订阅
1 篇文章 0 订阅

ISO/OSI 七层参考模型

(依次从上往下)
应用层:http,ftp,ssh 协议 ——具体的应用
表示层:处理数据格式,数据压缩,加密——原机器的表示格式
会话层:管理整个会话过程
传输层:端到端 ——到达目标机器后,知道要叫给那个应用进程
网络层:数据包IP——数据在网络上到底怎么从原机器送到目标机器,寻址(寻找到目标机器的地址)路由的过程(路由器)
数据链路层:帧frame——把物理层的bit流成帧,提供可靠的传输(帧中继)
物理层:bit ——提供电气规范,网卡和水晶头

TCP/IP模型 四/五层模型

(依次从上往下)
应用层:http ftp 上三层的一个结合
传输层:tcp和udp协议
网络层:网络约定 ip/icmp
链路层:硬件及网卡
物理层:(若四层的话则不包括物理层)

DNS域名解析

DNS服务器是一个分布式系统,因为一台系统是不够的,是树状结构的。如果访问过某一域名在一定时间内是会缓存起来的,减少了我们再次获取的过程(也就是主机名,一种字符串使用hosts文件来描述主机名和ip地址的关系)。
在这里插入图片描述

内核协议栈

数据包封装:
在这里插入图片描述
数据包分用:
在这里插入图片描述
文件传输:
在这里插入图片描述
同一网段内的两台主机进行传输:
通过应用层这里的话是用FTP传输协议,发出要执行的命令 get test.c,之后通过运输层给其增加标号21,是因为目标机器中21号是用来专门处理ftp协议的,再将增加过标号的命令通过网络层将源ip和目标ip增添上去,最后由链路层将mac信息写上去,通过路由传递出去。
对于路由算法:它会经过一个路由器将前面的mac信息收到,看到是自己的在路由器上往上传一层,之后知道到达目标ip必须从那个路由上走,于是会再次得到一个mac,逐次往后,直到最终达到目标ip;达到目标ip看到是mac则将这一层解放看ip,确定后解放前面的ip,往传输层看,确定其编号21ftp。
自己画图实现的话则是:
路由

跨网段的主机文件传输:需要经过一个或多个路由器
在这里插入图片描述

逐层解析

链路层

以太网的格式:帧头 46~1500帧尾,若是不够,则全部补0
在这里插入图片描述
在这里插入图片描述
帧头:全部在数据中放着;
帧尾:只是一个校验码;

广播包:在真正的发送数据之前,因为不知道B目标的缓存mac地址,所以会先发送一个广播包(ARP)来得到目标的应答包,以此来得到应答的mac地址(0806是ARP包,0800是IP包)
RARP:是知道mac去找ip,而ARP是知道ip去找mac,两者正好相反
在这里插入图片描述
ARP:地址解析协议
广播包:ffffffffffff每一个都能够收到,不是自己的就不用回应,是自己的则会进行回应(相当于学校喇叭广播一般),A给B发出的称为ARP请求报,而B给A发回的是ARP应答报。
所谓的ARP欺骗,是伪装成你想找的,将mac传递回去(dos攻击)
MTU最大传输单元:以太网最大只能够装101(相当于发快递时对包裹尺寸的限制)

路由:在复杂的网络结构中,找出一条通往终点的路线。
当IP数据包到达路由器时,路由器会先查看目的IP,路由器决定整个数据包是能够直接发送给目标主机,还是需要发送给下一个路由器,以此反复一直到达目标IP地址。

网络层

ip地址的分类:八位一体
点分十进制,每个字节范围是:0~255
五类ip地址

A类 0.0.0.0127.255.255.255
B类 128.0.0.0191.255.255.255
C类 192.0.0.0223.255.255.255
D类 224.0.0.0239.255.255.255
E类 240.0.0.0247.255.255.255

IP地址与子网掩码做与运算可以得到网络号,主机号从全0到全1就是子网的地址范围。
在这里插入图片描述
IP地址和子网掩码还有一种更简洁的表示方法,例如140.252.20.68/24,表示IP地址为140.252.20.68, 子网掩码的高24位是1,也就是255.255.255.0。

如果网络号相同的话,主机之间是可以相互通的,属于同一个网络(同一个局域网的网络号必须相同,否则就连接不上),如果网络不同的话,主机之间是不能够互通的,需要跨路由器进行转换。
将ip地址中的主机地址全部设为0,就成为了网络号,代表整个局域网。
将ip地址中的主机地址全部设为1,就成为了广播地址。
本机回环地址:127.0.0.1 查看内核协议栈里网络模块的运行状态是好是坏
解决ip不足的方法:动态分配ip地址;NAT技术;IPV6
ip是跨网络的,是不可靠的(网络丢包,不能够保证数据报一定能够到达目的地,如果传不到则回返回错误的信息),是无连接的(多个数据报之间没有任何关系是相互独立的)
在这里插入图片描述
8位生存时间(通常表示报在被路由器丢弃前都经过的网段数量),每过一个生命周期减一,也就是路由器的个数减一,直到减为零为止。

路由器:不同的网路之间选择路径,其内部有一张路由表
在这里插入图片描述
NAT:出去时候把私有ip转换为公有ip,回来的时候再把公有的ip转换为私有ip
在这里插入图片描述

传输层

TCP协议:面向连接的可靠字节流套接字

  1. 应用数据被分割成TCP认为最为合适的发送数据块(这和UDP完全不同,UDP应用程序产生的数据报长度将保持不变)
  2. 当TCP发出一个段后,必须收到成功的信号,否则定时器会超时重传
  3. TCP数据是放在ip上传的(避免失序会对收到的数据进行重新排序以正确的顺序交给应用层,并且重复了会自动丢掉),并且tcp会进行校验(TCP会保持它首部和数据的校验和,这是一个端到端的校验和,目的是检测数据在传输过程中的任何变化,如果收到的检验和有差错,将丢弃这个报文和不确认收到此报文,以希望发送端超时重发)
  4. 流量控制主要针对于对接受机器的接受能力去进行传输的。

面向字节流:创建一个TCP的socket同时在内核中创建一个发送缓冲区和接收缓冲区
调用write时,数据会先写入发送缓冲区中,如果发送的字节数太长会被拆分成多个TCP数据包发送,如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多或合适的时机发送出去。
接收数据的时候,数据也时从网卡驱动程序到达内核接收缓冲区,然后应用程序调用read从接收缓冲区拿数据。
TCP的连接既有发送缓冲区,也有接收缓冲区,那么对于这一个连接,既可以读数据也可以写数据这个概念叫做全双工。

端口命令:netstat -anp
在TCP/IP协议之中,使用“源IP”, “源端口号”, “目的IP”, “目的端口号”, “协议号” 这样一个五元组来标识一个通信,可以使用这个端口命令来查看。
如果是自己写的话会从1024-49151上进行选择,(49152-65535是私有端口号动态分配的)一般我们自己的端口是不需要选择的,系统会临时选择,也就是会从49152-65535之间进行选择。

TCP包首部:
在这里插入图片描述

  • ACK:为1代表有效,0代表无效
  • PSH:为1表示拿到之后第一时间要交给应用程序
  • RST:如果接收端关了则给我传回信号,进行重置
  • SYN:置为1则表示我和你要建立连接
  • FIN:置为1表示要关闭连接
  • 16位窗口大小是表示接收端的接受能力有多强
三次握手和四次挥手

tcp通信之前要建立连接的过程:TCP的三次握手(连接的话必须是客户端先行发起)
tcp通信关闭的话需要四次挥手,因为客户端到服务器关闭了,但服务器到客户端尚未关闭,还会接着将需要的数据传输过来,因此需要四次(半关闭状态还是可以发送数据)(结束的话可以是客户端发起,也可以是服务端发起)
在这里插入图片描述

为什么需要三次握手:客户端有去有回,服务端也必须有去有回才能够证明此路是通的(传i的话收到的 则是i+1,表示我收到了)
TCP将每个字节数据都进行编号,即序列号,每个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了那些数据,下一次你从哪里开始发。
同步序号怎么选择:不能够从固定的1开始选择

服务端状态转化:

  • [CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态, 等待客户端连接;
  • [LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端发送SYN确认报文.
  • [SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进入ESTABLISHED状态, 可以进行读写数据了.
  • [ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close), 服务器会收到结束报文段, 服务器返回确认报文段并进入CLOSE_WAIT;
  • [CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK状态, 等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
  • [LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK, 彻底关闭连接.

客户端状态转化:

  • [CLOSED -> SYN_SENT] 客户端调用connect, 发送同步报文段;
  • [SYN_SENT -> ESTABLISHED] connect调用成功, 则进入ESTABLISHED状态, 开始读写数据;
  • [ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时, 向服务器发送结束报文段, 同时进入FIN_WAIT_1;
  • [FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服务器的结束报文段;
  • [FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出LAST_ACK;
  • [TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(Max Segment Life, 报文最大生存时间)的时间, 才会进入CLOSED状态.
    在这里插入图片描述
  • last状态是为了保证客户端收到数据,而time_wait的话,则需要保持2倍的msl(TCP报文的最大生存时间)
  • time_wait的作用
    就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);
    同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK)

在TIME_WAIT状态时,想要重新启动服务器,需加做此操作

流量控制

接收端处理数据的速度是有限的,如果发送端发的太快,导致接收端的缓冲区被打满,整个时候如果发送端继续发送就会造成丢包,继而引起丢包重传等一系列连锁的反应。因此TCP支持根据接收端的处理能力来决定发送端的发送速度,整个机制就叫做流量控制。

  • 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段, 通过ACK端通知发送端;
  • 窗口大小字段越大, 说明网络的吞吐量越高;
  • 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
  • 发送端接受到这个窗口之后, 就会减慢自己的发送速度;
  • 如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端.

网络上很多计算机可能当前的网络状态就已经比较拥堵,因此在不清楚当前网络状态下,贸然发送大量数据很有可能雪上加霜,因此TCP引入了慢启动拥塞控制机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。

在这里插入图片描述
此处引入一个概念成为拥塞窗口,发送开始的时候,定义拥塞空窗大小为1,每次收到一个ACK应答,拥塞窗口加1,每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小作比较,取较小的值作为实际发送的窗口。(拥塞窗口增长速度是指数级的,慢启动只是指初始时慢,但是增长速度非常的快,而为了不增长那么快因此不能使拥塞窗口单纯的加倍,此处引入一个叫做慢启动的阙值,当拥塞窗口超过这个阙值的时候,不在按照指数方式增长而是按照线性方式增长)
拥塞控制:归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太多大的压力的折中方案。

滑动窗口机制

如果知道接收方的接受能力,则使用滑动窗口机制
在这里插入图片描述
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值;发送前四个段的时候不需要任何ACK,直接发送;而为了维护整个滑动窗口,需要开辟发送缓冲区来记录那些数据没有应答,只有确认应答过的数据才能从缓存区内删掉。
TCP:全双工通信,里面有两条线,客户端的接收缓存接收服务器的发送缓存,而服务器的接收缓存则接收客户端的发送缓存。
Nagle算法:发送数据的时候,把小包的的数据攒在一起发过去,可以提高网络传输的效率(链路层装数据最大的是1500,除过头部的话则是1460) 三种情况 1. 超时200ms 2. FIN关闭 3. 攒够1460字节则发送
Nagle算法的内核里面只能够有一个包是没有被确认的,如果没有应答则不会再发;如果希望及时发送的话,则可以把Nagle算法关闭。
在这里插入图片描述

TCP 套接字编程

Socket编程,socket函数,s是有大小区别的
socket
在这里插入图片描述
其中AF_INET 是ipv4,而STREAM则是tcp协议,DGRAM是dup协议
在这里插入图片描述
创建套接字(做监控功能,给连上的分配具体的套接字),之后bind进行绑定id,listen进行链接,连上之后新创建的socket才是真正和服务端进行打交道,这个socket会把服务端的ip信息全部填入到这里,就可以和客户端真正的进行传输数据,而accept所返回的套接字称之为已连接套接字。
在这里插入图片描述
accept的大小指的是已经完成的accept队列大小;

服务端
	//创建监听套接字
	int lfd = socket(AF_INET, SOCK_STREAM, 0);
	
	//ip初始化
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(9999);
	inet_aton("192.168.204.200", &addr.sin_addr);//字符串转整

	//绑定bind
	int r = bind(ldf, (struct sockaddr*)&addr, sizeof(addr));

	//设置成被动套接字listen
	if ((r = listen(lfd, SOMAXCONN) == -1) {
		perror("listen"),exit(0);
	}//否则为设置成功

	//等待连接accept
	int newfd=accept(lfd,NULL,NULL);

客户端
	//创建套接字
	int fd = socket(AF_INET, SOCK_STREAM, 0);

	//将ipi信息放入
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(9999);
	inet_aton("192.168.204.200", &addr.sin_addr);

	//连接
	int r = connect(fd, (struct sockaddr*)&addr, sizeof(addr));

客户端的connect的内部实现:
在这里插入图片描述

粘包问题


发送的数据报对方收到的是不完整的。这也是为何write函数一定要有返回值,是因为可能需要读取三个字节,但缓存区只有一个字节的空,这时候返回值就可以告诉我们只读了1个字节。

解决年报的方法:明确两个包之间的边界
定长数据包
2.

管道破裂

如果对端已关闭,你给对端发送的第一个协议报,对端会给你内核协议栈发送一个协议报RST,告诉你管道破裂, 因此服务端都会加上这段代码,将pipe信号忽略掉,就不会导致管道破裂将服务端关闭掉。
在这里插入图片描述

TCP异常情况
  1. 进程崩溃:打开的文件描述符都会关闭,但进程崩溃依然能够正常收到fin报(应用进程调用close主动关闭,还是调用exit将其关闭的,或者是进程崩溃的)这三种情况都会收到fin报,会结束掉进程,但是若这个时候你还在给对方发送数据,则对方段的内核会给你回个rst,说这里程序都已经没了已重置,若还发则会返回管道破裂的信息SIGPIPE,会把进程干掉。
  2. 操作系统崩溃:断电,网络故障(局域网)这三种情况操作系统都是收不到fin报的,这个时候若发送数据,则得不到对方ack,超时重传后会断了
  • 操作系统崩溃重启:若操作系统崩溃后重启,在你超时重传过程中已经重新启动,则数据可以正常传送,但是会收到一个rst,告诉你已重置
  • 网络:若是广域网的网络断掉,会选择其他路由器去走,若其他路由器都走不到目的地,则路由器会给远端发送消息说目标主机不可达。
网络聊天室

多个客户端连接同一个服务器(一个分配一个线程,但是在一个线程发送消息的时候,要将消息发送给每一个线程,这就需要使用链表,将每个线程存放在链表里)
在这里插入图片描述

为何TCP这么复杂?因为要保证可靠性,同时又尽可能地提高性能
可靠性:校验和,序列号,确认应答,超时重发,连接管理,流量控制,拥塞控制
提高性能:滑动窗口,快速重传,延迟应答,捎带应答

UDP传输协议:无连接不可靠但却更高效的数据报套接字

udp传输协议:一股脑地把应用程序传给ip层的数据发出去,不管对方有没有接收到(不可靠,但更高效)
UDP:面向数据报协议 无连接(想发就发,不相发就不发,不需要三次握手和四次挥手)不可靠(对方收到没收到不管,只管发),一般情况下更高效(没有超出重传,不需要校验) 数据包套接字,你给多少我就发多少。
在这里插入图片描述
面向数据报:应用层交给UDP多长的报文,UDP原样发送既不会拆分也不会合并,例如用UDP传输100个字节的数据,发送端调用一次sendto,发送100个字节,那么接收端也必须调用对应的一次recvfrom,接收100个字节,而不能循环调用10次recvfrom,每次接收10个字节。
UDP的缓冲区:UDP没有真正意义上的发送缓冲区,调用sendto会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作;具有接收缓存区,但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致,如果缓冲区满了,再到达的UDP数据就会被丢弃。
如果我们传输的数据超过64K,就需要再应用层手动分包,多次发送,并在接收端手动拼装。
UDP套接字编程:
在这里插入图片描述

对于UDP协议来说,是否也存在“粘包问题”呢?
对于UDP如果还没有上层交付数据,UDP的报文长度仍然在,同时UDP是一个个把数据交给应用层,就有很明确的数据边界。
使用UDP的时候,要么收到完整的UDP报文,,要么不收。

TCP和UDP对比

说了TCP是可靠连接,那么是不是TCP一定就优于UDP呢?
TCP用于可靠传输的情况,应用于文件传输,重要状态更新等场景;UDP用域对告诉传输和实时性要求较高的通信领域,例如早期的QQ,视频传输等,另外UDP可以用于广播。

应用层中的HTTP协议

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值