TCP/IP四层网络

一.OSI七层模型

物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的数模转换与模数转换),这一层的数据叫做比特。

数据链路层:定义了如何让格式化数据以进行传输,以及如何让控制对物理介质的访问,这一层通常还提供错误检测和纠正,以确保数据的可靠传输。

网络层:在位于不同地理位置的网络中的两个主机系统之间提供连接和路径选择,Internet的发展使得从世界各站点访问信息的用户数大大增加,而网络层正是管理这种连接的层。

传输层:定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的), 主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组,常常把这一层数据叫做段。

会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路,主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)。

表示层:可确保一个系统的应用层所发送的信息可以被另一个系统的应用层读取。例如,PC程序与另一台计算机进行通信,其中一台计算机使用扩展二一十进制交换码(EBCDIC),而另一台则使用美国信息交换标准码(ASCII)来表示相同的字符。如有必要,表示层会通过使用一种通格式来实现多种数据格式之间的转换。

应用层: 是最靠近用户的OSI层,这一层为用户的应用程序(例如电子邮件、文件传输和终端仿真)提供网络服务。

    OSI引入了服务、接口、协议、分层的概念、TCP/IP借鉴了OSI的这些概念建立了TCP/IP模型。简单点说OSI是一种理论下的模型,而TCP/IP已被广泛使用,成为网络连接的标准。

二.TCP/IP四层模型

TCP/IP协议是一系列网络协议的总和;包括:TCP,IP,UDP,ARP等,这些被称为子协议。在这些协议中,最重要、最著名的就是TCP和IP。因此,大部分网络管理员称整个协议族为“TCP/IP”它定义了电子设备如何接入互联网,以及数据如何在它们之间互相传输。

扫描二维码关注公众号,回复: 14364955 查看本文章

 

链路层:有时也称作数据链路层或网络接口层,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡,负责数据帧的发送和接收。

网络层:提供数据封包传送服务。在TCP/IP协议族中,网络层协议包括IP协议(网际协议),ICMP协议(Internet互联网控制报文协议),以及IGMP协议(Internet组管理协议)。

运输层:提供进程间的数据传输。在TCP/IP协议族中,有两个互不相同的传输协议:TCP(传输控制协议)和UDP(用户数据报协议)。

应用层:负责处理特定的应用程序细节,应用程序间沟通的层(HTTP)。

本文福利,免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部↓↓

我们来模拟一下组包和发包的过程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h> //struct ifreq
#include <sys/ioctl.h> //ioctl、SIOCGIFADDR
#include <sys/socket.h>
#include <netinet/ether.h> //ETH_P_ALL
#include <netpacket/packet.h> //struct sockaddr_ll
 
 
unsigned short checksum(unsigned short *buf, int nword);//校验和函数
int main(int argc, char *argv[])
{
//原始套接字,用来传输底层协议
int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
unsigned char send_msg[1024] = {
//MAC
0xc8, 0x9c, 0xdc, 0xa7, 0x13, 0x7c,
0xc8, 0x9c, 0xdc, 0xb7, 0x0f, 0x19,
0x08, 0x00,
//IP
0x45, 0x00, 0x00, 0x00,           
0x00, 0x00, 0x00, 0x00,
0x80, 17,   0x00, 0x00,
//TTL:协议:UDP、16位首部校验和
172,  200,   223,  4,
//源ip
172,  200,   223,  4,
//目的ip
//UDP
0x09, 0x79, 0x09, 0x79,           
0x00, 0x00, 0x00, 0x00, 
};
 
int len = sprintf(send_msg+42, "1:%d:%s:%s:%d:%s", 123,"xx", "xx",32,"逗你呢");//78个字节
if(len % 2 == 1) {
 ++len;
}
 
*((unsigned short *)&send_msg[16]) = htons(20+8+len);
*((unsigned short *)&send_msg[14+20+4]) = htons(8+len);
//3.UDP伪头部
unsigned char pseudo_head[1024] = {
//UDP伪头部
172,  20,   223,  4,
//src_ip: 172.20.226.17
172,  20,   223,  4,
0x00, 17,   0x00, 0x00,             
};
 
*((unsigned short *)&pseudo_head[10]) = htons(8 + len);
memcpy(pseudo_head+12, send_msg+34, 8+len);
*((unsigned short *)&send_msg[24]) = htons(checksum((unsigned short *)(send_msg+14),20/2));
*((unsigned short *)&send_msg[40]) = htons(checksum((unsigned short *)pseudo_head,(12+8+len)/2));
 
struct sockaddr_ll sll;
struct ifreq ethreq;
strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);
 
if(-1 == ioctl(sock_raw_fd, SIOCGIFINDEX, ðreq)) {
    perror("ioctl");
    close(sock_raw_fd);
    exit(-1);
}
 
