【计算机网络】基本概念

下面的内容是是进行理解的重要部分,但是可能会有一些跳跃的感觉。

目录

初识协议

  • 协议:可以理解为约定。

就像我们约定明天一起出去玩。

但是这样的约定并不完善,比如我们乘坐的交通工具不同,你走路,他高铁,这样就互相错开而无法一起玩耍。

所以对于协议暂时的两个结论:

第一点:虽然只有一个协议,但是有不同的实现形式。

第二点:协议一定要完善。

那么指定协议的肯定都是一些很牛的组织,这些我们就不关心了

协议分层

在软件工程中,解耦是很重要的,而分层就是进行解耦的重要办法。

甚至在语言方面也提供了一些对应的功能:函数指针,继承,多态...

协议本质也是软件,在设计上为了更好的进行模块化,解耦合,也是被设计成为

层状结构的。

那么解耦具体有什么好处?

我们看下图,有两层协议(语言层与设备层)

可以看到虽然上层变化或者下层变化是不会影响整体的,所以让软件维护的

成本更低,出错也更好排查。

OSI七层模型

OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型,是一个逻辑上的定义和规范;

• 把网络从逻辑上分为了 7 层. 每一层都有相关、相对应的物理设备,比如路由器,交换机;

• OSI 七层模型是一种框架性的设计方法,其最主要的功能使就是帮助不同类型的主机实现数据传输;

• 它的最大优点是将服务、接口和协议这三个概念明确地区分开来,概念清楚,理论也比较完整. 通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯;

• 但是, 它既复杂又不实用; 所以我们按照 TCP/IP 四层模型来讲解.


注意:我们已经提到过,制定协议和实现的是两批人。

所以win,linux...操作系统可以互相通信是因为各自OS都有工程师实现这一份协议,但由于是同一分协议,所以即使OS不一样我们可以认为他们可以通信。

而在网络角度,OSI 定的协议 7 层模型其实非常完善,但是在实际操作的过程中,会话层、表示层是不可能接入到操作系统中的,所以在工程实践中,最终落地的是 5 层协议。

TCP/IP 五层(或四层)模型

至于为什么叫4层是因为我们这里不关心物理层,而是关心剩下的软件层。

TCP/IP 是一组协议的代名词,它还包括许多协议,组成了 TCP/IP 协议簇.

TCP/IP 通讯协议采用了 5 层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求.

• 物理层: 负责光/电信号的传递方式. 比如现在以太网通用的网线(双绞 线)、早期以太网采用的的同轴电缆(现在主要用于有线电视)、光纤, 现在的 wifi 无线网使用电磁波等都属于物理层的概念。物理层的能力决定了最大传输速率、传输距离、抗干扰性等. 集线器(Hub)工作在物理层.

• 数据链路层: 负责设备之间的数据帧的传送和识别. 例如网卡设备的驱动、帧同步(就是说从网线上检测到什么信号算作新帧的开始)、冲突检测(如果检测到冲突就自动重发)、数据差错校验等工作. 有以太网、令牌环网, 无线 LAN 等标准. 交换机(Switch)工作在数据链路层.

• 网络层: 负责地址管理和路由选择. 例如在 IP 协议中, 通过 IP 地址来标识一台主机, 并通过路由表的方式规划出两台主机之间的数据传输的线路(路由). 路由器(Router)工作在网路层.

• 传输层: 负责两台主机之间的数据传输. 如传输控制协议 (TCP), 能够确保数据可靠的从源主机发送到目标主机.

• 应用层: 负责应用程序间沟通,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等. 我们的网络编程主要就是针对应用层.

• 对于一台主机, 它的操作系统内核实现了从传输层到物理层的内容;

• 对于一台路由器, 它实现了从网络层到物理层;

• 对于一台交换机, 它实现了从数据链路层到物理层;

• 对于集线器, 它只实现了物理层;

