TCP/IP模型
自顶向下分别为:
l应用层
l传输层:负责两台主机之间的数据传输,如TCP协议,能够确保数据可靠地从源主机发送到目标主机
l网络层:地址管理和路由选择。例如IP协议通过IP地址来标识一台主机,并通过路由表的方式规划出两台主机之间的数据传输的线路。路由器工作在网络层
l数据链路层:负责设备之间的数据帧的发送和识别。例如网卡设备驱动,帧同步,冲突检测。例如交换机工作在数据链路层
l物理层
一般而言
l对于一台主机,它的操作系统内核实现了从传输层到物理层的内容
l对于一台路由器,它实现了从网络层到物理层
l对于一台交换机,它实现了从数据链路层到物理层
但是并不绝对。很多交换机也实现了网络层的转发;很多路由器也实现了部分传输层的内容 (比如端口转发);
本地通信 vs 网络通信-> 由于距离变长导致引发新的问题
l如何处理数据
l数据丢失了怎么办?
l如何精准地定位目标主机
l如何确定当下立即要去哪里
针对新问题,引入解决问题的方案->TCP/IP协议
TCP/IP协议理解:一种网络长距离通信的解决方案
朴素地理解:协议也是一种约定好的结构体,通信双方都认识地结构化地数据类型。因为协议栈是分层的,所以每层双方都有协议,同层之间,互相可以认识对方的协议。->同层之间可以认识对方定义的结构体
MAC地址:
MAC地址用来识别数据链路层中相连的节点
长度为48bit,6个字节,一般用16进制数字加上冒号的形式标识
MAC地址在网卡出厂时就确定了,不能修改。MAC地址通常是唯一的
以太网中,任何时候,只允许一台机器向网络中发送数据。
如果有多台同时发送,会发生数据干扰,称之为数据碰撞
局域网通信的过程中,主机对收到的报文确认是否是发给自己的,是通过mac地址判定
协议报头:每个网络协议,在真正数据前面加的一段”控制信息”
例如包裹快递时,假设寄一个洗面奶,当用户收到这个洗面奶后,通常会收到一个包裹,而不是一个赤裸裸地洗面奶。而收到的信息往往比需要的信息多一部分,这个多出的一部分就叫做协议报头。
例如:数据链路层的以太网报头:里面放源MAC,目标MAC,类型
而每层都有协议,所以当我们进行上述传输过程的时候,需要进行封装和解包
细节1:传输层协议中,既有TCP,又有UDP,怎么保证确定哪个协议呢?
不考虑应用层协议,任何协议应包含两种能力:
A,报头必须要能做到和有效载荷进行分离的能力
B,报头中必须包含:如何将自己的有效载荷交付给上层的具体那一个协议
细节2:底层收到报文,但是该报文不是发送给”我”的,那么”我”在数据链路层就直接丢弃
细节3:对一个报文自顶向下做封装,可以理解为从上往下做入栈操作,解包可以理解为出栈过程。
一般把应用层叫做请求与应答。封装了传输层的整个报文叫做数据段。添加了网络报头的喜欢叫做数据报。添加链路层报头的,叫做数据帧。
除了报头,剩下的叫做“有效载荷“
报文 = 报头 + 有效载荷
在网络传输的过程中,数据不是直接发送给对方主机的,而是先要自顶向下将数据交付给下层协议,最后由底层发送,然后由对方主机的底层来进行接受,在自底向上进行向上交付。
网络中的地址管理:认识IP地址
IP协议有IPv4和IPv6两个版本
对于IPv4,IP地址是一个4字节,32位的整数,通常用点分十进制的字符串标识IP地址
例如192.168.0.1
MAC地址是一种只在局域网有效的标识地址的方式。
因为MAC地址是数据链路层的地址,它的作用范围是同一广播域
同一个网段里的所有主机,他们的IP地址前面几个是一样的。只要在网络层,就会有路由功能。主机A不知道172主机在哪里!但是主机A一定能确定,报文绝对不是发给本地网络的主机(因为IP地址的前面几个不一样)。所以主机A就把报文发送给路由器,有了目的IP,可以判定,要将报文发送给路由器
可以看到,在进行路由的过程中,IP地址不变,而MAC地址却一直在变,所以MAC地址只会在本地局域网有效。
网络层+IP的意义:给网络提供了一层虚拟化层,让世界所有的网络都叫做IP网路。
Socket编程复习
不管是网络还是系统。人和OS交互的唯一方式是:启动进程。
数据传输到主机不是目的,而是手段。到达主机内部,在交给主机内的进程,才是目的。
上网的两种行为:
1,从远端服务器,获取数据
2,本地数据上传到远端服务器
数据通过进程(进程在内存中),上网的时候,所有的数据经过网卡,把数据发送到网络
内存和网卡,网卡和网络之间的关系,我们叫做IO
冯诺依曼体系规定网卡只能进行IO操作。决定了应用层软件只能做获取信息和发送信息的行为。
网络通信本质是进程间通信,而进程间通信的前提是让两个进程看到同一份不同资源,这个同一份资源,指的就是网络
但是系统中,同时存在很多进程,当数据到达目标主机之后,怎么转发给目标进程?->引入端口号
认识端口号
端口号是传输层协议的内容
是一个2字节(16位)的整数
端口号用来标识一个进程,告诉OS,当前的这个数据要交给哪一个进程来处理
IP+端口号能够标识网络上的某一台主机的某一个进程
一个端口号只能被一个进程占用(但一个进程可以占用多个端口号)
让进程绑定对应的端口号,可以理解成链入到哈希表里。
Linux下一切皆文件,网卡是文件,在内核创建struct file对象和文件描述符。打开的网络文件有自己的内核接收缓冲区。把有效载荷拷贝到自己的接收缓冲区里,上层可以像文件一样把文件数据读取到应用层
Pid也能标识进程,但是在网络中却不用这个,为什么?
1,不是所有的进程都要进行网络通信,不是所有的进程都要有端口号
2,Pid是一个系统的概念,重新启动进程,pid会发生变化,那网络也要跟着变化,耦合度大
IP+Port = 全网内唯一的一个进程
我们把IP+Port叫做socket
(srcIP,srcPort,dstIP,dstPort)这样的四元组可以标识物联网中唯二的两个进程。
我们把ip+port叫做套接字socket
粗略认识TCP协议(传输控制协议)
l传输层协议
l有连接
l可靠传输
l面向字节流
面向字节流:数据像水一样,没有边界,没有结构,只是一串连续的字节,读到多少算多少
UDP协议(用户数据报协议)
l传输层协议
l无连接
l不可靠传输
l面向数据报
我们知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,那么网络数据流同样有大端小端之分,那么如何定义网络数据流的地址呢?
l发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出
l接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存
l因此,网络数据流的地址这样规定:先发出的数据是低地址,后发出的数据是高地址
lTCP/IP协议规定:网络数据流应采用大端字节序,即低地址高字节
凡是发送到网络中的数据,必须是大端
l如果当前发送主机是小端,就必须先将数据转成大端,否则就忽略,直接发送即可
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
例如 htonl 表示将 32 位的长整数从主机字节序转换为网络字节序,例如将 IP 地址转换后准备发送。
网络规定:所有发送到网络上的数据,都必须是大端的。
socket常见API
System V:本地之间进程间通信
Posix:能做网络通信,也能进行本地通信
三种套接字:网络socket,本地socket,原始socket
Sockaddr结构:如果用网络通信,就传sockaddr_in。如果用本地通信,就穿sockaddr_un。自己定义的结构可以是sockaddr_in或者sockaddr_un。但是传参数的时候必须穿sockaddr类型的指针(地址强转)。函数内部拿着前16位地址类型,自行区分是本地通信还是网络通信。
IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4包括16位地址类型,16位端口号和32位IP地址
IPv4和IPv6地址类型分别定义为常数AF_INET,AF_INET6
Sockaddr结构:
Sockaddr_in结构
虽然 socket api 的接口是sockaddr,但是我们真正在基于 IPv4 编程时,使用的数据结构是sockaddr_in;这个结构里主要有三部分信息:地址类型、端口号、IP 地址。
In_addr结构
in_addr用来标识一个IPv4的IP地址,其实就是一个32位的整数