bzero(&sll, sizeof(sll));
sll.sll_ifindex = ethreq.ifr_ifindex;
len = sendto(sock_raw_fd, send_msg, 14+20+8+len, 0 , (struct sockaddr *)&sll, sizeof(sll));
if(len == -1) {
    perror("sendto");
}
    return 0;
}
 
unsigned short checksum(unsigned short *buf, int nword)
{
    unsigned long sum;
    for(sum = 0; nword > 0; nword--) {
    sum += htons(*buf);
    buf++;
}
sum = (sum>>16) + (sum&0xffff);
sum += (sum>>16);
return ~sum;
}

2.1 协议详解

2.1.1 IP

IP是TCP/IP协议族中最为核心的协议,所有的TCP/UDP/ICMP及IGMP数据都以IP数据报格式传输,IP提供不可靠无连接的服务,用来提供从源IP到目的IP的服务。

不可靠:不能保证IP数据包能成功的传输到目的IP上。

无连接:IP不维护任何连接,只负责维护数据包,数据包也可以不按顺序发送。

版本:IP协议的版本,目前最广泛使用的IP协议版本号 IPV4.
首部长度:32bit字节。
服务类型:一般取值为0。
总长度:整个IP数据包长度,包括头和数据。
标识:每产生一个数据报,计数器就加1。
标志:分为3个字段,依次为保留位、不分片位和更多片位
片偏移:被分片后,某片在源分组中的相对位置,也就是相对于数据段的起点,该片从何处开始。
生存时间:数据报在网络中的寿命,经过路由器TTL将减1,TTL为0将会被路由器丢弃。
协议:此数据使用了哪种协议,常见的有ICMP-1,IGMP-2,TCP-6,UDP-17,IPv6(41)。
首部校验和:只校验数据报的首部数据。
源IP地址:发送方IP地址
目的IP地址:接收方的IP地址。

2.1.2 TCP

       面向连接的可靠的传输层通信协议,使用流程包括建立连接-使用连接-释放连接,也是一种全双工模型,即数据可以在同一时间双向传播。同时TCP具有可靠性,对数据包排序和检错,错误的数据包将会被重传。

源端口号: 发送方端口号(决定那个程序发送)。
目的端口号:接收方端口号(决定那个程序接收)。
序列号:TCP序列号对数据包进行标记,用来保证可靠性,避免丢包乱序。
确认序号:对发送方的确认信息,告诉发送方这个序列号之前的数据包都收到了。
首部长度:TCP首部长度。
保留:占6位,保留为今后使用,目前应置为0.
URG: 此位置1,表明紧急指针字段有效,它告诉系统此报文段中有紧急数据,应尽快传送确认
ACK: 仅当ACK=1时确认号字段才有效.
PSH:当两个应用进程进行交互式的通信时,有时在一端的应就能够收到对方的响应。在这种情况下,TCP就可以使用推送(pu置1,并立即创建一个报文段发送出去,接收方收到PSH=1的报文段付给接收应用进程,而不再等到整个缓存都填满后再向上交付
复位RST: 用于复位相应的TCP连接同步SYN: 仅在三次握手建立TCP连接时有效。
终止FIN:用来释放一个连接。
窗口:指发送本报文段的一方的接收窗,接收数据段的数目。
校验和:发送方校验一次,接收放校验一次,如果两次的校验和一致说明数据正确,否则丢弃该数据。
紧急指针:仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据),即指出了紧急数据的末尾在报文中的位置。

2.1.3 UDP

  1. 源端口号:发送方端口号
  2. 目的端口号:接收方端口号
  3. 长度:UDP用户数据报的长度,最小值是8(仅在首部)
  4. 校验和:检测UDP用户数据报在传输中是否有错,有错就放弃。

2.2 TCP的三次握手和四次挥手

建立连接协议(三次握手)

第一次握手:客户端发送syn包(syn=x)的数据包到服务器,并进入SYN_SEND状态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP连接都将被一直保持下去。注意为什么采用三次握手而不是两次?

两次握手有可能没有建立连接就开始发送信息,信息发送失败会不同重试,造成死锁。

终止连接协议(四次挥手)

第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可以接受数据。

第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号, SYN 和 FIN 都有seq序号)。

第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。

第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1完成四次挥手。

为什么采用四次挥手?

在TCP连接中,服务端的SYN和ACK向客户端是一次发放的,而断开连接的过程,服务端向客户端发送的ACK和FIN是两次发送的。因为在服务端接收到客户端的FIN后,服务端可能还有数据要传输,所以先发送ACK,服务端处理完自己的事情后,就可以发送FIN断开连接。

2.3 TCP如何保证可靠性
 

2.3.1 校验和
      在发送方将整个报文段分为多个16位的段,然后将所有段进行反码相加,将结果存放在检验和字段中,接收方用相同的方法进行计算,如最终结果为检验字段所有位是全1则正确(UDP中为0是正确),否则存在错误。

