Linux初识网络
计算机网络背景
网络发展
独立模式: 计算机之间相互独立;
网络互联: 多台计算机连接在一起, 完成数据共享;
局域网LAN: 计算机数量更多了, 通过交换机和路由器连接在一起;
广域网WAN: 将远隔千里的计算机都连在一起;
所谓 "局域网" 和 "广域网" 只是一个相对的概念. 比如, 我们有 "天朝特色" 的广域网, 也可以看做一个比较大的局域网.
认识 "协议"
协议本质是一种约定,通过双方规定的方式减少沟通成本
计算机生产厂商有很多; 计算机操作系统, 也有很多; 计算机网络硬件设备, 还是有很多;
让这些不同厂商之间生产的计算机能够相互顺畅的通信就需要约定一个共同的标准, 大家都来遵守, 这就是网络协议;
协议最后的表现形式是通信双方都认识的结构体对象
发送的数据中多发的就是协议
网络协议初识
协议分层
分层:高内聚低耦合降低维护成本
分层最大的好处在于 "封装" . 面向对象例子
OSI七层模型 
OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型, 是一个逻辑上的定义和规范;
把网络从逻辑上分为了7层. 每一层都有相关、相对应的物理设备,比如路由器,交换机; OSI 七层模型是一种框架性的设计方法,其最主要的功能使就是帮助不同类型的主机实现数据传输;
它的最大优点是将服务、接口和协议这三个概念明确地区分开来,概念清楚,理论也比较完整. 通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯; 但是, 它既复杂又不实用;
在实际操作的过程中,会话层、表示层是不可能接入到操作系统中的,所以在工程实践中,最终落地的是 5 层协议。
物理层我们考虑的比较少. 因此很多时候也可以称为 TCP/IP四层模型. 所以我们按照TCP/IP四层模型来讲解.
TCP/IP五层(或四层)模型
TCP/IP是一组协议的代名词,它还包括许多协议,组成了TCP/IP协议簇. TCP/IP通讯协议采用了5层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求.
物理层: 负责光/电信号的传递方式. 比如现在以太网通用的网线(双绞 线)、早期以太网采用的的同轴电缆 (现在主要用于有线电视)、光纤, 现在的wifi无线网使用电磁波等都属于物理层的概念。物理层的能力决定了最大传输速率、传输距离、抗干扰性等. 集线器(Hub)工作在物理层.
数据链路层: 负责设备之间的数据帧的传送和识别. 例如网卡设备的驱动、帧同步(就是说从网线上检测 到什么信号算作新帧的开始)、冲突检测(如果检测到冲突就自动重发)、数据差错校验等工作. 有以太 网、令牌环网, 无线LAN等标准. 交换机(Switch)工作在数据链路层.
网络层: 负责地址管理和路由选择. 例如在IP协议中, 通过IP地址来标识一台主机, 并通过路由表的方式规 划出两台主机之间的数据传输的线路(路由). 路由器(Router)工作在网路层.
传输层: 负责两台主机之间的数据传输. 如传输控制协议 (TCP), 能够确保数据可靠的从源主机发送到目标 主机.
应用层: 负责应用程序间沟通,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问 协议(Telnet)等. 我们的网络编程主要就是针对应用层.
一般而言
对于一台主机, 它的操作系统内核实现了从传输层到物理层的内容;
对于一台路由器, 它实现了从网络层到物理层;
对于一台交换机, 它实现了从数据链路层到物理层; 对于集线器, 它只实现了物理层;
但是并不绝对. 很多交换机也实现了网络层的转发; 很多路由器也实现了部分传输层的内容(比如端口转发);
网卡是文件,往文件里写就是往网卡里写就是向网络里写
数据链路层在各种软件驱动中执行(网卡驱动的一部分)
网络协议栈和OS有什么关系
用户要访问网卡必须通过操作系统(访问硬件)但是用户不能访问操作系统内部,必须要操作系统提供他的调用接口
用户程序通过操作系统提供的API(如socket函数)来请求网络通信服务,而具体的通信实现由操作系统内核中的协议栈完成。
网络通信的本质就是贯穿协议栈的过程
网络传输基本流程
网络传输流程图
同一个网段内的两台主机进行文件传输.
而跨网段的主机的文件传输. 数据从一台计算机到另一台计算机传输过程中要经过一个或多个路由器.
数据包封装和分用
不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数据报 (datagram),在链路层叫做帧(frame). 应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装 (Encapsulation).
首部信息中包含了一些类似于首部有多长, 载荷(payload)有多长, 上层协议是什么等信息. 数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部, 根据首部中的 "上层协议 字段" 将数据交给对应的上层协议处理.
数据封装分用过程
报头给应用层,消息(有效载荷)给用户
序号保证数据顺序,网络层报头告诉那两个主机交流
冯诺依曼体系规定,外设拿到信息交给CPU必须先给内存
网络中的地址管理
mac地址保证局域网唯一性
IP地址保证全网唯一性
认识IP地址
IP协议有两个版本, IPv4和IPv6. 凡是提到IP协议, 没有特殊说明的, 默认都是指IPv4
IP地址是在IP协议中, 用来标识网络中不同主机的地址; 对于IPv4来说, IP地址是一个4字节, 32位的整数; 我们通常也使用 "点分十进制" 的字符串表示IP地址, 例如 192.168.0.1 ; 用点分割的每一个数字表示一个 字节, 范围是 0 - 255;
在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址.
认识MAC地址
MAC地址用来识别数据链路层中相连的节点; 长度为48位, 及6个字节. 一般用16进制数字加上冒号的形式来表示(例如: 08:00:27:03:fb:19) 在网卡出厂时就确定了, 不能修改. mac地址通常是唯一的(虚拟机中的mac地址不是真实的mac地址, 可 能会冲突; 也有些网卡支持用户配置mac地址).
局域网中所有消息在硬件上都会收到,但是会检查报文,目标地址(mac地址对比)的主机才能到用户层(报文软件),数据链路层实现,一旦判断报文不是他的就直接丢弃,上层不知道接收到过
局域网看做多台主机的共享资源
交换机隔离碰撞域,通过交换机(或类似二层设备)才能实现跨碰撞域的MAC地址通信,因为交换机是唯一能隔离和转发不同碰撞域间数据的设备。
网络编程套接字
我们光有IP地址还不可以完成通信要有IP地址、端口号和协议类型
IP地址 ≈ 找到正确的房子
端口号 ≈ 敲对具体的门(哪个程序)
协议类型 ≈ 用对方听懂的语言交流
三者缺一不可,这正是OSI七层模型/ TCP/IP五层模型的精妙之处。
端口号
进程有很多但网络协议栈在每个操作系统内只有一套(公共资源)要有端口号能被应用层绑定并被传输层识别,这样传输层就能把数据交给进程
认识端口号
端口号(port)是传输层协议的内容.
端口号是一个2字节16位的整数; 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
IP地址 + 端口号能够标识网络上的某一台主机的某一个进程; 一个端口号只能被一个进程占用.一个进程可以绑定多个端口号
理解 "端口号" 和 "进程ID"
我们之前在学习系统编程的时候, 学习了 pid 表示唯一一个进程;
此处我们的端口号也是唯一表示一个进程. 那么这两者之间是怎样的关系
我们在技术上也可以用pid来标识进行通信,这样是为了解耦
PID(进程ID) | 端口号(Port) | |
---|---|---|
作用层级 | 操作系统进程管理 | 网络通信的传输层 |
唯一性 | 全系统唯一 | 同一主机+同一协议+同一时刻唯一 |
生命周期 | 进程创建时分配,退出时释放 | 进程绑定端口时占用,释放后可用 |
类比10086 | 客服人员的工号(如:工号9527) | 热线电话号码(10086) |
一个端口号可被多个进程轮流使用(通过accept()
创建新连接)
一个进程可同时处理多个端口
另外, 一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定; (多个进程绑定一个端口号不知道交付给哪个进程(哈希))
理解源端口号和目的端口号
成功通信 = 源端口(临时ID) + 目的端口(服务ID) + 传输协议(TCP/UDP快递公司)
源端口号 = 发送方门牌号(谁寄的)
目的端口号 = 接收方门牌号(寄给谁)
假设取经团队是数据包,大唐长安和天竺是主机:
角色 | 网络通信对应物 | 端口号作用 |
---|---|---|
唐僧 | 数据段(TCP/UDP) | 携带源/目的信息 |
通关文牒 | 传输层头部 | 写明"从哪来+到哪去" |
大唐驿站 | 源端口号(随机分配) | 朝廷回信时知道送回哪个驿站 |
天竺佛经库 | 目的端口号(固定) | 如来知道把经书交给谁(HTTP=80) |
通信过程:
唐僧(数据)从大唐驿站3000号(源端口)出发
目标明确送到天竺佛经库80号(目的端口)
如来(服务端)拿到经书后,回信地址写驿站3000号
TCP(全双工)
传输层协议 有连接 可靠传输 面向字节流
没人访问服务器就一直等待(被动的),不能退出,必须随时随地应对客户端的请求
listen:将套接字设置为监听状态
netstat -nltp
n能显示成数字的就显示成数字
l:listen状态
t:tcp
p:显示对应的进程
tcp面向链接,在进行通信之前要把链接建立起来
accept:成功返回整数的文件描述符,失败-1
UDP(用户数据报协议)
传输层协议 无连接 不可靠传输 面向数据报
网络字节序
内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏 移地址也有大端小端之分, 网络数据流同样有大端小端之分
如何定义网络数据流的地址:
发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;
转换函数
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络 字节序和主机字节序的转换。
这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回 ;
如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。
socket编程接口
socket 常见API
domain:创建套接字的域(网络通信还是本地协议)(AF_INET:IPv4)
type: 定义出的套接字类型
protocol:协议类型
返回值:依旧是文件,创建套接字的本质是创建一个文件,指向底层的网卡设备
sockaddr结构(类似于多态)
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同.
前2字节的作用
• sa_family 是所有结构体的第一个字段,用于区分类型:
◦ AF_INET(IPv4):值为 2
◦ AF_UNIX(本地):值为 1
◦ AF_INET6(IPv6):值为 10
• 系统调用(如 bind()、connect())通过该字段判断如何解析后续数据。后续字节的差异
结构体 第3字节起的内容 sockaddr_in
端口号(2字节) + IPv4地址(4字节) sockaddr_un
文件路径(最多108字节) sockaddr
未定义具体格式(需转换后使用) IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16 位端口号和32位IP地址.
IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址, 不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好 处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;
sockaddr 结构
sockaddr_in 结构
虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主 要有三部分信息: 地址类型, 端口号, IP地址.
in_addr结构