但是并不绝对. 很多交换机也实现了网络层的转发; 很多路由器也实现了部分传输层的内容(比如端口转发);

所以现在我们就有一些基本概念的储备了:比如协议的理解,协议要分层,遵守TCP/IP协议,实现的不同...

再识协议

我们按照三段论来。

为什么要有 TCP/IP 协议?

我们仔细回想一下,其实在一个主机内部也是有协议的,比如磁盘与内存,否则介质不同的两种容器如何能相互配合?

只不过我们在上层感知不到。

但是在网络通信中,由于距离的变化,协议就比主机内的困难许多。

tcp/ip协议是什么?

是应对距离变长的解决方案。

怎么做?

这就是计算机网络的全部内容了。

TCP/IP 协议与操作系统的关系(宏观上,怎么实现的)

所以究竟什么是协议?

注意:OS 源代码一般都是用 C/C++语言写的。

我们现在可以再次理解一下什么是协议了。

观察下面图片。

问题:主机 B 能识别 data,并且准确提取 a=10,b=20,c=30 吗?

答案:是可以的!因为我们遵守同一个协议,你有的字段我都有。

所以也就理所当然的可以做到了。

关于协议的朴素理解:所谓协议,就是通信双方都认识的结构化的数据类型

因为协议栈是分层的,所以,每层都有双方都有协议,同层之间,互相可以认识对方的协议。

例子

当我们买快递时,会出现以下情况:

我们收到物品 + 对应的快递单,随后我们将快递单丢弃,取出我们买到的物品。

简单的一句话我们就可以得到一些结论:

快递单是快递公司制定的,我们都知道快递单各个字段含义。

发件人会填充快递单。

收件人根据快递单拿到属于自己的物品。

那么这与我们的协议有什么关系?

因为我们使用的是同一份协议,理所当然的接收方可以收到发送方的报文,根据协议报头拿到有效载荷。

网络传输基本流程

局域网网络传输流程图

局域网(以太网为例)通信原理

先说一个事实:两台主机在同一个局域网能够直接通信,原理类似上课。

就像是老师让张三回答问题,但只有张三站起来了,虽然同学们都收到了老师发的信息,但是由于目的地址不是其他同学,所以只有张三做出反应,其他同学不反应。张三回答问题只有老师做出反应,也是同理。

在这个例子映射到网络上:教室就是局域网;地址就是mac地址;老师与张三就是互相通信的两个主机,其他同学就是其他主机。

每台主机在局域网上,要有唯一的标识来保证主机的唯一性:mac 地址。

认识mac地址

• MAC 地址用来识别数据链路层中相连的节点;

• 长度为 48 位, 及 6 个字节. 一般用 16 进制数字加上冒号的形式来表示(例如:08:00:27:03:fb:19)

• 在网卡出厂时就确定了, 不能修改. mac 地址通常是唯一的(虚拟机中的 mac 地址不是真实的 mac 地址, 可能会冲突; 也有些网卡支持用户配置 mac 地址).

所以两台主机通信抽象出来是这样的:

再补充一些点:

• 以太网中,任何时刻,只允许一台机器向网络中发送数据

• 如果有多台同时发送,会发生数据干扰,我们称之为数据碰撞

• 所有发送数据的主机要进行碰撞检测和碰撞避免

可以发现二三点是实现第一点的。

我们甚至可以从系统角度去理解第一点:局域网是公共资源,任何时刻只能有一台主机访问!

其他的点:

• 没有交换机的情况下,一个以太网就是一个碰撞域

• 局域网通信的过程中,主机对收到的报文确认是否是发给自己的,是通过目标mac 地址判定

数据包封包和分用

但是注意我们已经说明上图是抽象出来的,因为并没有体现出协议栈的特点。

所以真正的局域网内通信是下图这样:

而其中每层都有协议,所以当我进行进行上述传输流程的时候,要进行封包和解包

