linux–网络协议初识
事实: 通信的主机之间距离变长了---->引发出新的通信问题?
- 如何使用数据问题(应用层)
- 可靠性问题(传输层)
- 主机定位问题(网络层)
- 数据报局域网转发问题(数据链路层)
人提出网络协议解决方案—方案有好有坏–为了方便扩展,替换或维护–故将网络协议设置为分层结构
层和层之间松耦合,可以随时替换或维护.
-
为什么是协议? 为什么? 怎么办?
- “协议是一种约定”–计算机协议就是计算机和计算机之间的约定
- 为什么—通信主机之间距离变远了, 减少通信成本;沟通成本
- 制定协议规则
-
什么是TCP/IP协议?
- TCP/IP协议本质是一种解决方案
- TCP/IP协议能分层,前提是问题们本身可以分层
-
网络分层(why?what?how?)
- why? 层和层降低耦合,方便维护.
- what? how?
TCP/IP五层模型
- 物理层:负责光电信号的传递方式,比如现在的网线(双绞线).光纤,物理层的能力决定了最大传输速率,传输距离,抗干扰能力等, 集线器(放大信号)工作在物理层
- 数据链路层:负责设备之间的数据帧的传送和识别,如: 网卡设别的驱动,帧同步, 冲突检测,数据差错效验等 有以太网 ,令牌环网, 无线LAN等标准,交换机工作在数据链路层.
- 网络层: 负责地址管理和路由选择,例如在IP协议中,通过IP地址标识一台主机,并通过路由表的方式规划处两台主句之间的数据传输的线路(路由),路由器工作网络层.
- 传输层: 负责两台主机之间的数据传输,如传输控制协议(TCP),能够保证数据可靠的从源主机发送到目标主句.
- 应用层: 负责应用程序之间的沟通, 如:简单的电子邮件传输(SMTP), 文件传输协议(FTP),网络远程访问协议(Telent), 网络编程主要针对应用层.
操作系统和网络协议栈之间的关系?
- 朴素的理解协议, 站在语言角度,重新理解协议
问题:主机B可以识别data,并准确提取 a = 10, b = 20, c = 30吗?
可以! 因为双方都有相同的结构体类型struct protocol,也就是说使用相同的代码实现协议,用相同的自定义类型,天然就具有"共识",能识别对方发来的数据.这就是约定.
协议即 -- 通信双方都可以认识的结构化的数据类型
因为协议是分层的,所以每层双方都有协议,同层之间,互相可以认识对方的协议.
网络传输基本原理
- 局域网传输基本原理
- 两台主机在一个局域网上,可以直接通信.
- 每台主机在局域网上,要有唯一的标识来保证主机的唯一性:mac地址
- mac地址用来识别数据链路层中相连的节点
- 长为48位,即6个字节,例如:08:00:27:03:fb:19
- 在出厂时就确定了,不能修改,mac地址通常是唯一的
- 在以太网(局域网中的一种类型)中任何时刻,只允许一台主机向网络中发送数据.
- 如果有多台主机同时发送数据,会发送数据干扰,我们成为数据碰撞,因此局域网也成为碰撞域.
- 所有发送数据的主机都要进行碰撞避免和碰撞检测.
- 没有交换机的情况下,一个以太网就是一个碰撞域.
- 在局域网通信过程中,主机对收到的报文确认是否是发送给自己的,通过mac地址确定.
- 以太网发送数据碰撞时,处理方案是:过一会再发送数据, 令牌环网的处理方案是: 谁拿令牌谁才可以向网络中发送数据.
- 局域网本质:一个临界资源.
同一个网段内的两台主机进行发信息的过程.
而其中每层都有协议(本质就是结构体),所以当我进行进行上述传输流程的时候,要进行封装和解包
- 报头部分,对应协议层的结构体字段,叫报头
- 处理报头,剩下的叫有效载荷.
- 故:报文 = 报头 + 有效载荷.
- 首部信息中包含首部多长,载荷多长,上层协议是什么等信息.
不同层的完成报文叫法:
- 传输层:段
- 网络层: 数据报
- 链路层:帧
数据的包装和分用:
**解包: 将报头和有效载荷分离 **— 双方协议相同, 互相认识都认识结构化报头,固定长度的报头
分用: 将自己的有效载荷交给上层哪一个协议?(在报头中有上层协议)
我们学习任何协议,都要在宏观是建立这样的认识:
- 要学习的协议,是如何做到解包的? 只有明确解包,封装也就可以理解
- 将自己的报头和有效载荷分离(解包) 2. 将自己的有效载荷交给上层协议(分用)
- 要学习的协议,是如何将自己的有效载荷,交付给上一层协议的?
跨网络传输流程图(唐僧的故事去西天取经)
认识IP地址:IP地址用来标识网络中不同的主机地址
数据报文中一般都有: 源IP地址和目的IP地址 源MAC地址 和 目的MAC地址
IP地址的意义:
- 为什么要去目标主机要先走路由器?
- 目的IP的意义? 路径规划的依据.
结合封装和解包,体现路由器解包和封装的特点
对比IP地址和MAC地址的区别?
- IP地址在整个路由过程中,一直不变
- Mac地址一直在改变
- 目的IP是一种长远目标,Mac地址是下一阶段的目标,目的IP是路径选择的重要依据,Mac地址是局域网转发的重要依据
IP网络的意义和网络通信的宏观流程
IP网络层存在的意义:提供网络虚拟层,让世界所有网络都是IP网络,屏蔽最底层网络差异.
Socket编程预备
- 理解源IP地址和目的IP地址
- IP在网络中,用来标识主机的唯一性.
思考数据传输到主机是目的吗?
不是,因为数据是给人用到,如:聊天是人在聊天,下载是人在下载,浏览网页是人在浏览.但是人是怎样看到聊天信息的呢?如恶化执行下载任务?如何浏览网页信息?通过启动进程qq 迅雷 浏览器.
而启动的进程是人的代表,进程是人在系统中的代表,只要将数据交给进程, 人就相当于拿到数据.
所以:数据传输到主机不是目的,而是手段,到达目标主机内部,再交给主机内的进程此时目的.
但是系统中,同时会存在多个进程,当数据到达目标主机之后,如何转发给进程?
- 端口号
端口号(port)是传输层协议的内容
- 端口号是一个2字节和16位整数.
- 端口号用来标识一个进程,告诉系统,当前这个数据要交给哪一个进程来处理
- IP地址 + 端口号 能够标识网络上某一台主机的某一个进程.
- 一个端口号可以被多个进程占用.
端口号的划分
- 0 - 1023: 知名端口号,HTTP, FTP, SSH 这些广为使用的引用层协议,他们的端口都是固定的.
- 1024 - 65535: 操作系统动态分配的端口号,客户端程序的端口号.
理解"端口号" 和 “进程ID”
-
pid表示唯一一个进程,此处端口号也唯一标识一个进程,二者之间的关系是什么样的?
-
一共进程可以绑定多个端口号,但是一个端口不能被多个进程绑定.
-
进程ID属于系统的概念, 技术上也是唯一性,确实可以用来标识唯一的一个进程,但是这样做,会让进程管理和网络强耦合,实际设计时并没有这样做.
理解源端口和目的端口号
- 传输层协议(TCP和UDP)的数据段中有两个端口号,分别叫做源端口号和目的端口号,就是在描述"数据是谁发的,发给谁";
理解socket
-
综上所述:IP地址用来标识互联网中唯一一台主机, 端口号用来标识主机中唯一一个网络进程.
-
IP + Port : 就可以表示互联网中的唯一一个网络进程.
-
所以,通信的时候,本质就是两个互联网那个进程之间的通信;{srcip,srcport,dstip,dstport} 这样的四元组就可以标识互联网中唯二的两个进程.
-
所以通信的本质:进程之间通信
-
我们将 ip + port 叫做套接字socket
传输层的代表
- 传输层属于内核,那么我们通过网络协议栈进程通信,必定调用的是传输层提供的系统调用,来进行网络通信.
认识TCP/UCP协议
TCP(Transmission Control Protocol 传输控制协议)
- 传输层
- 有连接
- 可靠传输
- 面向字节流
UDP(User Datagram Protocol 用户数据包协议)
- 传输层协议
- 无连接
- 不可靠传输
- 面向数据报
网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网络数据流同时有大端和小端之分,那么如何定义网络数据流的地址?
- 发送主机通常将发送缓冲区中的数据按内存地址从低到高地址的顺序发出.
- 接受主机把从网络上接到的字节依次保存在接受缓冲区中,也是按照内存地址从低到高的顺序保存.
- 因此,网络数据流的地址应该这样规定:先发出的数据是低地址,后发出的地址是高地址.
- TCP/IP协议规定,网络数据流采用大端字节序,即低地址高字节
- 无论主机是大端还是小端机,都会按照TCP/IP规定的网络字节序来发送和接受数据.
使用以下库函数做网络字节序和主机字节序1转换:
- h 表示host, n表示newwork, l 表示32位长整数(IP地址),s 表示16位短整数(端口号).
socket编程接口
socket常见API
//创建socket文件描述符(TCP/UDP, 客户端和服务端)
int socket(in domain, int type, int protocol)
//绑定端口(TCP/UDP, 服务端)
int bind(int socket, const struct sockaddr* address, socklen_t* address_len);
//开始监听socket(TCP, 服务端)
int listen(int socket, int backlog);
//接收请求(TCP,服务端)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
//建立连接(TCP,客户端)
int connect(int sockfd, const struct sockaddt* addr, socklen_t addr_len);
sockaddr结构体
socket API是一层抽象的网络接口,使用各种底层网络协议,如IPV4, IPv6,等,然而各种网络地址的格式并不相同.
-
IPv4 和 IPv6 的地址格式定义在
netinet/in.h
中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型,16位端口号,32位IP地址. -
IPv4 和 IPv6 地址类型分别定义位常数,AF_INET,AF_INET6,这样只要取得某种sockaddr结构体的首地址,不需要知道具体类型的sockaddr结构体,就可以根据地址类型字段确定结构体的内容.
-
socket API 可以都用struct sockaddr* 类型表示(类似与形参是void*),在使用时候,需要强制类型转换为sockaddr_in,这样的好处是程序的通用性,可以结构IPv4 IPV6等各种类型的sockaddr结构体指针作为参考.
sockaddr结构
sockaddr_in结构
虽然sock api结构是sockaddr,但是我们真正在基于IPv4编程时,使用的数据结构是sockaddr_in,这个结构主要有3个部分信息,地址类型,端口号,IP地址.
in_addr 结构
in_addr 用来表示一个IPv4的IP地址,其实就是一个32位整数.
(完结!!!)