0%

双十一豪豪酱买了群晖DS220j家用NAS服务器,为了方便工作文件的备份传输,让电脑(PC、Mac)能用上远程网络驱动,就需要让服务器穿透内网,并申请一个独立域名,想着就用腾讯云和家里的小米路由器AX3600来干吧。下方是对硬件上需要的一些要求:

光猫:使用 桥接模式,具有 独立公网IP

路由器:使用 PPPoE模式 进行拨号、支持 端口映射端口转发DMZ

群晖NAS:支持 DDNSUPnP 协议、支持 证书使用范围管理

前期准备工作

更改工作模式

光猫路由器 都支持 路由模式桥接模式,都支持多个无线接入和有线接入,但因为自己购买的路由器具有更多不一样的功能,例如支持UPnP等,而且光猫是连接物理光纤进行上网的,而自己购买的路由器不能直接连接光纤上网,还得依赖于光猫,所以可以认为是两种不一样的工作硬件。

如果光猫已经工作在桥接模式(通过查看路由器后台的 上网方式 是否为PPPoE,如果是则说明自己购买的路由器工作在路由模式,电信光猫工作在桥接模式)。一般没有此类服务器需求的家庭网络,都是让光猫工作在路由拨号模式(即路由模式)的,而路由器则工作在桥接模式,此情况则需要将两个硬件设备的工作模式进行互换,参考以下步骤:

  1. 打电话给电信营业厅 (0000)10000 (前方的 (0000) 是自己的市区号),确认一下光猫是路由模式还是桥接模式,如果是路由模式,则将其改成桥接模式。
  2. 确认改成桥接模式后,询问宽带的账户和密码,在路由器后台输入账户密码并连接测试,确保路由器工作在拨号模式,而光猫工作在桥接模式。

目前国内家庭宽带都是默认内网IP,想要公网IP就只能向运营商申请了,这并不需要额外的收费。电信手上的IP资源最多,也最大方。我也是照着别人的说法,打电话给电信客服,说装监控需要公网IP,客服登记之后,过几个小时就打电话说已经换好了,干净利落,服务好。目前好像还没见到过电信要不到公网IP的情况。

——《BT下载教程 篇一:BT下载大提速! 获取公网IP和端口映射转发简单教程

image-20211113000614809

多重NAT改成公网IP

同样是打给电信,然后申请公网IP,电信客服专员一般会在一个小时内处理完毕,然后就可以得到公网IP了。

“谁拨号,谁就有公网IP” ——即此时如果路由器已经成功设置了路由模式(使用PPPoE拨号上网),则可以在路由器后台查看到其公网IP地址,如:

image-20211113001927548

DDNS域名绑定

虽然已经获得公网IP,但是该IP还是会随机变动,还会需要使用DDNS服务将公网IP映射到对应域名上,而群辉服务器则提供此功能,此处需要自己申请一个域名(此步骤省略)。进入群辉服务器后台,开启DDNS功能,将域名和随机公网IP绑定和更新。

另外,需要自己上所用的服务器上开启API Token,并提供给群辉DDNS服务(类似于授权,可以由DDNS服务自己检测家庭网络的公网IP,并自动绑定最新的公网IP)。

API Token申请

下方以腾讯云的DNSPOD为例,点击DNSPOD中的 API密钥,进行 新建密钥 即可,将对应的 SecretIdSecretKey 复制下来。

image-20211113002208036
image-20211113001142315

DDNS添加

将获取到的 SecretId 填写到 用户名/电子邮件 中, 将 SecretKey 填写到 密码/密钥 中。

image-20211113001623814

经过前面的步骤,可以看到群晖NAS服务器自动测试的 DDNS状态正常 即可。

image-20211112175853513

端口映射和转发

端口映射:Port Mapping, 即将内网中的主机的一个端口映射到外网主机的一个端口,以提供相应的服务。当用户访问外网IP的这个端口时,服务器自动将请求映射到对应局域网内部的机器上。

端口转发:Port Forwarding, 即将外网对应 EA 端口上收到的数据转发到内网某个主机对应的某个 IA 端口上,或者反过来将内网某个主机 IB 端口的数据转发到对应外网的 EB端口 上。

UPnP协议:Universal Plug and Play,即 即插即用。主要是微软在推行的一个标准,适用于家庭网络,用于设备间的发现和连接。UPnP 最大的愿景就是希望实现任何设备只要一接入网络就能被网络中的所有其它设备发现,做到完全的即插即用。UPnP是一个多层协议构成的框架体系,每一层都以相邻的下层为基础,同时又是相邻上层的基础。直至达到应用层为止。

手动端口转发或自动端口转发

手动端口转发:

自动端口转发:

image-20211112180357037
image-20211112175632036
image-20211112180450097

配置DMZ

通过配置DMZ,我们可以将需要保护的Web应用程序服务器和数据库系统放在内网中,把没有包含敏感数据、担当代理数据访问职责的主机放置于DMZ中,这样就为应用系统安全提供了保障。

DMZ使包含重要数据的内部系统免于直接暴露给外部网络而受到攻击,攻击者即使初步入侵成功,还要面临DMZ设置的新的障碍。

image-20211112172411026

DMZ开启前

image-20211112172343953

DMZ开启后

虽然也是随机公网IP,但是可以使用DMZ将连接到路由器的设备映射到外网上。

image-20211112175925384

安全证书设置

设置安全证书及其使用范围,不要使用自我签名的证书,所有浏览器都会识别和阻止你访问的

经过前面DDNS的 API token 配置之后,群晖NAS能够获取到对应域名的免费证书,并提供页面进行管理,将所有使用到的应用安全验证证书都设置为对应云服务提供商提供的证书即可。

image-20211112180133874

后台服务器访问

经过上面复杂的折腾之后,就可以使用自定义的域名进行后台登录和管理了。

输入 https://nax.xxx.xxx:5000 即可进行后台服务器的访问,也可以使用 ping 进行测试,如下:

image-20211113002928045

远程驱动器设置

  1. 在文件管理器中右键点击 此电脑 的属性
  2. 然后点击 映射网络驱动器
image-20211112174837538
  1. 选择本地的 虚拟驱动号 进行设置
  2. 输入 网络地址端口号,格式如 https://nas.xxx.xxx:5006
image-20211112175022321
  1. 按照提示输入群晖提供的账户名和密码即可进行连接
image-20211112174513370
  1. 连接完毕即可查看到对应的网络驱动器
image-20211112174559989

参考

  1. 【干货】什么是端口转发?什么是端口映射?如何设置端口映射?
  2. DMZ
  3. upnp协议简介(一)

本笔记不是全面的C++学习笔记,而是基于个人在原有的C语言基础和浅薄的C++的开发经验上的补足笔记。

本笔记仅供个人学习记录,不提供完整学习指导,如有需要请自行阅读《C++ Primer Plus》。

本文会参考 GNU C++ 进行代码调试,部分文档内容会参考 MS C++ 进行理解。

简介

C++融合了三种不同的编程方式:

  • 面向过程编程(继承于C)
  • 面向对象编程
  • 泛型编程(Generic Programming),即C++模板(Template)

过程 VS 对象

面向过程编程(Process Oriented Programming),即 结构化编程过程结构化编程,是指根据执行的操作来构思一个程序。程序任务需要解决的问题按照 “1、2、3、4” 这样的顺序一一编写,如果程序任务过大,则将较大的任务拆解成较小的可以理清结构逻辑的小任务去完成。C语言的编程开发思路就是使用程序模块(函数)来表示各个任务模块。

面向对象编程(Object Oriented Programming),简称 OOP。

结构化编程在程序逻辑的清晰度和可靠性上占据优势,但是在代码量上和面向对象相比不占优势

为了应付代码量的挑战,与强调算法的过程性编程不同,OOP强调数据。过程性编程试图使问题满足语言要求,OOP则是使语言满足问题的要求。

(Class) 是规定了 数据操作数据类型(Data Type)。类所创建的实体称为对象。

OOP 强调的是编程的数据方面,GP编程强调的是 独立于特定数据类型,即强调 通用非特定 类型。

image-20211027135117490

系统的位数

  • 系统的位数是由什么决定的?包括嵌入式系统的8位、16位、32位和通用系统的32位和64位?

位数 是由CPU的最大寻址空间决定的。

New Features in C++

以下主要从C++和C的区别点进行记录,是个人之前从未掌握过的知识,另外也会记录C++的一些高级用法。

Statement

  • C与C++的语句申明位置有哪些差异?

C语言的变量声明通常都应位于函数或过程开始位置(Pascal也是),而C++并没有该限制,因此这点可以说是C++的优点也可以是缺点。

Range-based for

C++11 引入了一种崭新的 for() 循环形式,可以逐一迭代某个给定的 区间数组集合(range, array or collection) 内的每一个元素。而其他语言可能称之为 foreach 循环,其一般性用法如下:

1
2
3
for(decl:coll){
statement
}

其中:

  • decl 是给定之 coll 集合中的每个元素的声明。
  • statement 会针对给定之 decl 进行执行

使用样例(下左)及其输出结果(下右)如:

image-20211030084723644

而且上面这种是基于 C-style array 的新旧语法搭配的 C++,实际上如果 coll 集合提供成员函数 begin()end() ,那么使用 for(decl:coll) 时便等同于使用 for(auto _pos=coll.begin(), _end=coll.end(); _pos!=_end;++_pos)

Data Type

  • 数据类型的大小由什么决定?数据类型大小(即占空内存空间)与系统位数、编译器的关联性?
  • 嵌入式系统与通用系统的数据类型大小差异在哪?
  • double 和 float 在内存上的差异?

在 C++ 中, 变量(Variable) 一词通常用于 引用 标量数据类型 的 实例,而 其他类型(class / struct)的实例通常称为 对象(Object)。

C++ 是 强类型语言 ,也是 静态类型

在代码中声明变量时,必须 显式指定其类型,或使用 关键字指示编译器从初始值表达式 auto 推断类型。 在代码中声明函数时,必须指定每个参数的类型及其返回值;如果函数未返回任何 void 值,则必须指定 。当使用允许任意类型参数的函数模板时例外。

基本(内置)类型

基本数据类型,简称 基本类型基类型

下图显示了 Microsoft C++ 实现中的内置类型的相对大小:

多个内置类型的相对大小(以字节为单位)的关系图。

下表列出了最常用的基本类型及其在 Microsoft C++ 实现中的大小:

类型 大小 评论
int 4 个字节 整数值的默认选择。
double 8 个字节 浮点值的默认选择。
bool 1 个字节 表示可为 true 或 false 的值。
char 1 个字节 用于早期 C 样式字符串或 std:: 字符串对象中无需转换为 UNICODE 的 ASCII 字符。
wchar_t 2 个字节 表示可能以 UNICODE 格式进行编码的“宽”字符值(Windows 上为 UTF-16,其他操作系统上可能不同)。 这是用于 std::wstring 类型字符串的字符类型。
unsigned char 1 个字节 C++ 没有内置字节类型。 使用 unsigned char 表示字节值。
unsigned int 4 个字节 位标志的默认选项。
long long 8 个字节 表示非常大的整数值。