2.3.2 序列号
       在数据包中都带有编号,能够保证可靠性,去重也是基于序列号实现的。在TCP的首部中有一个标志位——ACK,此标志位表示确认号是否有效。接收方对于按序到达的数据会进行确认,当标志位ACK=1时确认首部的确认字段有效。进行确认时,确认字段值表示这个值之前的数据都已经按序到达了。而发送方如果收到了已发送的数据的确认报文,则继续传输下一部分数据;而如果等待了一定时间还没有收到确认报文就会启动重传机制。

2.3.3 超时重传
保温发出后在超时时间处没有收到接收方的回应,则会启动重传机制。有两种情况,一:发送包丢失,二:报文传输超时。

2.3.4 连接管理
三次握手和四次挥手

2.3.5 流量控制
滑动窗口:滑动窗口本质上是描述接受方的TCP数据报缓冲区大小的数据,发送方根据这个数据来计算自己最多能发送多长的数据,假设A向B发送报文,在建立连接的的阶段B告诉了A我的接收窗口rwnd=一个数值,所以发送窗口不能大于这个数值。第一次发送数据,接收窗口的大小根据链路带宽的大小来决定的,通过采取慢启动和拥塞避免算法等机制来使带宽和性能取得最佳。

Nagel算法:

1比特流协议:把要发送的报文的第一个字节发送出去,接收方确认后再把缓冲区的剩余字节打包发送出去。

后退N协议:由于停等协议要为每一个帧进行确认后才继续发送下一帧,大大降低了信道利用率,因此又提出了后退n协议。后退n协议中,发送方在发完一个数据帧后,不停下来等待应答帧,而是连续发送若干个数据帧,即使在连续发送过程中收到了接收方发来的应答帧,也可以继续发送。且发送方在每发送完一个数据帧时都要设置超时定时器。只要在所设置的超时时间内仍收到确认帧,就要重发相应的数据帧。如:当发送方发送了N个帧后,若发现该N帧的前一个帧在计时器超时后仍未返回其确认信息,则该帧被判为出错或丢失,此时发送方就不得不重新发送出错帧及其后的N帧。

选择重传:在后退n协议中,接收方若发现错误帧就不再接收后续的帧,即使是正确到达的帧,这显然是一种浪费。另一种效率更高的策略是当接收方发现某帧出错后,其后继续送来的正确的帧虽然不能立即递交给接收方的高层,但接收方仍可收下来,存放在一个缓冲区中,同时要求发送方重新传送出错的那一帧。一旦收到重新传来的帧后,就可以原已存于缓冲区中的其余帧一并按正确的顺序递交高层。

拥塞控制:

慢启动:TCP传输的过程中,发送端开始发送数据的时候,如果刚开始就发送大量的数据,那么就可能造成一些问题。网络可能在开始的时候就很拥堵,如果给网络中在扔出大量数据,那么这个拥堵就会加剧。所以TCP引入了慢启动的机制,以1开始,以2的指数增长。

拥塞避免:当拥塞窗口 >= 门限值时,就会进入“拥塞避免算法”。这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。是一个线性上升的算法。(门限值:拥塞窗口的一半但不小于2倍最大报文长度)

快重传:当发生超时重传后,门限值会变成拥塞窗口的一半。

快恢复:重新执行"慢开始",达到减半的门限值后执行“拥塞避免”。

2.4 TCP连接复用

2.4.1.为什么需要tcp连接池

服务和服务之间的连接是开发过程中很常见的操作,为了服务解耦,减少相互依赖,增强系统稳定性,灵活性,所以会增加许许多多的服务通信链路,随着服务通信链路的增加,网络通信次数就会成倍的增长,那么随之而来的就是网络资源的消耗加剧,例如:带宽,连接数以及cpu,内存等。

每个连接建立时都会申请内存用来做socket buffer

每个连接都要做三次握手四次挥手

每个连接关闭时都要释放内存空间

并发高时,会产生大量的连接,影响系统调度,会占用太多系统资源

2.4.2.连接池的实现流程

2.4.3 连接池的结构

空闲连接切片:freeConns []*Conn

空闲连接的锁:lastDialErrorMu sync.RWMutex

控制连接池大小的管道:queue chan struct{}

2.4.4 获取连接

验证连接池是否关闭。

验证是否超过连接池的的最大连接数,没超过计数管道容量减少一个单位;如果超过了1秒后重试,直到重试时间超过最大等待时间则返回失败。

没有超过连接池最大连接数,如果空闲连接切片中有连接,则判断获取到的连接是否超时;如果没有空闲连接则创建一个新连接,创建成功返回,创建失败则将之前技术管道容量减少的一个单位还原。

2.4.5 释放连接

如果连接失效,则把连接关闭,计数管道容量增加一个单位。

如果连接没有失效,把连接放到空闲连接切片里,计数管道容量增加一个单位。

原文链接:TCP/IP四层网络 - 资料 - 我爱音视频网 - 构建全国最权威的音视频技术交流分享论坛

 本文福利,免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部↓↓

猜你喜欢

转载自blog.csdn.net/m0_60259116/article/details/125643240