注意到:逻辑上同层是直接沟通的,但是物理上要走协议栈,最终还是要落实在硬件上。那么为什么叫做栈?

因为我们每封包一个报头就是push一次,每解包一次就是一个pop。

可是解包时,每层都有那么多协议,怎么知道自己是要交付给那一层?

我们在封包时已经确定了(除了应用层,报头中都会标识上一个协议),所以就知道。这个过程叫分用。

下面我们明确一下概念

• 报头部分,就是对应协议层的结构体字段,我们一般叫做报头

现在可以总结一下了:

跨网络传输流程图

网络中的地址管理 - 认识 IP 地址

IP 协议有两个版本, IPv4 和 IPv6. 凡是提到 IP 协议, 没有特殊说明的,默认都是指 IPv4。

• IP 地址是在 IP 协议中, 用来标识网络中不同主机的地址;

• 对于 IPv4 来说, IP 地址是一个 4 字节, 32 位的整数;

• 我们通常也使用 "点分十进制" 的字符串表示 IP 地址, 例如 192.168.0.1 ; 用点分割的每一个数字表示一个字节, 范围是 0 - 255;

注意这里的IP地址使用点分十进制表示时(字符传串式)如果全部占满需要15字节(未包含\0),但是这样只是容易看,却不容易传输,因此需要以4字节的形式,因为1字节8比特位,可以标识0~255,所以4字节即可表示出IP地址。

跨网段的主机的数据传输. 数据从一台计算机到另一台计算机传输过程中要经过一个或多个路由器.

下面是一张示意图

这里就要简单说一下路由器:至少需要有两张网卡才可以进行路由。

更加具体的可以看下图:

IP我们这里是很难理解的,只能去感性认识。

注意:同一个局域网内的IP地址是类似的(可以看到左侧都是192... 右侧都是172...)

这里就有一个问题:为什么从链路层知道去路由器?明明还有其他主机的存在。

这是因为在网络层会干一件事:当前主机发现不是推给当前局域网内的,于是进行路由推到路由器。

在链路层封包上自己的mac地址与路由器的max地址。

然后推到路由器继续解包分用,再封包,最终到达目的地主机。

通俗来说:向下传输时,添加报头到网络层时,由于目的IP不在当前局域网,所以只能进行路由到路由器。

逻辑上是直接传输,但是物理上仍旧要走链路层封包 + 解包分用。

对比 IP 地址和 Mac 地址的区别

• IP 地址在整个路由过程中,一直不变(目前,我们只能这样说明,后面修正)

• Mac 地址一直在变

• 目的 IP 是一种长远目标,Mac 是下一阶段目标,目的 IP 是路径选择的重要依据,mac 地址是局域网转发的重要依据。

IP 网络层存在的意义:提供网络虚拟层,让世界的所有网络都是 IP 网络,屏蔽最底层网络的差异。

socket 编程接口

理解源 IP 地址和目的 IP 地址

IP 在网络中,用来标识主机的唯一性。

我们在网络层封包时需要标识自己的IP与目的地IP。

这里要思考一个问题:

数据传输到主机是目的吗?

不是的。因为数据是给人用的。比如:聊天是人在聊天,下载是人在下载,浏览网页是人在浏览。

但是人是怎么看到聊天信息的呢?怎么执行下载任务呢?怎么浏览网页信息呢?通过启动的 qq,迅雷,浏览器。

而启动的 qq,迅雷,浏览器都是进程。换句话说,进程是人在系统中的代表,只要把数据给进程,人就相当于就拿到了数据。

所以:数据传输到主机不是目的,而是手段。到达主机内部,在交给主机内的进程,才是目的。

但是系统中,同时会存在非常多的进程,当数据到达目标主机之后,怎么转发给目标进程?这就要在网络的背景下,在系统中,标识主机的唯一性。

认识端口号

我们并没有通过pid标识,而是端口号:端口号(port)是传输层协议的内容.

• 端口号是一个 2 字节 16 位的整数;