类型数据的宽度(Width),即占用内存大小是由 计算机字长编译程序 决定的。计算机字长提供了硬件计算精度的支持,编译程序则提供了源程序到机器码的转换。

下方为C/C++ 在通用系统或嵌入式系统中的常见数据类型的大小,所有值的单位均为 bit

BASIC DATA TYPE 8 bit MCU 16bit MCU (51) 32bit (x86) 32bit (TI F28004x)
CLA ON
32bit (TI F28004x)
CLA OFF
64bit (x86)
in C++ (g++)
64bit (x86)
in C (gcc)
char 8 8 8 8 8 8
short 16 16 16 8 16 16
int 16 32 32 16 32 32
long 32 32 32 16 64 64
long long / 64 64 32 64 64
float 32 32 32 32 32 32
double 32 64 16 64 64
long double / 32 128 128

下方内容转自网络,待验证。

(x86平台下)long intlong,给人的感觉好像是长整型,但实际上,它和 int 一样,只有32位。cppreference 给出的定义如下,但在实际的使用中,longint 几乎没有区别。

int - basic integer type. The keyword int may be omitted if any of the modifiers listed below are used. If no length modifiers are present, it’s guaranteed to have a width of at least 16 bits. However, on 32/64 bit systems it is almost exclusively guaranteed to have width of at least 32 bits. long - target type will have width of at least 32 bits.

实际上经过测试:在 x86_64 平台下,C 和 C++ 中的 long 长度都是要比 int 更长的,即 int 默认为 32bit 宽度,long64bit 宽度。

image-20211028101248341

Embedded System

嵌入式C语言中常用的数据类型如下图所示,而不同的

image-20211027155153218

double / float

IEEE二进制浮点数算术标准IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的“浮点数运算符”;它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。

IEEE 754规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80比特实做)。只有32位模式有强制要求,其他都是选择性的。大部分编程语言都有提供IEEE浮点数格式与算术,但有些将其列为非必需的。例如,IEEE 754问世之前就有的C语言,现在有包括IEEE算术,但不算作强制要求(C语言的float通常是指IEEE单精确度,而double是指双精确度)。

该标准的全称为IEEE二进制浮点数算术标准(ANSI/IEEE Std 754-1985),又称IEC 60559:1989,微处理器系统的二进制浮点数算术(本来的编号是IEC 559:1989)。后来还有“与基数无关的浮点数”的“IEEE 854-1987标准”,有规定基数为2跟10的状况。现在最新标准是“IEEE 854-2008标准”。

Variable

C++ 与 ANSI C(C99标准)不同之处,在于后者只保证名称中的前63个字符有意义,即使第64个字符不同,但是只要前63个字符相同的变量则被认为是相同的。

在某些情况下,其他程序员会使用的变量命名风格及其意义:

  • strsz 前缀:表示以空字符结束的字符串
  • b 前缀:表示布尔值
  • p 前缀:表示指针
  • c 前缀:表示单个字符

Class

:(用户定义/标准的)数据类型规范,详细描述了如何表示信息以及对数据执行的操作。

对象:是根据类规范创建的实体(好似之于变量和基本数据类型)。

就像函数可以来自函数库一样,类也可以来自 类库。从技术上说,大部分类库都没有被内置到C++语言中,而是语言标准指定的类。类定义 位于类库文件中,并没有被内置到编译器里(有需要可以修改,虽然不建议)。

C++之所以如此有吸引力,很大程度上是由于存在大量支持UNIX、Macintosh 和 Windows 编程的类库。