• 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;

• IP 地址 + 端口号能够标识网络上的某一台主机的某一个进程;

• 一个端口号只能被一个进程占用.

端口号范围划分

• 0 - 1023: 知名端口号, HTTP, FTP, SSH 等这些广为使用的应用层协议, 他们的端口号都是固定的.

• 1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的.

理解 "端口号" 和 "进程 ID"

我们之前在学习系统编程的时候, 学习了 pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程.

另外, 一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定;

程 ID 属于系统概念,技术上也具有唯一性,确实可以用来标识唯一的一个进程,但是这样做,会让系统进程管理和网络强耦合,实际设计的时候,并没有选择这样做。

理解源端口号和目的端口号

传输层协议(TCP 和 UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号.就是在描述 "数据是谁发的, 要发给谁";

理解 socket

• 综上,IP 地址用来标识互联网中唯一的一台主机,port 用来标识该主机上唯一的一个网络进程

• IP+Port 就能表示互联网中唯一的一个进程

• 所以,通信的时候,本质是两个互联网进程代表人来进行通信{srcIp,srcPort,dstIp,dstPort}这样的 4 元组就能标识互联网中唯二的两个进程

• 所以,网络通信的本质,也是进程间通信

• 我们把 ip+port 叫做套接字 socket

总结一下:

请求时一定要有{srcIp,srcPort,dstIp,dstPort}

服务端要和一个port关联。

没有采用pid而是端口号是因为pid是系统级别,而通信是网络级别,这样就造成了强耦合。

另外,进程都有pid,但是不是所有的进程都要通信,只有有port的才需要进行。这也区分了这两点。

传输层的典型代表

如果我们了解了系统,也了解了网络协议栈,我们就会清楚,传输层是属于内核的,那么我们要通过网络协议栈进行通信,必定调用的是传输层提供的系统调用,来进行的网络通信。

认识 TCP 协议

此处我们先对 TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识;

后面我们再详细讨论 TCP 的一些细节问题.

• 传输层协议

• 有连接

• 可靠传输

• 面向字节流

认识 UDP 协议

此处我们也是对 UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识; 后面再详细讨论.

• 传输层协议

• 无连接

• 不可靠传输

• 面向数据报

网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

• 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;

• 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;

• 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.

• TCP/IP 协议规定,网络数据流应采用大端字节序,即低地址高字节.

• 不管这台主机是大端机还是小端机, 都会按照这个 TCP/IP 规定的网络字节序来发送/接收数据;

• 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

为使网络程序具有可移植性,使同样的 C 代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

• 这些函数名很好记,h 表示 host,n 表示 network,l 表示 32 位长整数,s 表示 16 位短整数。

• 例如 htonl 表示将 32 位的长整数从主机字节序转换为网络字节序,例如将 IP 地址转换后准备发送。

• 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;

• 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

socket 编程接口

socket 常见 API

cpp 复制代码
C
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int 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 sockaddr *addr,
socklen_t addrlen);

sockaddr 结构

socket API 是一层抽象的网络编程接口,适用于各种底层网络协议,如 IPv4、IPv6,以及后面要讲的 UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同.

• 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 结构

in_addr 用来表示一个 IPv4 的 IP 地址. 其实就是一个 32 位的整数;

相关推荐
网络安全-杰克7 分钟前
网络安全概论
网络·web安全·php
不是二师兄的八戒9 分钟前
本地 PHP 和 Java 开发环境 Docker 化与配置开机自启
java·docker·php
小牛itbull9 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
怀澈12211 分钟前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
请叫我欧皇i18 分钟前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
闲暇部落21 分钟前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
GIS瞧葩菜30 分钟前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript
chnming198735 分钟前
STL关联式容器之set
开发语言·c++
带多刺的玫瑰38 分钟前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
威桑1 小时前
MinGW 与 MSVC 的区别与联系及相关特性分析
c++·mingw·msvc