类描述 指定了可以对类对象执行的所有操作。要对特定对象执行这些允许的操作有两种方法,一是直接使用类方法(本质即函数调用),二是重定义运算符(如 cincout 使用的 >><<

Function

被调用的函数称为 被调用函数(called function),包含函数调用的函数称为 调用函数(calling function)。

函数原型:即 函数声明,函数原型或声明只描述函数接口,指仅有函数头和分号 ; 而没有函数体的语句。函数原型定义了需要传递给函数的参数以及函数本身需要返回的值的类型。

库文件:存放了函数编译代码的文件。

头文件:包含了函数原型的文件。

image-20211028085847535

在有些语言中,有返回值的函数被称为 函数(function),无返回值的函数被称为 过程(procedure) 或 子程序(subroutine)。

但在C/C++中,都被称为 函数

Main Function

  • 有什么有些IDE中的main函数的括号里是带参数的?

是否在 主函数main() 参数括号 () 中使用关键字 void ,在C++和C中有明显的区别:

  • 在C++中,让括号空着和在括号中写 void 等效。
  • 而在C中,让括号空着意味着对是否接受参数保持沉默。

int main(int argc, const char * argv[]) 是UNIX和linux中的标准写法。int main() 只是默许的用法。

使用main函数时经常都是不带参数的,因此main 后的括号都是空括号。实际上,main函数可以带参数,这个参数可以认为是 main函数的 形式参数

C语言规定

  1. main函数的参数只能有两个,习惯上这两个参数写为 argcargv。因此,main函数的函数头可写为:main (argc,argv)
  2. argc (第一个形参)必须是 整型变量argv (第二个形参)必须是指向字符串的 指针数组

由于main函数不能被其它函数调用,因此不可能在程序内部取得实际值。那么,在何处把实参值赋予main函数的形参呢?

实际上,main函数的参数值是从 操作系统命令行/Terminal 上获得的。当我们要运行一个可执行文件时,输入空格,输入文件名,再输入实际参数即可把这些实参传送到main的形参中去。

argc 的数值会随着通过命令行传递给可执行文件的参数的增加而增加,即 argc 其实是代表着 参数的数量

image-20211027144512641

编译指令

using 是编译指令的关键字,

参考

  1. C++ Primer Plus
  2. int main(int argc, const char * argv[])
  3. C++ 类型系统
  4. IEEE二进制浮点数算术标准(IEEE 754)

WSL

在windows的 linux子系统下安装的具有图形界面的软件都可以在windows上打开,但是如果直接在MS商店安装的 kali linux 子系统是最精简版本,很多开发者需要用的软件(如 gcc / make / vim 等)都不存在,需要使用官方提供的完整安装命令去安装一些必须程序。

按照官方指示安装完必要程序后,可以在文件管理器里看到 linux 子系统的根目录,一定程度上很方便windows的用户使用linux系统并用图形化界面管理其文件。

image-20211027103100831

百度云盘安装

百度云盘是个人经常使用到的工具,在linux系统上也会安装,但wsl系统的话,多少可能会因为某些依赖文件缺失导致安装失败。

缺失的文件可以通过先更新源库(sudo apt update)再进行安装的方式解决,但有些时候可能是源库的问题,导致该依赖的包不存在(如下)。

image-20211027104129827

因此需要通过手动下载(利用 wget )该依赖包,然后自行安装(利用 sudo dpkg -i )的方式来解决,其中百度云盘缺失的依赖文件及其包连接如下:

  • libappindicator3-1:http://mirrors.ustc.edu.cn/debian/pool/main/liba/libappindicator/libappindicator3-1_0.4.92-3.1_amd64.deb
  • libindicator3-7:http://mirrors.ustc.edu.cn/debian/pool/main/libi/libindicator/libindicator3-7_0.5.0-2_amd64.deb
  • libdbusmenu-glib4:http://ftp.br.debian.org/debian/pool/main/libd/libdbusmenu/libdbusmenu-glib4_18.10.20180917~bzr490+repack1-1_amd64.deb
  • libdbusmenu-gtk3-4:http://ftp.br.debian.org/debian/pool/main/libd/libdbusmenu/libdbusmenu-gtk3-4_18.10.20180917~bzr490+repack1-1_amd64.deb

安装完毕之后即可正常进行软件使用了,虽然 wsl 的界面还是差强人意了些,哈哈。

image-20211027104032244

Traceroute(路由追踪)的原理及实现

traceroute 和 tracert 是Linux和Windows平台下用于追踪网络设备距离远近的工具,向目标设备发包,从 TTL=1 开始向外发包,逐渐增加 TTL 的值,直到目标主机。在介绍traceroute和tracert的原理之前,需要了解几个技术名词:

IP,协议是TCP/IP协议族中最核心的部分,它的作用是在两台主机之间传输数据,所有上层协议的数据(HTTP、TCP、UDP等)都会被封装在一个个的IP数据包中被发送到网络上。

ICMP,即 Internet Control Message Protocol,互联网控制报文协议,它常用于传递错误信息,ICMP协议是IP层的一部分,它的报文也是通过IP数据包来传输的。

TTL,即 time-to-live,是IP数据包中的一个字段,它指定了数据包最多能经过几次路由器。从我们源主机发出去的数据包在到达目的主机的路上要经过许多个路由器的转发,在发送数据包的时候源主机会设置一个TTL的值,每经过一个路由器TTL就会被减去一,当TTL为0的时候该数据包会被直接丢弃(不再继续转发),并发送一个超时ICMP报文给源主机。

实现方案

tracert (windows)只支持基于 ICMP 报文发送,而 traceroute(Linux/Mac) 支持多种报文协议(UDP、ICMP、TCP)的发送,但不带任何选项(Options)时默认使用的是UDP。(具体参数和选项自行请自行 man 以查看手册)

下方所示图片分别为 tracert (windows)的帮助手册,以及在traceroute(Linux)上分别使用三种协议追踪 baidu.com 的结果截图。后续仅对UDP及ICMP追踪做较为详细的说明。

tracert的帮助说明

使用 TCP 报文的 traceroute:

基于TCP报文的traceroute

使用 ICMP 报文的 traceroute:

基于ICMP报文的traceroute

使用 UDP 报文的 traceroute:

基于UDP报文的traceroute

基于UDP实现

在基于UDP的实现中,客户端发送的数据包是通过UDP协议来传输的,使用了一个大于 30000 的端口号,服务器在收到这个数据包的时候会返回一个端口不可达的ICMP错误信息,客户端通过判断收到的错误信息是TTL超时还是端口不可达来判断数据包是否到达目标主机,具体的流程如图:

image-20211022085752243

实现流程

  1. 客户端发送一个TTL为 1 ,端口号大于 30000 的UDP数据包,到达第一站路由器之后TTL被减去 1 ,返回了一个超时的ICMP数据包,客户端得到第一跳路由器的地址。
  2. 客户端发送一个TTL为 2 的数据包,在第二跳的路由器节点处超时,得到第二跳路由器的地址。
  3. 客户端发送一个TTL为 3 的数据包,数据包成功到达目标主机,返回一个端口不可达错误,traceroute结束。

Linux和macOS系统自带了一个traceroute指令,可以结合Wireshark抓包来看看它的实现原理。首先对百度的域名进行traceroute:traceroute www.baidu.com,每一跳默认发送三个数据包,我们会看到下面这样的输出:

image-20211022085720428

对该域名的IP:115.239.210.27进行traceroute,此时Wireshark抓包的结果如下:

image-20211022085739900

抓包结果

注意看红框处的内容,跟第一张图对比,可以看到traceroute程序首先通过UDP协议向目标地址115.239.210.27发送了一个TTL为1的数据包,然后在第一个路由器中TTL超时,返回一个错误类型为Time-to-live exceeded的ICMP数据包,此时我们通过该数据包的源地址可知第一站路由器的地址为10.242.0.1。之后只需要不停增加TTL的值就能得到每一跳的地址了。

然而一直跑下去会发现,traceroute并不能到达目的地,当TTL增加到一定大小之后就一直拿不到返回的数据包了:

image-20211022085834275

结果全是丢失,其实这个时候数据包已经到达目标服务器了,但是因为安全问题大部分的应用服务器都不提供UDP服务(或者被防火墙挡掉),所以我们拿不到服务器的任何返回,程序就理所当然的认为还没有结束,一直尝试增加数据包的TTL。

目前在网上找到许多开源iOS traceroute实现大多都是基于UDP的方案,实际用起来并不能达到想要的效果,所以我们需要采用另一种方案来实现。

基于ICMP实现

上述方案失败的原因是由于服务器对于UDP数据包的处理,所以在这一种实现中我们不使用UDP协议,而是直接发送一个ICMP回显请求(echo request)数据包,服务器在收到回显请求的时候会向客户端发送一个ICMP回显应答(echo reply)数据包,在这之后的流程还是跟第一种方案一样。这样就避免了我们的traceroute数据包被服务器的防火墙策略墙掉。

采用这种方案的实现流程如下:

image-20211022085752243

实现流程

  1. 客户端发送一个TTL为1的ICMP请求回显数据包,在第一跳的时候超时并返回一个ICMP超时数据包,得到第一跳的地址。
  2. 客户端发送一个TTL为2的ICMP请求回显数据包,得到第二跳的地址。
  3. 客户端发送一个TTL为3的ICMP请求回显数据包,到达目标主机,目标主机返回一个ICMP回显应答,traceroute结束。

可以看出与第一种实现相比,区别主要在发送的数据包类型以及对于结束的判断上,大体的流程还是一致的。

HPP头文件

.hpp,其实质就是将 .cpp 的实现代码混入 .h 头文件当中,定义与实现都包含在同一文件,则该类的使用只需要调用 #include<xxx.hpp> 以引用该文件即可,无需再将 .cpp 加入到project中进行编译。

而实现代码将直接编译到调用者的 .obj 文件中,不再生成单独的 .obj,采用 .hpp 将大幅度减少调用 project中的 .cpp 文件数与编译次数,也不用再发布烦人的 .lib.dll 文件,因此非常适合用来编写公用的开源库。

使用注意

.hpp 头文件的优点不少,但是编写中有以下几点要注意:

  1. .h 类似,但 .hppHeader Plus Plus 的简写,是 C++程序头文件 。

  2. VCL专用的头文件,已预编译。

  3. 是一般 模板类 的头文件。

  4. 一般来说,.h 里面只有声明,没有实现,而 .hpp 里声明实现都有,后者可以减少 .cpp 的数量。

  5. .h 里面可以有 using namespace std;,而 .hpp 里则无。

  6. 不可包含 全局对象全局函数 。由于 .hpp 本质上是作为 .h 被调用者所include,所以当 .hpp 文件中存在全局对象或者全局函数,而该 .hpp 被多个调用者include时,将在链接时导致符号重定义错误要避免这种情况,需要去除全局对象,将全局函数封装为类的静态方法

  7. 类之间不可循环调用

在.h和.cpp的场景中,当两个类或者多个类之间有循环调用关系时,只要预先在头文件做被调用类的声明即可,如下:

1
2
3
4
5
6
7
8
9
10
11
class B;

class A{
public:
void someMethod(B b);
};

class B{
public :
void someMethod(A a);
};

.hpp 场景中,由于定义与实现都已经存在于一个文件,调用者必需明确知道被调用者的所有定义,而不能等到 .cpp 中去编译。因此hpp中必须整理类之间调用关系,不可产生循环调用。同理,对于当两个类A和B分别定义在各自的 .hpp 文件中,形如以下的循环调用也将导致编译错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
//a.hpp
#include "b.hpp"
class A{
public :
void someMethod(B b);
};

//b.hpp
#include "a.hpp"
class B{
public :
void someMethod(A a);
};
  1. 不可使用静态成员

静态成员的使用限制在于如果类含有静态成员,则在 .hpp 中必需加入静态成员初始化代码,当该 .hpp 被多个文档include时,将产生符号重定义错误。唯一的例外是 const static 整型成员,因为在vs2003中,该类型允许在定义时初始化,如:

1
2
3
4
class A{
public:
const static int intValue = 123;
};

由于静态成员的使用是很常见的场景,无法强制清除,因此可以考虑以下几种方式(以下示例均为同一类中方法)

  1. 类中仅有一个静态成员时,且仅有一个调用者时,可以通过 局域静态变量模拟
1
2
3
4
5
//方法模拟获取静态成员
someType getMember(){
static someType value(xxx);//作用域内静态变量
return value;
}
  1. 类中有多个方法需要调用静态成员,而且可能存在多个静态成员时,可以将每个静态成员封装一个模拟方法,供其他方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
someType getMemberA() {
static someType value(xxx);//作用域内静态变量
return value;
}

someType getMemberB(){
static someType value(xxx);//作用域内静态变量
return value;
}

void accessMemberA(){
someType member = getMemberA();//获取静态成员
};

//获取两个静态成员
void accessStaticMember(){
someType a = getMemberA();//获取静态成员
someType b = getMemberB();
};

  1. 第二种方法对于大部分情况是通用的,但是当所需的静态成员过多时,编写封装方法的工作量将非常巨大,在此种情况下,建议使用 Singleton模式,将被调用类定义成普通类,然后使用Singleton将其变为全局唯一的对象进行调用。

如原 .h.cpp 中的定义如下:

1
2
3
4
5
6
7
class A{
public :
type getMember(){
return member;
}
static type member;//静态成员
}

采用singleton方式,实现代码可能如下(singleton实现请自行查阅相关文档)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//实际实现类
class Aprovider{
public :
type getMember(){
return member;
}
type member;//变为普通成员
}

//提供给调用者的接口类
class A{
public :
type getMember(){
return Singleton<AProvider >::getInstance()->getMember();
}
}

分离式编译

在介绍分离式编译之前需要先介绍一下 分离式代码,在C++代码中,声明和定义是可以分开写在多个文件中,当然也可以写在同一个文件里面的,如 .hpp

往往是为了逻辑条理的清晰而分开书写,但使用 g++ 和 Terminal 直接编译 main.cpp 时,都是使用的 g++ main.cpp -o main 的命令,也就是只能编译该 main.cpp 一个文件,以生成 main 为名的二进制可运行文件,实际上,书写在其他 .cpp 文件中的代码也需要一同编译,这些书写在其他 .cpp 文件中的代码被称为 分离式代码,只编译 main.cpp 就会出现 undefined reference 之类的错误。

命令行G++

在 Windows / Linux / Mac 的 Terminal 下都可以使用 g++ 命令进行编译,格式如下:

1
g++ -c main.cpp xxx1.cpp xxx2.cpp

使用 -c 选项,将包含 main()main.cpp 与 其他分离式代码文件 xxx1.cppxxx2.cpp 一同编译,然后生成一个个对象文件( .o.obj )。上面这种方法会生成三个文件 main.oxxx1.oxxx2.o ,每个 .o 都是一个对象文件,但不一定可执行(因为缺少 main() 函数),仍需要通过进一步 链接 成可执行文件:

1
g++ -o main main.o xxx1.o xxx2.o

通过上面这一行代码可以生成名为 main 的可执行二进制文件。

以上两句命令也可以通过下面这句命令替代:

1
g++ -o main main.cpp xxx1.cpp xxx2.cpp

Makefile

Makefile 文件描述了 Linux 系统下 C/C++ 工程的编译规则,它用来自动化编译 C/C++ 项目。一旦写编写好 Makefile 文件,只需要一个 make 命令,整个工程就开始自动编译,不再需要手动执行 GCC 命令。

一个中大型 C/C++ 工程的源文件有成百上千个,它们按照功能、模块、类型分别放在不同的目录中,Makefile 文件定义了一系列规则,指明了源文件的编译顺序、依赖关系、是否需要重新编译等。

如果是在 Windows 下作开发的话不需要去考虑这个问题,因为 Windows 下的集成开发环境(IDE)。当然,Windows 下的 Visual Studio Code如果没配置好,也只是个编辑器而已,不算是个IDE。一般的MVS(Microsoft Visual Studio)都已经内置了 Makefile,或者说会自动生成 Makefile,不用去手动编写。

Linux 中却不能这样,需要去手动的完成这项工作。Linux 下可以学习的开发语言有很多,常见的有 C/C++语言、python、java 等等。在 Linux(Unix) 下做开发的话,不了解 Makefile 是一件非常失败的事情。不懂 Makefile,就操作不了多文件编程,就完成不了相对于大的工程项目的操作。Makefile 可以说是必须掌握的一项技能。

Makefile 可以简单的认为是一个工程文件的编译规则,描述了整个工程的 编译链接 等规则。其中包含了那些文件需要编译,那些文件不需要编译,那些文件需要 先编译,那些文件需要 后编译,那些文件需要 重建 等等。编译整个工程需要涉及到的,在 Makefile 中都可以进行描述。换句话说,Makefile 可以使得我们的项目工程的编译变得自动化,不需要每次都手动输入一堆源文件和参数。

Makefile 可以彻底简化编译的操作。把要链接的库文件放在 Makefile 中,制定相应的规则和对应的链接顺序。这样只需要执行 make 命令,工程就会自动编译。每次想要编译工程的时候就执行 make 命令,省略掉手动编译中的参数选项和命令,非常的方便。

Makefile 支持多线程并发操作,会极大的缩短编译时间,并且当修改了源文件之后,编译整个工程的时候,make 命令只会编译修改过的文件,没有修改的文件不用重新编译,也极大的解决了耗费时间的问题。

Makefile 格式

image-20211012134610578
  • targets:规则的目标,可以是 Object File(一般称它为中间文件),也可以是可执行文件,还可以是一个标签;
  • prerequisites:是我们的依赖文件,要生成 targets 需要的文件或者是目标。可以是多个,也可以是没有;
  • command:make 需要执行的命令(任意的 shell 命令)。可以有多条命令,每一条命令占一行。

注意:我们的 目标(target) 和 依赖文件(prerequisite) 之间要使用 冒号 : 分隔开,命令的开始(before the command) 一定要使用 Tab 键。

Reference

  1. hpp.h与.h的区别
  2. Makefile教程:Makefile文件编写1天入门 (biancheng.net)

PART 1 THE BASICS

WHY TEMPLATES?

C++ 要求我们使用指定的类型来声明变量、函数 和 大部分其他实体。然而,很多代码对于不同的类型看起来都是一样的。比如,对于不同的数据结构,如 int 数组或 string 字符串向量,只要包含的类型可以相互比较,快速排序算法的实现在结构上看起来是一样的。

如果使用的编程语言不支持 通用性(genericity)的 特殊语言功能(special language feature),将不得不面临如下选择,坏的选择(bad alternatives):

  • 你可以为不同的数据类型一遍又一遍地进行着相同的行为声明;
  • 你可以为常见的基本类型(common base type)编写一段代码,例如 objectvoid*
  • 你可以使用特殊的预处理器。

如果使用其他语言,可能以前就做过一些或者所有上方所述的内容了。然而,这些方法的每一种都有其缺点:

  • 如果你一遍又一遍地实现着这些行为,毫无疑问是叠矩重规(reinvent the wheel)。你犯了相同的错误,并且你试图寻找避免复杂但能更好的算法,因为它们会制造出更多的错误。
  • 如果你为常见基本类编写代码,你会失去类型检查的好处。另外,类可能要求继承于(be derived from)能够使代码更加难以维护的特殊基础类。
  • 如果你使用特殊的预处理器,代码会被一些没有 作用范围(scope)和类型且能够造成 奇怪语义错误(strange semantic errors)的 ”愚蠢的文本替换机制“ 替代掉。

模板是能够解决重复代码编写问题且不会造成上述这些短板的方案。模板是为一种或更多尚未声明类型的函数或者类。当使用模板时,可以隐式或显式地将类型当做参数传递过去。因为模板是语言的特点,可以拥有全部的类型检查和作用范围的支持(full support of type checking and scope)。

在C++标准库中,几乎所有的代码都是模板代码。该库提供排序算法以对 对象、指定类型的值、数据结构(也叫做 容器类(Container Classes)) 进行排序,以管理指定类型的元素 和 字符串(被参数化的字符串类型)等等。

然而,以上仅是模板的使用入门,模板还允许 参数化行为(parameterize behavior) 以 优化代码(optimize code) 和 参数化信息(parameterize infomation)。

FUNCTION TEMPLATES

函数模板(function templates),即 参数化的函数(functions that are parameterized),因此代表了 一个函数系列(a family of functions)。

函数模板为不同的数据类型提供了相同的功能行为调用,或换句话说,代表了一个函数系列。该函数系列看起来就像个普通函数,除了函数的某些元素未被确定,因为这些元素被参数化了。

TEMPLATES DELARATION AND DEFINITION

EXAMPLE

以下样例 声明 了一个函数系列,且下面参数的类型被空置(is left open),如参数 T

1
2
3
4
5
template<typename T>
T max(T a, T b){
// if b < a then yield a else yield b
return b < a ? a : b;
}

在上述样例中,模板参数必须使用 template <comma-separated-list-of-parameters> 的格式进行声明。

此处的关键字 typename 代表着类型参数,这是迄今为止C++程序中最常见的一种模板参数,当然其他参数也可以,后续介绍。此处的类型参数是 T ,可以使用任意标识符当做参数名称,但使用 T 就是惯例而已。类型参数代表着 任意类型(arbitrary type),当函数调用时,由调用的函数决定具体数据类型。开发者可以调用任何类型,只要其提供模板使用的操作。

在上述样例中,类型 T 必须支持 操作符(operator) < ,因为 ab 使用该操作符进行比较,也许在 max() 的定义中很难发现,但要说明的是,T 类型必须是可复制的才能够被返回(T must be copyable in order to be returned)。

由于历史遗留原因,仍可以使用关键词 class 来定义类型参数。typename 关键字是在 C++98 标准之后才出现的,在那之前,class 是引入类型参数的唯一方法,至今仍可以使用。因此,上述模板也可以用下面的代码平替(在语义上没有区别)。

1
2
3
4
temelate<class T>
T max(T a, T b){
return b<a?a:b;
}

但因为 class 也是 的关键字,可能导致二义性,最好在声明模板时使用关键字 typename 。需要注意的是,与类声明不同,当声明类型参数时,关键字 struct 不能用来代替 typename

在C++17之前,类型 T 也必须是可复制的,以确保能够传递参数。但C++17之后,可以传递临时数(temporary),即使没有一个备份或者一个构造器可用(even if neither a copy nor a move constructor is valid)。

如何编写多个函数模板,需要注意什么?

同一个文件内可以编写多个不同的模板函数,如下:

1
2
3
4
5
6
7
8
template<typename T>
T Max(T a, T b){
return b<a?a:b;
}

template<typename t2>
t2 foo(t2*){
}

上面两个模板函数就在同一个文件下,typename 关键字后面的 T 或者 t2 需要具有唯一性。

USING TEMPLATES

需要注意的是,在使用该函数模板时,要在该函数前加上双冒号 :: ,以确保函数模板能够在全局命名空间中被查找到。如果出现下方的 call to ’sth.’ is ambiguous 错误, 即是说需要调用的函数模糊不清,因为标准库中也有一个 std::max(),编译器查找不到该函数具体是在哪个函数文件中。

image-20211005171952999

当然,以上错误也可以通过写不同的函数名称来避免。

模板并不是被编译成一个可以处理所有数据类型的实体,而是被编译成了所有不同数据类型的实体。 即,int max()short max()string max() 等等。单实体多适应 的模板虽然好像可行,但是实际上并不存在。所有的语言规则都遵循 ” 不同的模板参数生成不同的实体“ 的原则。

上面这种用具体类型取代模板参数的过程被称为 实体化(instantiation)。需要注意的是,仅仅是对函数模板的使用就可以触发该实体化过程,因此开发者就没有必要要求实体化过程单独进行(request the instantiation separately)。

另外,只要产生的代码是有效的,void 型也是可用的模板参数,例如:

1
2
3
4
template<typename T>
T foo(T*){

}

1
2
void* vp = nullptr;  // 引出 void foo(void*)
void(vp);

TWO-PHASE TRANSLATION

两段式编译,即Two-phase translation。

如果试图为一个不支持所有操作的模板进行实例化,将会导致编译时错误。

1
2
3
std::complex<float> c1, c2;  //doesn't provide operator <

::max(c1, c2); //ERROR at compile time

因此,模板在被编译时会经过如下两个阶段:

  1. 在定义且没有实例化时,忽略模板参数来检查自身代码的正确性:
    • 标点符号错误被发现,例如缺少分号 ;
    • 使用不依赖已知模板参数的未知命名(类型名,函数名等);
    • 不依赖于已检查模板参数的 静态断言(static assertions)
  2. 在实例化时,模板代码会被再次检查以确保可用,特别是依赖于模板参数的都会被二次检查(double-checked),例如:
1
2
3
4
5
6
7
template<typename T>
void foo(T t){
undeclared();
undeclared(t);
static_assert(sizeof(int>10, "int too small");
static_assert(sizeof(T>10, "T too small");
}

注意到某些编译器在第一阶段没有进行全面检查(don’t perform the full checks),所以直到在最后一阶段的模板代码实例化之前都没办法看到问题。

COMPILE AND LINK

在实际处理模板时,两段式编译会导致很多重要的问题:当函数模板被用于触发其实例化时,编译器(在某些点)需要查看模板定义。当一个函数的声明足以编译它时,就打破了普通函数通常的编译和连接的区别。

TEMPLATE ARGUMENT DEDUCTION

模板实参推断,即 Template argument deduction。

当给函数模板传递实参时,模板参数由我们传递过去的实参决定。如果传递的是两个 int 型实参给 参数类型 T ,则C++编译器就能推断出此时的 T 一定是 int

然而 T 可能只是该参数类型的一部分。例如,声明 max() 可以使用 常参(constant references)。

1
2
3
4
template<typename T>
T max(T const& a, T const& b){
return b<a?a:b;
}

如上方代码所示,传递 整型 int 参数,T 又会被推断为 int ,因为函数参数和 int const& 匹配。

类型推导过程中的类型转换

注意,自动类型转换被限制在类型推导期间:

  • 引用传递:当通过引用声明来调用参数(declaring call parameters by reference)时,即使是 微不足道的转换(trivial conersion) 也不适用于类型推导。用同一个模板参数 T 声明的两个参数类型必须完全匹配。
  • 按值传递:当按数值声明调用参数时,只支持 decay 的琐碎转换。带有const或volatile的限定被忽略,引用转换为被引用的类型,而原始数组或函数转换为相应的指针类型。对于用同一模板参数T声明的两个参数,decayed 的类型必须匹配。

错误提示:对int类型的非恒定值引用不能与int类型的临时值绑定

image-20211007035818560

前方已经说了,类型的自动转换相关注意事项,同一模板参数的两个参数类型必须完全匹配。如果在同一个 .cpp 文件中书写下面下面代码,则会出现下方报错提示 “推导类型冲突(deduced conflicting types for parameter ’T’)”。

1
2
Max(4, 7.2);
Max("Hello",s);

错误提示如下:

image-20211007040416472

但是如果非要使用不一样的数据类型的两个参数来套用同一模板,以下有三种解决方法:

  1. .cpp 文件中进行参数传递时,使用参数类型强制转换。如 Max(static_cast<double>(4), 7.2);

TERMINOLOGY

  1. 按值传递 passing by value
  2. 引用传递 passing by reference

REFERENCE

  1. <C++ Templates> David Vandecoorde

计算机网络体系结构

计算机网络是计算机技术和通信技术相结合的技术领域。由若干计算机用通信信道连接至一起,互相交换信息共享资源,形成计算机网络。

OSI 五层模型

在国际标准化组织(ISO)提出 开放系统互联(OSI,Open System Interconnection)参考模型中,网络系统结构划分为7层。

该OSI模型由上至下分别是 应用层表示层会话层传输层网络层数据链路层物理层

img

在本模型中,物理层数据链路层网络层面向网络通信 的层级;会话层表示层应用层面向信息处理 的层级。

应用层

应用层,即Application Layer。OSI参考模型中最靠近用户的一层,是为计算机用户提供应用接口,也为用户直接提供各种网络服务。我们常见应用层的网络服务协议有:HTTP,HTTPS,FTP,POP3、SMTP等。

实际公司A的老板就是我们所述的用户,而他要发送的商业报价单,就是应用层提供的一种网络服务,当然,老板也可以选择其他服务,比如说,发一份商业合同,发一份询价单,等等。

OSI七层模型中,除了应用层以外,其他功能层级都与用户实际应用没有什么直接联系,能够直接为用户提供各种应用服务的是应用层。应用层可以包含各种应用程序,有些由于使用普遍而实行了标准化,进而形成了应用层上的各种应用协议,诸如SMTP、POP3、WWW、TELNET、FTP、HTTP、HTTPS等。

表示层

表示层,即 Presentation Layer。其提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。如果必要,该层可提供一种标准表示形式,用于将计算机内部的多种数据格式转换成通信中采用的标准表示形式。数据压缩和加密也是表示层可提供的转换功能之一。

由于公司A和公司B是不同国家的公司,他们之间的商定统一用英语作为交流的语言,所以此时表示层(公司的文秘),就是将应用层的传递信息转翻译成英语。同时为了防止别的公司看到,公司A的人也会对这份报价单做一些加密的处理。这就是表示的作用,将应用层的数据转换翻译等。

会话层

会话层,即 Section Layer。负责建立、管理和终止表示层实体之间的通信会话。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。

会话层的同事拿到表示层的同事转换后资料,(会话层的同事类似公司的外联部),会话层的同事那里可能会掌握本公司与其他好多公司的联系方式,这里公司就是实际传递过程中的实体。他们要管理本公司与外界好多公司的联系会话。当接收到表示层的数据后,会话层将会建立并记录本次会话,他首先要找到公司B的地址信息,然后将整份资料放进信封,并写上地址和联系方式。准备将资料寄出。等到确定公司B接收到此份报价单后,此次会话就算结束了,外联部的同事就会终止此次会话。

传输层

传输层,即 Transport Layer。负责建立了主机端到端的链接,传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,包括 处理差错控制流量控制 等问题。该层向高层屏蔽了下层数据通信的细节,使高层用户看到的只是在两个传输实体间的一条主机到主机的、可由用户控制和设定的、可靠的数据通路。我们通常说的,TCP/UDP就是在这一层。端口号既是这里的“端”。

传输层就相当于公司中的负责快递邮件收发的人,公司自己的投递员,他们负责将上一层的要寄出的资料投递到快递公司或邮局。

传输层位于第三层和高三层之间,也是 面向网路通信面向信息处理 之间的重要层级,是整个协议层次的核心。

传输层的任务是为高层从源端机到目的机提供可靠、经济的数据传输服务,而与具体网络无关。

为了向用户提供经济有效的服务,传输层还提供多路复用和分流的功能。

网络层

网络层,即 Network Layer。负责通过IP寻址来建立两个节点之间的连接,为源端的运输层送来的分组,选择合适的路由和交换节点,正确无误地按照地址传送给目的端的运输层。就是通常说的IP层。这一层就是我们经常说的IP协议层。IP协议是Internet的基础。

网络层就相当于快递公司庞大的快递网络,全国不同的集散中心,比如说,从深圳发往北京的顺丰快递(陆运为例啊,空运好像直接就飞到北京了),首先要到顺丰的深圳集散中心,从深圳集散中心再送到武汉集散中心,从武汉集散中心再寄到北京顺义集散中心。这个每个集散中心,就相当于网络中的一个IP节点。

数据链路层

数据链路层,即 Data Linker Layer。负责将比特组合成字节,再将字节组合成帧,使用链路层地址 (以太网使用MAC地址)来访问介质,并进行差错检测。数据链路层又分为2个子层:逻辑链路控制子层(LLC)和媒体访问控制子层(MAC)。MAC子层处理CSMA/CD算法、数据出错校验、成帧等;LLC子层定义了一些字段使上次协议能共享数据链路层。 在实际使用中,LLC子层并非必需的。

这个没找到合适的例子

在物理线路上,由于噪声干扰、信号衰减畸变等原因,传输过程中常常出现差错,物理层只负责透明传输无结构的原始比特流,不能进行任何差错控制。

因此,在一条线路上传输数据时,除了必须有一条物理线路外,还需要必要规程来控制数据传输。把这些规程的软件/硬件加到链路上,就构成了数据链路层(Data Linker Layer)。

作用:通过一系列数据链路层协议,在不可靠的物理链路上实现可靠的数据传输。

为此,通常将原始数据分割成一定长度的数据单元(帧),一帧内包含 同步信号差错控制流量控制控制信息数据信息寻址 等。

差错控制

差错控制涉及两方面的问题,即 如何检测错误如何纠正错误

检查错误:要判断一个数据块是否存在错误,发送端必须在数据块中加入冗余信息,使得数据块和冗余信息间存在某种关联,接收端通过验证其关联性来判断数据是否出错。在数据块中加入冗余信息的过程称为 差错编码

差错编码有两种策略,即 仅带有检错能力的检错码带有纠错能力的检错码/纠错码。但任何一种检/纠错码的能力都是有限的,即 不能检/纠出所有的错误。一般检错码的能力越强,所需的冗余信息就会越多,编码效率会随之降低。

常见的差错编码有:

  1. 海明码,即 Hamming Code。由 Richard Hamming 于1950年提出,可纠正一个 bit 错误的编码。
  2. 循环冗余码,即 Cyclic Redundancy Code,又名 多项式码。最广泛使用的编码,但 漏检率很低,只需要简单电路即可实现。
  3. 奇偶校验码,即 Parity Check Code。 最常见也最简单的编码,只需要一个比特,但只能检出奇数个错误,漏检率达50%
  4. 校验和码,即 Checksum Code。也是最常见的检错方式,是传输的数据块中 各字节累加 后得到的 一个字节 或 按字“异或”运算 的结果。

物理层

物理层,即 Physical Layer。实际最终信号的传输是通过物理层实现的。通过物理介质传输比特流。规定了电平、速度和电缆针脚。常用设备有(各种物理设备)集线器、中继器、调制解调器、网线、双绞线、同轴电缆。这些都是物理层的传输介质。

快递寄送过程中的交通工具,就相当于我们的物理层,例如汽车,火车,飞机,船。

其中物理层有几个概念需要了解和区分,包括 传输速率信道容量传输媒体调制解调交换技术网络拓扑多路复用

传输速率

比特率:每秒传输的二进制位数。

波特率:每秒传输的码元率。

码元:单位携带的比特信息量。

如果码元率为1比特,则波特率和比特率数值上相等。

信道容量

信道容量,即信道能支持的最大数据传输速率,由信道带宽和信噪比决定。

传输媒体

传输媒体,即传输电信号的物理介质。可以是有线、无线、双绞线、同轴电缆 或 光纤 等。

调制解调

调制解调,即一种数据转换成适合在信道上传输的某种电信号形式。

数字信号转换为模拟信号有 调幅调频调相 等方式。

数字信号的信道编码方式有 单极型脉冲双极性脉冲编码 (有归零码 和 不归零码 之分)及 曼彻斯特编码 等(信道编码)。

交换技术

物理层的交换技术有三种:电路交换报文交换分组交换

TYPE PRINCIPLE ADVANTAGES DISADVANTAGES
电路交换 要求通信双方之间建立一条实际的物理通道,并在整个通信过程中 通路独占 数据在中间环节无停留,传输可靠,实时效应好; 电路不共享,资源浪费大,同时电路的建立和撤出的时间较长。
报文交换 一个 大报文(长度无限制的数据块)在通过从源站到目的站之间的中间站时采用 存储-转发 方式(有缓冲区)。 提高线路利用率 大报文延迟时间长,出错率高。
分组交换 将一个大报文分割成一定长度的信息单元(分组),各单元以此编号,以分组为单位进行 存储-转发 除了线路共享外,要求中间环节的缓存区减少,也减少了分组在网络中的延迟时间。
由于各分组在网络中可以走不同路径,该并行传输降低了报文的传输时间。分组长度变短同时降低了出错率(发现出错时重发数据所需时间也缩短)。

网络拓扑

网络拓扑(Network Topology)即网络中节点的互联结构形式,主要分为 星型结构总线型结构树型结构环型结构网型结构。(网络上很多文章都有 混合型结构 这一拓扑类型)

1002_1

TYPE PRINCIPLE SWITCHING ADVANTAGES DISADVANTAGES
星型结构 通过点对点连接至中央节点,任意两点的通信都依赖中央节点。 电路交换 任意节点故障都只会影响本站,而不影响全网。 极大以来中央节点,对中央节点的可靠性和容量要求很高,同时因为需要中央节点连接,耗费大量电缆。
总线型结构 采用单一信道作为传输介质,所有站点通过相应硬件结构接到公共信道(总线)上,任意站点发送的信息,所有其他站都能收到。 分组交换 所需电缆长度短,布线容易。
且总线仅仅是传输信道,无任何处理功能,属于无源器件,可靠性高,增加或减少站点都相对方便。
由于所有节点共享一条公共信道,当多点同时发送信号时,信号会因相互碰撞而造成传输失败,称之为 冲突
系统范围受到限制(传输速率和传输距离相互制约)。一个站点的故障可能影响整个网络,故障检测需要在各站点上进行。(待考究)
树形结构 由总线型结构演化而来,从树根开始,每一个节点向下都可以由许多分支。 故障比较容易隔离和检查。
环型结构 站点和连接站点的点-点链路组成一个闭合环路,每个站点从一条链路上接受数据,然后以相同的速率从另一条链路上发送出去。
链路大多数是单方向的,即数据沿一个方向在网上环行。
所需介质长度较短,由于链路单方向性,可以用光纤作为传输介质; 与总线型结构一样存在冲突问题,一个站点故障会引起全网故障。
网型结构 每个站点都有一条或几条链路同其他站点连接。 由于站点间存在多条路径,数据传输时可以选择空闲站点或绕开故障点,因而 网络资源利用较为充分
但站点或线路故障对网络整体影响较小,可靠性较高
结构较为复杂,成本较高。

多路复用技术

传输媒体的能力(频带宽)往往很强,对传输资源能力的应用(复用)是指将多路信号组合在一条物理信道上进行传输,然后接收端再将各路信号分离开。常见的多路复用技术如 频分多路复用(FDM)、时分多路复用(TDM) 和 码分多址(CDMA)等。

TYPE PRINCIPLE ADVANTAGES DISADVANTAGES
频分多路复用 将信道带宽按频率分割为若干子信道,每个子信道用来传输一路信号。
时分多路复用 将使用信道的时间分成一个个时间片,按一定规律将时间片分配给各路信号,每路信号只能在自己的时间片内独占信道进行传输。
码分多址 允许所有站点在同一时间使用整个信道进行数据传输。

以上三类多路复用技术可以形象地比喻为多个人要发言讨论不同问题时,如何使用同一个会议厅(信道)。

  • 可以把会议厅分成几个小厅(好比频分多路复用),各小厅同时进行各自不同的发言,互不干扰;

  • 可以在一个厅内让各议题在固定的时间片内轮流发言(好比时分多路复用);

  • 可以在一个厅内让各自议题同时发言,但是要用不同的语言(好比码分多址),对某个议题的人来说只能听懂自己的语言,而其他语言被视为随机噪音,可以排除。

TCP/IP 五层模型

实际TCP/IP使用的是5层模型,其中OSI模型中的 应用层、表示层 和 会话层 都用一层 应用层 进行表示,如下图。在每一层都工作着不同的设备,比如我们常用的交换机就工作在数据链路层的,一般的路由器是工作在网络层的。

img

在每一层实现的协议也各不同,即每一层的服务也不同,下图列出了每层主要的协议。

img

现场总线

在工业数据通信领域,总线 是指由导线组成的传输线束,连接多个传感器和执行器,实现各部件之间传送信息的公共通信干线。

然后再来看国际电工技术委员会(IEC)在IEC 61158中对现场总线的标准定义:现场总线 是安装在制造或过程区域的现场装置与控制室内的自动控制装置之间的数字式、串行、多点通信的数据总线。

从定义中可以看到,现场总线是一种数据总线技术,是一种通信协议,且该通信是数字式、串行、多节点的。

目前,在工控领域,车间现场应用最广泛的是 工业以太网 技术和 工业现场总线 技术,为工厂实现自动化带来有力推动。

现场总线应当是应用在生产最底层的一种总线型拓扑网络,即该总线是用于现场控制系统,直接与所有受控(设备)节点串行相连的通信网络。

工业自动化控制的现场一般可以从一台家电设备到一个车间、一个工厂。控制设备和网络所处的环境以及报文结构都有其特殊性,对信号的干扰往往是多方面的,而要求控制必须实时性很强。

现场总线技术

现场总线(Fieldbus)是电气工程及其自动化领域发展起来的一种工业数据总线,它主要解决工业现场的智能化仪器仪表、控制器、执行机构等现场设备间的数字通信以及这些现场控制设备和高级控制系统之间的信息传递问题。由于现场总线简单、可靠、经济实用等一系列突出的优点,因而受到了许多标准团体和计算机厂商的高度重视

传统控制系统难以实现设备之间 以及 系统与外界之间的信息交换,是一个“信息孤岛”。要满足自动化控制技术现代化的要求,同时实现整个企业的信息集成,实施综合自动化,就必须设计出一种能在工业设备之间的多点数字通信,实现底层现场设备之间以及生产现场与外界的信息交换。

现场总线控制系统(Field Control System,FCS)既是一个开放通信网络,又是一种全分布控制系统。作为智能设备的联系纽带,把挂接在总线上、作为网络节点的智能设备连接为网络系统,并进一步构成合成系统,实现基本控制、补偿计算、参数修改、报警、显示、监控、优化 及 管控一体化的综合自动化功能。

现场总线技术是一项集嵌入式系统、控制、计算机、数字通信、网络为一体的综合技术。

局限性

也可以说现场总线是工业控制和计算机网络两者的边缘产物。从纯理论的角度看,它应属于网络范畴。但是现有的网络技术不能完全适应工业现场控制系统的要求,无论是从网络的结构、协议、实时性,还是从适应性、灵活性、可靠性 乃至 成本 上进行考虑,工业控制的底层都有它的特殊性。

现场总线其规模应属于局域网、总线型结构,简单但能满足现场使用需求,所传输信息短小且实时性很强、可靠性高(网络结构层次少,信息帧短小有利于提高实时性和降低受干扰的概率)。然而现场的环境干扰因数众多,有些很强烈且带突发性。

现场总线发展

一般把50年代前的气动信号控制系统PCS称作 第一代,把4~20mA等电动模拟信号控制系统称为 第二代,把数字计算机集中式控制系统称为 第三代,而把70年代中期以来的集散式分布控制系统DCS称作 第四代

现场总线控制系统FCS作为新一代控制系统,一方面,突破了DCS系统采用通信专用网络的局限,采用了基于公开化、标准化的解决方案,克服了封闭系统所造成的缺陷;另一方面把DCS的集中与分散相结合的集散系统结构,变成了新型全分布式结构,把控制功能彻底下放到现场。可以说,开放性、分散性与数字通讯是现场总线系统最显著的特征。

  • 1984年美国Intel公司提出一种 计算机分布式控制系统-位总线(BITBUS),它主要是将低速的面向过程的 输入输出通道与高速的计算机多总线(MULTIBUS)分离,形成了现场总线的最初概念。
  • 80年代中期,美国Rosemount 公司开发了一种可寻址的远程传感器(HART)通信协议。采用在4~20mA模拟量叠加了一种频率信号,用双绞线实现数字信号传输。HART协议已是现场总线的雏形。
  • 1985年由Honeywell和Bailey等大公司发起,成立了World FIP制定了FIP协议。
  • 1987年,以Siemens,Rosemount,横河等几家著名公司为首也成立了一个专门委员会互操作系统协议(ISP)并制定了PROFIBUS协议。后来美国仪器仪表学会也制定了现场总线标准IEC/ISA SP50。
  • 随着时间的推移,世界逐渐形成了两个针锋相对的互相竞争的现场总线集团:一个是以Siemens、Rosemount,横河为首的ISP集团;另一个是由Honeywell、Bailey等公司牵头的WorldFIP集团。1994年,两大集团宣布合并,融合成现场总线基金会(Fieldbus Foundation)简称FF。对于现场总线的技术发展和制定标准,基金委员会取得以下共识:共同制定遵循IEC/ISA SP50协议标准;商定现场总线技术发展阶段时间表。

现场总线能力

部分应用场景或产品开发时,会对现场总线的能力提出如下要求:

本质安全防爆本质安全 是指通过设计等手段使生产设备或生产系统本身具有安全性,即使在误操作或发生故障的情况下也不会造成事故的功能。具体包括 失误—安全(误操作不会导致事故发生或自动阻止误操作)和 故障—安全 功能(设备、工艺发生故障时还能暂时正常工作或自动转变安全状态)。

本质安全型电气设备的防爆原理:通过限制电气设备电路的各种参数,或采取保护措施来限制电路的火花放电能量和热能,使其在正常工作和规定的故障状态下产生的电火花和热效应均不能点燃周围环境的爆炸性混合物,从而实现了电气防爆,这种电气设备的电路本身就具有防爆性能,也就是从“本质”上就是安全的。

现场总线分类

国际上有40多种现场总线,但没有任何一种现场总线能覆盖所有的应用面,按其传输数据的大小可分为3类:

  1. 传感器总线(sensor bus),属于位传输;
  2. 设备总线(device bus),属于字节传输;
  3. 现场总线,属于数据流传输。

FF

FF,即 Foundation Field bus,基金会现场总线。

是以美国Fisher-Rouse mount公司为首的联合了横河、ABB、西门子、英维斯等80家公司制定的ISP协议和以Honeywell公司为首的联合欧洲等地150余家公司制定的World FIP协议于1994年9月合并的。该总线在过程自动化领域得到了广泛的应用,具有良好的发展前景。

FF 总线采用国际标准化组织ISO的开放化系统互联OSI的简化模型(1,2,7层),即物理层、数据链路层、应用层,另外增加了用户层。

FF 分低速H1和高速H2两种通信速率,前者传输速率为31.25Kbit/秒,通信距离可达1900m,可支持总线供电和本质安全防爆环境。后者传输速率为1Mbit/秒和2.5Mbit/秒,通信距离为750m和500m,支持双绞线、光缆和无线发射,协议符号IEC1158-2标准。

FF 的物理媒介的传输信号采用曼切斯特编码。

CAN

CAN,即Controller Area Network,控制器局域网络。

最早由德国BOSCH公司推出,它广泛用于离散控制领域,其总线规范已被ISO国际标准组织制定为国际标准,得到了Intel、Motorola、NEC等公司的支持。CAN协议分为二层:物理层和数据链路层。CAN的信号传输采用短帧结构,传输时间短,具有自动关闭功能,具有较强的抗干扰能力。CAN支持多主工作方式,并采用了非破坏性总线仲裁技术,通过设置优先级来避免冲突,通讯距离最远可达10KM/5Kbps/s,通讯速率最高可达40M/1Mbp/s,网络节点数实际可达110个。目前已有多家公司开发了符合CAN协议的通信芯片。

LonWorks

由美国Echelon公司推出,并由Motorola、Toshiba公司共同倡导。它采用ISO/OSI模型的全部7层通讯协议,采用面向对象的设计方法,通过网络变量把网络通信设计简化为参数设置。支持双绞线、同轴电缆、光缆和红外线等多种通信介质,通讯速率从300bit/s至1.5M/s不等,直接通信距离可达2700m(78Kbit/s),被誉为通用控制网络。Lonworks技术采用的Lon Talk协议被封装到Neuron(神经元)的芯片中,并得以实现。采用LonWorks技术和神经元芯片的产品,被广泛应用在楼宇自动化、家庭自动化、保安系统、办公设备、交通运输、工业过程控制等行业。

Device Net

Device Net是一种低成本的通信连接也是一种简单的网络解决方案,有着开放的网络标准。Device Net具有的直接互联性不仅改善了设备间的通信而且提供了相当重要的设备级阵地功能。Device Net基于CAN技术,传输率为125Kbit/s至500Kbit/s,每个网络的最大节点为64个,其通信模式为:生产者/客户(Producer/Consumer),采用多信道广播信息发送方式。位于Device Net网络上的设备可以自由连接或断开,不影响网上的其他设备,而且其设备的安装布线成本也较低。Device Net总线的组织结构是Open Device Net Vendor Association(开放式设备网络供应商协会,简称“ODVA”)。

PROFIBUS

PROFIBUS是德国标准(DIN19245)和欧洲标准(EN50170)的现场总线标准。由PROFIBUS--DP、PROFIBUS-FMS、PROFIBUS-PA系列组成。DP用于分散外设间高速数据传输,适用于加工自动化领域。FMS适用于纺织、楼宇自动化、可编程控制器、低压开关等。PA用于过程自动化的总线类型,服从IEC1158-2标准。PROFIBUS支持主-从系统、纯主站系统、多主多从混合系统等几种传输方式。PROFIBUS的传输速率为9.6Kbit/s至12Mbit/s,最大传输距离在9.6Kbit/s下为1200m,在12Mbit/s小为200m,可采用中继器延长至10km,传输介质为双绞线或者光缆,最多可挂接127个站点。

HART

HART,即 Highway Addressable Remote Transducer,高速可寻址远程传感器。

最早由Rosemount公司开发。其特点是在现有模拟信号传输线上实现数字信号通信,属于模拟系统向数字系统转变的过渡产品。其通信模型采用物理层、数据链路层和应用层三层,支持点对点主从应答方式和多点广播方式。由于它采用模拟数字信号混和,难以开发通用的通信接口芯片。

HART能利用总线供电,可满足本质安全防爆的要求,并可用于由手持编程器与管理系统主机作为主设备的双主设备系统。

CC-Link是Control&Communication Link(控制与通信链路系统)的缩写,在1996年11月,由三菱电机为主导的多家公司推出,其增长势头迅猛,在亚洲占有较大份额。在其系统中,可以将控制和信息数据同是以10Mbit/s高速传送至现场网络,具有性能卓越、使用简单、应用广泛、节省成本等优点。其不仅解决了工业现场配线复杂的问题,同时具有优异的抗噪性能和兼容性。CC-Link是一个以设备层为主的网络,同时也可覆盖较高层次的控制层和较低层次的传感层。2005年7月CC-Link被中国国家标准委员会批准为中国国家标准指导性技术文件。

World FIP

World FIP的北美部分与ISP合并为FF以后,World FIP的欧洲部分仍保持独立,总部设在法国。其在欧洲市场占有重要地位,特别是在法国占有率大约为60%。World FIP的特点是具有单一的总线结构来适用不同的应用领域的需求,而且没有任何网关或网桥,用软件的办法来解决高速和低速的衔接。World FIP与FFHSE可以实现“透明联接”,并对FF的H1进行了技术拓展,如速率等。在与IEC61158第一类型的连接方面,World FIP做得最好,走在世界前列。

此外较有影响的现场总线还有丹麦公司Process-Data A/S提出的P-Net,该总线主要应用于农业、林业、水利、食品等行业;Swift Net现场总线主要使用在航空航天等领域,还有一些其他的现场总线这里就不再赘述了。

INTERBUS

INTERBUS是德国Phoenix公司推出的较早的现场总线,2000年2月成为国际标准IEC61158。INTERBUS采用国际标准化组织ISO的开放化系统互联OSI的简化模型(1,2,7层),即物理层、数据链路层、应用层,具有强大的可靠性、可诊断性和易维护性。其采用集总帧型的数据环通信,具有低速度、高效率的特点,并严格保证了数据传输的同步性和周期性;该总线的实时性、抗干扰性和可维护性也非常出色。INTERBUS广泛地应用到汽车、烟草、仓储、造纸、包装、食品等工业,成为国际现场总线的领先者。

其他

RS-485和现场总线同属于总线;ModBus和现场总线同属于通信协议;

严格来讲,RS-485≠现场总线,ModBus≠现场总线,RS-485+ModBus(还有其他什么的)才构成现场总线,其中RS-485工作在现场总线的物理层,ModBus工作在现场总线的应用层;

参考

  1. 《现场总线CAN原理与技术应用》第二版,北京航空航天大学出版社,饶运涛、邹继军、王进宏、郑勇芸 编著
  2. 现场总线
  3. OSI网络模型
  4. 本质安全
  5. 本质安全型电气设备防爆原理

通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。

image-20210927010622456

抖动时间

抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。这是一个很重要的时间参数,在很多场合都要用到。

按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒,大家可以用示波器测试一下。键抖动会引起一次按键被误读多次。为确保CPU对键的一次闭合仅作一次处理,必须去除键抖动。在键闭合稳定时读取键的状态,并且必须判别到键释放稳定后再作处理。

消抖方法

按键的消抖,可用硬件或软件两种方法。

硬件消抖

在键数较少时可用硬件方法消除键抖动。下图所示的RS触发器为常用的硬件去抖。

image-20210927010535868

图中两个“与非”门构成一个RS触发器。当按键未按下时,输出为1;当键按下时,输出为0。此时即使用按键的机械性能,使按键因弹性抖动而产生瞬时断开(抖动跳开B),中要按键不返回原始状态A,双稳态电路的状态不改变,输出保持为0,不会产生抖动的波形。也就是说,即使B点的电压波形是抖动的,但经双稳态电路之后,其输出为正规的矩形波。这一点通过分析RS触发器的工作过程很容易得到验证。

另一种硬件消抖的方法利用电容的放电延时,采用并联电容法,也可以实现硬件消抖,如图3所示:

image-20210927010459327

软件消抖

如果按键较多,常用软件方法去抖,即检测出键闭合后执行一个延时程序,5ms~10ms的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。当检测到按键释放后,也要给5ms~10ms的延时,待后沿抖动消失后才能转入该键的处理程序。还可以利用定时器中断来消抖。

消抖是为了避免在按键按下或是抬起时电平剧烈抖动带来的影响。一般来说,软件消抖的方法是不断检测按键值,直到按键值稳定。实现方法:假设未按键时输入1,按键后输入为0,抖动时不定。可以做以下检测:检测到按键输入为0之后,延时20ms,再次检测,如果按键还为0,那么就认为有按键输入。延时的20ms恰好避开了抖动期。

下面再介绍一种按键消抖的方法:利用switch()结构,程序设计如下:

无延时的软件消抖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*********************************************
名称:键盘扫描子函数
功能:在按键稳定期内判断键值,并返回键值
**********************************************/
uchar keyscan(void)
{
static char key_state = 0;
static char key_value = 0;
uchar key_press, key_return = 0;
key_press=turn_left&turn_right; //读按键I/O电平
switch (key_state)
{
case 0 : // 按键初始态
if (key_press==0) key_state = 1; // 键被按下,但需要确认是否是干扰
break;
case 1 : // 按键确认态
if (key_press==0)
//如有键按下则不是干扰,判断键值
{ if(turn_left==0) //判断是哪一个按键被按下
key_value=1; //按键较多时可采用switch选择结构
else if(turn_right==0)
key_value=2;
else key_value=0;
key_state = 2; // 状态转换到键释放态
}
else key_state = 0; // 按键已抬起,属于干扰,转换到按键初始态
break;
case 2 :
if (key_press==1)
{
key_return=key_value;//按键释放后再输出键值
key_value=0;
key_state = 0; //如果按键释放,转换到按键初始态
} break;
}return key_return; //返回键值
}
/*********************************************
名称:按键处理子函数
功能:
**********************************************/
void key_operation(void)
{
switch (keyscan()) //根据键值不同,执行不同的内容
{ case 1:
hight_votage-=1;
if(hight_votage<5)
hight_votage=5; break;
case 2:
hight_votage+=1;
if(hight_votage>25)
hight_votage=25; break;
default :break;
}
}

总结

只要有按键就一定要想到消抖,总之不管是硬件消抖还是软件消抖,在脑海里始终要想到按键按下时出现图一的情景,然后再进行相应的设计。

对于多个按键的设计常用思路是:按照面向过程的编程方式,将数据与过程分离。把和按键状态相关的东西比如按键功能统统塞到结构里, 把消抖的代码放在一个函数中。这里介绍按键设计的几种方法:

  1. 矩阵键盘,(这个文档里放了几个矩阵键盘的程序,个人觉得这几个程序比较精简,大家可以参看一下)

  2. ADC按键,ADC按键的优点是节省IO口,但是需要调配好电阻值,个人觉得有点麻烦。

  3. 并入串出按键

  4. 4*3键盘, 并且复用端口很牛

参考

  1. 浅谈如何按键消抖

前置知识

  • 带宽及传输速率转换
  • TCP/IP网络协议
  • 硬件功耗计算和测量

概述

将从通信速率、通信距离、通信功耗、组网能力/网路容量、 应用场景 及 安全性 等各方面来区分无线通信各种技术。

通信距离和通信速率

超近距离
10公分以内
近距离
1米以内
近距离
20米以内
中距离
20至100米
远距离
1公里以内
超远距离
1公里以上
超高速率
> 100Mbps
RFID WiFi 5G
高速率
< 100Mbps
RFID WiFi 3G/4G
中速率
< 10Mbps
WiFi
Bluethooth
Bluethooth CMDA2K1X
2G
低速率
< 1Mbps
NFC ZigBee
Z-Ware
《IEEE 802.11 ah》 Lora
NB-IOT(基于MTC)
GPRS
超低速率
< 1Kbps
Sigfox

还有很多躺在历史垃圾堆角落里,没有得到使用和普及的通信协议,如 WiMax(IEEE 802.16),

通信功耗

超高功耗:> 100mA

高功耗:30~100mA

中功耗:< 30mA

低功耗:< 20mA

超低功耗:< 10mA

超低功耗 低功耗 中功耗 高功耗

组网能力/网路容量

应用场景

LPWAN/LPWA

LPWAN, Low Power Wide Area Network,低功耗广域网络,也可以缩写为LPWA。

LPWAN 不是指任何一种特定技术,而是作为任何网络的通用术语,这些网络旨在以比其他网络(如蜂窝、卫星或 WiFi)更低的功率进行无线通信。此外,与其他使用蓝牙或 NFC 的低功耗网络相比,LPWAN 的通信距离更远。

LPWA 网络非常有限的带宽不适合大多数消费者和商业应用,例如语音、视频、音频甚至文本消息。因此,LPWA 网络几乎完全由物联网 (IoT) 和 M2M(机器对机器)通信中的设备使用。

虽然冰箱、灯泡或 Nest 温度计等家用或商用设备都可以轻松搭载家庭或办公室 WiFi 连接,但某些设备不能依赖这种连接。

LoRa、SigFox、NB-IoT 都属于 LPWA 的范畴,LoRa 不需要 SIM 卡,NB-IoT 需要 SIM 卡。

image-20210920171429124

LoRa

LoRa是美国Semtech公司的专有技术,实际是一种采用扩频方案的无线调制解调技术。Semtech在2012年通过并购法国Cycleo公司获得LoRa的IP产权,以此设计制造射频芯片并进行市场销售,是一家纯半导体公司,垄断LoRa芯片的供货。目前Semtech虽然也进行少量LoRa的IP授权,如国内的阿里,但IP是Semtech专有,没有授权任何公司无法设计制造LoRa芯片,所以客户的选择余地很小。

LoRa使用的也是公共频段进行射频信号传输,上层协议及规范由LoRaWan定义,LoRa联盟负责发布和维护。任何人可以购买LoRa芯片或者模块来设计LoRa终端和网关设备,也可以设计或者购买设备来搭建LoRa网络,所以目前多数的LoRa网络都是小区域和私有的,很少有全国性的通用物联网络,而设备和网络间的兼容也是一个很大的挑战。

LoRa通过扩频技术实现高灵敏度,从而能够进行远距离传输,但是网络容量有限,无法高效实现大批量设备信息的并行接收和处理,这对于大面积或全国性的部署是个巨大挑战。

术语

A link budget is used to compute cell coverage by accounting for all the factors that determine the cell coverage to balance the system cost against the required cell capacity. Link budget factors controlled by the RF engineer include transmitter-radiated power, antenna gain, noise figures, and co-channel interference (i.e., reuse factor N). Factors that cannot be controlled directly, but must be considered in the link budget, include propagation path loss and system bandwidth.

链路预算是用来计算小区覆盖率的,它考虑了决定小区覆盖率的所有因素,以平衡系统成本和所需小区容量。由射频工程师控制的链路预算因素包括发射器辐射功率、天线增益、噪声数字和同信道干扰(即重用系数N)。不能直接控制,但必须在链路预算中考虑的因素包括传播路径损耗和系统带宽。

参考

  1. 《无线传感网络》杨博雄/倪玉华
  2. 物联网通信标准分类 谁最有优势?
  3. 为什么WIMAX没有普及?
  4. 什么是LPWA
  5. NB-IoT 通往智能世界的基础联接技术
  6. Link Budget - an overview | ScienceDirect Topics
  7. LPWA 物联网络

RS Serial Port

RS系列(RS-232/RS-422/RS-485)标准(RS,即Recommend Standard,推荐标准 之意 )是常用的(异步)串行通信接口标准,该系列标准由美国电子工业协会(EIA)牵头开发。

RS系列标准只对 接口电气特性(Electric Specification of Interface)进行规定,并不涉及插件、电缆或协议,再次基础上,用户可以建立自己的接头、插座形状 以及 高层通信协议 。

RS-232

RS-232标准接口(又称EIA RS-232)是由美国电子工业协会(EIA)联合贝尔系统公司、调制解调器厂家及计算机终端生产厂家于1970年共同制定,其全名是“数据终端设备( DTE)和数据通信设备(DCE)之间串行二进制数据交换接口技术标准”。

RS232协议 和 TTL协议更多是电气特性(如电平标准和电压),二者在软件协议层面是一样的,如对于同样传输 0b01010101 来说,RS232 和 TTL的时序对比如下图所示:

img

图 RS232标准和TTL标准的时序对比

当然,RS232和TTL/CMOS之间的转换,不仅仅是简单的电平转换,还要考虑到其他一些因素,比如 调节矫正 一些电平(提高或降低对应的电平),确保可能的有害的RS232电压不会破坏微控制器的串口针脚。较为成熟的方案是通过MAX3232之类的芯片,把TTL电平转为RS232电平,或者在淘宝购买TTL转RS232电缆即可。

由于RS-232采取 不平衡传输 方式,即所谓 单端通讯。收(RX)、发(TX) 端的数据信号是相对于信号地(GND)。典型的RS-232信号在正负电平之间摆动,

  • 在发送数据时,发送端驱动器输出正电平在+5+15V,负电平在-5-15V电平。
  • 当无数据传输时,线上为TTL,从开始传送数据到结束,线上电平从TTL电平到RS-232电平再返回TTL电平。

接收器典型的工作电平在 +3~+12V-3~-12V由于发送电平与接收电平的差仅为2V至3V左右,所以其共模抑制能力差,再加上双绞线上的分布电容,其传送距离最大为约15米,最高速率为20Kbps。RS-232是为 单节点通讯(即点对点,用一对收、发设备)而设计的,其驱动器负载为3kΩ~7kΩ。所以RS-232适合本地设备之间的通信。

RS-422

RS-422、RS-485与RS-232不一样,数据信号采用差分传输方式,也称作 平衡传输,它使用一对双绞线,将其中一线定义为A,另一线定义为B。通常情况下,发送驱动器A、B之间的正电平在+2+6V,是一个逻辑状态,负电平在-2V6V,是另一个逻辑状态。另有一个信号地C,在RS-485中还有一“使能”端,而在RS-422中这是可用可不用的。“使能”端是用于控制发送驱动器与传输线的切断与连接。当“使能”端起作用时,发送驱动器处于高阻状态,称作“第三态”,即它是有别于逻辑“1”与“0”的第三态。

由于接收器采用高输入阻抗和发送驱动器比RS232更强的驱动能力,故允许在相同传输线上连接多个接收节点,最多可接10个节点。即一个主设备(Master),其余为从设备(Salve),从设备之间不能通信,所以RS-422支持点对多的双向通信。RS-422四线接口由于采用单独的发送和接收通道,因此不必控制数据方向,各装置之间任何必须的信号交换均可以按软件方式(XON/XOFF握手)或硬件方式(一对单独的双绞线)实现。RS-422的最大传输距离为4000英尺(约1219米),最大传输速率为10Mbps。其平衡双绞线的长度与传输速率成反比,在100Kbps速率以下,才可能达到最大传输距离。只有在很短的距离下才能获得最高速率传输。一般100米长的双绞线上所能获得的最大传输速率仅为1Mbps。RS-422需要一终接电阻,要求其阻值约等于传输电缆的特性阻抗。在矩距离传输时可不需终接电阻,即一般在300米以下不需终接电阻。终接电阻接在传输电缆的最远端。

RS-485

RS-485 标准只对 接口电气特性(Electric Specification of Interface)进行规定,并不涉及插件、电缆或协议,再次基础上,用户可以建立自己的接头、插座形状 以及 高层通信协议 。

RS-485 的 通信距离通信速率 是负相关的,

RS485有 两线制四线制,四线制只能实现点对点的通信方式,现很少采用。

接口(标准)对比

以下仅从RS的硬件要求进行横向对比:

表 RS系列标准对比
RS-232 RS-422 RS-485
半双工/全双工 全双工 全双工 半双工
波特率
传输速率范围 300bps~100Kbps 10Mbps 10Mbps
联网支持(多设备连接) 不支持 支持 支持
电压范围
差模/共模 共模 差模 差模
最少信号线数量 3根 5根 2根
最大传输距离 15m(波特率=9600时) 3900m
控制信号(DE,Drive Enable) 可选
输入阻抗 3~7kΩ

使用RS422协议的设备间的引脚互相连接示意图:

img

UART Protocol

UART,即Universal Asynchronous Receiver/Transmitter,通用异步收发

在通信和计算机科学中,Serial communication是一个通用概念,泛指所有的串行的通信协议,如RS232、USB、I2C、SPI、1-Wire、Ethernet等。这里的串行(serial),是相对并行通信(parallel communication)来说的,如下图:

img

广义上讲,UART是指 支持RS系列(物理接口)标准和TLL电平 的 通信协议

因为UART更多关注规定编码格式的标准,如 波特率(baud rate)、帧格式波特率误差 等等,所以了解UART时,不可避免需要先在电气特性上区分几个常用的概念,如 TLL电平CMOS电平

TTL集成电路的主要型式为晶体管-晶体管逻辑门(transistor-transistor logic gate),TTL大部分都采用5V电源。

TTL电平(参考值) CMOS电平(参考值)
输入高电平 ≥ 2.4V ≈ 0.7*VCC
输入低电平 ≤ 0.4V ≈ 0.3*VCC
输出高电平 ≥ 2.0V ≈ VCC 或 <0.9*VCC
输出低电平 ≤ 0.8V ≈ GND 或 < 0.1*VCC

:VCC的概念可以参考文章《VCC、VDD、VEE、VSS的区别》,具体输入输出的高低电平范围需要参考芯片及电路的手册。

接口

以下介绍均为UART常用的物理接口。

D型数据接口连接器,用于连接电子设备(比如:计算机与外设)的接口标准。因形状类似于英文字母D,故得名D型接口。

按照接口数量细分为A型(15针),B型(25针),C型(37针),D型(50针),E型(9针)。因此常见的计算机并口即为DB25针的连接器。而串口则应为DE9针连接器。

由于早期的计算机的串口与并口都是使用DB25针连接器,而人们则习惯把字母B与D合在一起记了下来,当作D型接口的共同名字,以至于后来计算机串口改用9针接口以后,人们更多的使用DB9而不是DE9来称呼9针的接口。这一习惯进一步推广的结果就是如今人们使用DBxx来代表D型接口,数字xx则为接口的针数。

DB9

COM口是指针对串行通信协议的一种端口,是PC上异步串行通信口的简写,大部分为9针孔D型。

因此 DB9 就和 COM口 混用了,但是COM口里又进一步区分RS232,RS422和RS485,传输功能依次递增。

image-20210915103057209
图 DB9/COM口在各RS标准下的引脚使用和定义

但由于历史原因,IBM的PC外部接口配置为RS232,成为实际上的PC界默认标准。所以,现在PC机的COM口的物理标准均为RS232。DB9/COM口的公头和母头实际外观如下:

image-20210915093214837image-20210915104612002
图 DB9/COM口的公头(左)和母头(右)外观

端子

端子,Terminal,也称为 接线终端 / 接线端子

种类分单孔,双孔,插口,挂钩等,从材料分,铜镀银,铜镀锌,铜,铝,铁等。它们的作用主要传递电信号或导电用。

端子排型号

转换器

USB转TTL

常见的USB转TTL的芯片,如CP2101 / CP2102 / CH340 / CH341 / FT232BM / FT232RL / PL2303 / PV8651等,尤其CH340和CP2102 的转换器最为常见。

image-20210915113729086

图 USB转TTL(CH340G芯片)转换器

image-20210915114016213

图 USB转TTL(FT232芯片)转换器

USB转DB9(RS232)

image-20210915095451523

图 USB转DB9数据线

RJ45和DB9

这种转换器通常用在交换器和路由器的调试工作上,

image-20210915105657103
图 RJ45母 转 DB9公

image-20210915105821577

图 RJ45公 转 DB9母

RJ45转端子

imgimgimg
图 RJ45公 转 2/3/4引脚端子

RS-232转RS-485

目前主流的家用台式机上已经逐渐取消DB9接口,在某些旧台式电脑主机或者商用台式电脑主机上可能仍有DB9端口可以适配 RS-232(DB9公/母)/RS-485(端子) 转换器,然后再利用一根双绞线(可以用网线制作)连接转换器的 A+ / B- 端子 和 通信设备。如下图所示:

image-20210915091142748
图 DB9公(RS232)转3引脚端子(RS485)

由于该转换器为电气隔离设计,需要使用独立的VCC/GND/T/R来进行供电,因此不能只连接两根信号线。

该类转换器同样按照DB9接口的公/母来区分引脚/信号定义,下表为深圳市宇泰科技出品的UT-2201转换器说明书中的内容:

image-20210915142023932

下图为RS485和转换器之间的连接示意,转换器和单点之间的连接:

image-20210915141402305

转换器和多点的连接:

image-20210915141728889

参考

  1. 《串行通信技术 面向嵌入式系统开发》 周云波 著 中国工信出版集团 电子工业出版社
  2. 《ANALOG DEVICE-5 V Low PowerEIA RS-485 Transceiver》
  3. 《InterfaceCircuitsforTIA/EIA-485(RS-485)》
  4. 聚英JY-CON503 RS232转RS485/RS422 宽压DC7-30V供电 全隔离设计
  5. DB9针和DB25针串口的引脚定义
  6. 为什么越来越多人使用RS232接口,却还分不清DB9、DB25的引脚定义?
  7. RS-232
  8. DB9
  9. 串口、COM口、UART口, TTL、RS-232、RS-485区别详解
  10. UART、RS232、TTL关系浅析
  11. RS232、RS422、RS485、TTL电平
  12. 一文搞懂Modbus与RS485通信协议