《通信软件设计》期末考点总结

就亿点点,轻轻松松的辣~

By yesmore on 2022-05-12
阅读时间 24 分钟
文章共 6k
阅读量

题型:问答 和 写程序(写 状态机程序、写报文结构程序)

方向:

1、操作命令,应该是实验用过的,以及一些讲过的概念(很少)

2、程序理解,主要是实验的前面几个,特简单的。包括 补充的 sizeof 那个程序,

注意:每个语句、每个标识符都看懂,少一个都不行,实验的程序前几个 简单,看懂意思、语法、改错

3、状态机设计、编程。比如识别 字串的 NNNN,识别 字串的78

4、学会报文定义,用C语言定义。比如IP报文 UDP 报文。当然不可能题目就是IP、UDP,和 MAVLINK 有关

5、通信软件相关的概念问答,当然比 1 的简单回答要深刻

6、报文C语言定义、封装报文的C语言程序,解析报文的C语言程序

0、常用命令

gcc编译+运行

1
2
3
4
5
6
7
8
9
10
11
12
gcc -c ../serial/lflserial.c
gcc -o tty_r tty_recv_file.c lflserial.o
gcc -o tty_s tty_send_file.c lflserial.o

# 创建一个窗口(终端)开启串口
./tty_r /dev/pts/4 tt

# 创建一个窗口(终端)
./tty_s /dev/pts/3 test_file

# 比较文件命令
diff test_file tt

其他命令

1
2
3
4
5
6
7
8
9
10
11
12
13
cd path  # 改变当前目录		 
pwd # 显示当前目录路径
cp [options] 源文件 目标文件(夹)# 拷贝文件
mv [options] 源文件 目标文件 # 移动文件 或 重命名
gcc c源文件 [-o 可执行文件名] # 编译c语言程序
rm [option] 文件(夹)名 # 删除文件 或 目录
diff [options] file1 file2 # 比较文件
ls # 列出当前目录下的文件
chmod 777 文件名 # 改变文件权限
chgrp 组名 文件名/目录 # 改表文件的组
chown [-R] 账号名:组群 文件/目录 # 改变文件的主(所有者)
vim 文件名 # 编辑文件
gedit filename

Linux文件夹

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
/bin 二进制可执行命令
/dev 设备特殊文件
/etc 系统管理和配置文件
/etc/rc.d 启动的配置文件和脚本
/home 用户主目录的基点,比如用户user的主目录就是/home/user,可以用~user表示
/lib 标准程序设计库,又叫动态链接共享库,作用类似windows里的.dll文件
/sbin 系统管理命令,这里存放的是系统管理员使用的管理程序
/tmp 公用的临时文件存储点
/root 系统管理员的主目录
/mnt 系统提供这个目录是让用户临时挂载其他的文件系统。
/lost+found 这个目录平时是空的,系统非正常关机而留下“无家可归”的文件(windows下叫什么.chk)就在这里
/proc 虚拟的目录,是系统内存的映射。可直接访问这个目录来获取系统信息。

/opt 存放可选程序的目录,比如我想安装firefox的最新beta版,可以放在这里。卸载的话删除那个文件夹好了
/var 某些大文件的溢出区,比方说各种服务的日志文件
/usr 最庞大的目录,要用到的应用程序和文件几乎都在这个目录。其中包含:
/usr/x11r6 存放x window的目录
/usr/bin 众多的应用程序
/usr/sbin 超级用户的一些管理程序
/usr/doc linux文档
/usr/include linux下开发和编译应用程序所需要的头文件
/usr/lib 常用的动态链接库和软件包的配置文件
/usr/man 帮助文档
/usr/src 源代码,linux内核的源代码就放在/usr/src/linux里
/usr/local/bin 本地增加的命令
/usr/local/lib 本地增加的库

一、概念题

0.C语言基础

标识符

  • sizeof 获取某个数据类型所占用空间的字节数
  • typedef 为一种数据类型定义一个新名字
  • struct 定义结构体
  • union 定义共用体

联合体:所有成员共用一块地址空间,也就是说联合体只放了一个被选中的成员;

结构体:所有的成员的内存占用是累加的,其所有成员都存在,不同成员会放在不同地址;

1.通信

通信 (Communication)就是信息的传递,是指由一地向另一地进行信息的传输与交换,其目的是传输消息。

把A地的信息在B地近似重现、把A地的信息传输到B地、靠协议实现的传输

2.信息

音讯 、消息 、通讯系统传输和处理的对象,泛指人类社会 传播 的一切 内容。

3.硬件

是 计算机硬件 的简称,是指 计算机系统中由电子,机械和光电元件等组成的各种物理装置的总称。这些物理装置按 系统结构 的要求构成一个有机整体为 计算机软件 运行提供物质基础。简而言之,硬件的功能是输入并存储 程序 和数据,以及 执行程序 把 数据 加工成可以利用的形式。从外观上来看,微机由 主机 箱和 外部设备 组成。

4.操作系统

操作系统(operating system,简称OS)是管理计算机硬件软件资源的计算机程序。操作系统需要处理如管理与配置内存、决定系统资源供需的优先次序、控制输入设备输出设备、操作网络与管理文件系统等基本事务。操作系统也提供一个让用户与系统交互操作的界面。

5.软件

包括程序数据及其相关文档的完整集合

13.通信软件

是指实现通信协议的软件

  • 完成数据/信息传输与交换的软件
  • 完成数据/信息产生与应用的软件
  • 实现网络中的管理/计费的软件

特征:

  • 线程设计:多终端通信
  • 协议体系:协议栈与报文传递
  • 通信协议:状态机与协议
  • 报文协议:协议解析与处理
  • 字节与报文协议:数据接收与报文识别
  • 操作系统内核:需要硬件驱动程序的支持
  • 计算机的IO接口:需要通信硬件的支持

6.应用软件

应用软件(Application)是和系统软件相对应的,是用户可以使用的各种程序设计语言,以及用各种程序设计语言编制的应用程序的集合,分为应用软件包用户程序。应用软件包是利用计算机解决某类问题而设计的程序的集合,多供用户使用。

应用软件是为满足用户不同领域、不同问题的应用需求而提供的那部分软件。 它可以拓宽计算机系统的应用领域,放大硬件的功能。

7.APP

智能手机的第三方应用程序

主要指的都是ios mac android等系统下的应用软件。

8.编程语言

编程语言(programming language)可以简单的理解为一种计算机和人都能识别的语言。一种计算机语言让程序员能够准确地定义计算机所需要使用的数据,并精确地定义在不同情况下所应当采取的行动。

计算机编程语言主要包括汇编语言、机器语言以及高级语言

9.编辑

10.编译

编译就是把高级语言变成计算机可以识别的2进制语言,计算机只认识1和0,编译程序把人们熟悉的语言换成2进制的;将源代码一次性转换成目标代码的过程。

1、利用编译程序从源语言编写的源程序产生目标程序的过程。

2、用编译程序产生目标程序的动作

11.脚本

使用一种特定的描述性 语言 ,依据一定的 格式 编写的 可执行文件

12.解释执行

编译:将源代码一次性转换成目标代码的过程

解释:将源代码逐条转换成目标代码同时逐条运行的过程。

执行解释过程的程序叫做解释器

14.命令

计算机命令:指计算机的快捷指令

  • 命令是应用程序(Application)向操作系统(OS)的交流。

  • 指令是操作系统CPU的处理信息。

  • 计算机指令包括计算机命令

15.指令

指令是控制计算机执行的命令,它由操作码和地址码组成。

指令是计算机能实现的基本操作,是指挥机器工作的指示和命令,指令均为二进制数形式;指令由操作码和地址码组成,操作码告诉计算机执行什么操作,地址码告诉计算机到哪个存储单元地址中读取参与操作的数据。

16.程序

程序是一组计算机能识别和执行的指令,是若干指令或命令的集合,运行于电子计算机上,满足人们某种需求的信息化工具。

17.通信程序

利用通信协议编写的具有通信功能的程序

18.数据

程序文件中包含的 变量初值 和程序使用的 字面常量值 。

19.数据结构

相互之间存在一种或多种特定关系的数据元素的集合

20.缓冲区(缓存)

内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

21.变量

没有固定的值,可以改变的数;在程序运行时其值可以改变的量

22.函数

函数是指一段可以直接被另一段程序或代码引用的程序或代码。 也叫做子程序、(OOP中)方法。

23.子程序

是一个大型程序中的某部份代码,由一个或多个语句块组成。 它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。

所有的高级语言中都有子程序这个概念,用子程序实现模块的功能。 在 C语言 中,子程序是由一个主函数和若干个函数构成的

24.队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

25.进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是 操作系统 结构的基础。

进程是线程的容器。 程序是指令、数据及其组织形式的描述,进程是程序的实体。

一个程序就是一个进程,每个进程至少1个线程

26.线程

线程在程序中是独立的、并发的执行流。

进程仅负责为各个线程提供所需的资源,真正执行任务的是线程,而不是进程。

27.通信线程

27.并行和并发

并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。

28.阻塞/非阻塞

阻塞非阻塞指的是调用者(程序)在等待返回结果(或输入)时的状态。

  • 阻塞时,在调用结果返回前,当前线程会被挂起,并在得到结果之后返回。
  • 非阻塞时,如果不能立刻得到结果,则该调用者不会阻塞当前线程。因此对应非阻塞的情况,调用者需要定时轮询查看处理状态。

补充:同步与异步

同步和异步 关注的是消息通信机制 (synchronous communication/ asynchronous communication)。

所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。

换句话说,就是由调用者主动等待这个调用的结果。

而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

29.文件

文件是计算机文件属于文件的一种,与普通文件载体不同,计算机文件是以计算机硬盘为载体存储在计算机上的信息集合。 文件可以是文本文档、图片、程序等等。 文件通常具有三个字母的文件扩展名,用于指示文件类型

30.设备文件

设备文件是应用程序与驱动程序交互的接口

31.硬件接口

硬件接口(hardware interface)指的是两个硬件设备之间的连接方式。硬件接口既包括物理上的接口,还包括逻辑上的数据传送协议。

32.通信线路

通信线路是保证信息传递的通路。

目前长途干线中有线主要是用大芯数的光缆,另有卫星、微波等无线线路。 省际及省内长途也是以光缆为主,另有微波、卫星电路。

33.通信硬件接口

是指中央处理器和标准通信子系统之间的接口。

34.路由

路由(routing)是指分组从源到目的地时,决定端到端路径的网络范围的进程 [1] 。路由工作在OSI参考模型第三层——网络层数据包转发设备。

路由是指路由器从一个接口上收到数据包,根据数据包的目的地址进行定向并转发到另一个接口的过程。路由通常与桥接来对比,在粗心的人看来,它们似乎完成的是同样的事。它们的主要区别在于桥接发生在OSI参考模型的第二层(数据链路层),而路由发生在第三层(网络层)。

35.路由表

路由器又可以称之为网关设备。路由器(Router)是连接两个或多个网络的硬件设备,在网络间起网关的作用,是读取每一个数据包中的地址然后决定如何传送的专用智能性的网络设备。它能够理解不同的协议,例如某个局域网使用的以太网协议,因特网使用的TCP/IP协议

二、状态机

通过状态来决定某个任务是否进行,称为状态机程序设计。即 发生了某件事,要做什么 还得根据状态来决定。

下雨事件

状态:在室内、在室外

事件:雨停了、下雨了

动作:打球、出门、看书、打伞、回家

状态转移:相应的事件和状态下发生了状态的改变

状态机

掌握三点

事件/状态/转移

例,有一个文本文件需要传输,但接收方不知道文件长度。这是用你的学号后2位作为文件结束,请设计一个识别文件结束的状态机。比如学号是35

imgimg

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <stdio.h>

#define M 4 //事件个数
#define N 3 //状态个数

#define S1 0 //状态1 没有结束符
#define S2 1 //状态2 有3了
#define S3 2 //状态3 文件结束

#define EVENT_3 0 //事件1 出现数字3
#define EVENT_5 1 //事件2 出现数字5
#define EVENT_NOT3 2 //事件3 不是数字3
#define EVENT_NOT3NOT5 3 //事件4 不是数字3或者5

int save(char num); // 存入缓存
int logerror(char num); // 没有响应事件的行为
int getEvent(int event, char cc, int currentState); // 获取事件

// 定义一个矩阵表类型
typedef struct matrix{
int (*Action)(char num);
int nextState;
} matrix_t;

// 矩阵表
matrix_t Set[M][N] = {
{{save, S2}, {save, S2}},
{{logerror, S1}, {save, S3}},
{{save, S1}, {logerror, S2}},
{{logerror, S1}, {save, S1}},
};

int save(char num){
printf("here is save num : %c!\n", num);
return 0;
}

int logerror(char num){
printf("logerror\n");
}

int getEvent(int event, char cc, int currentState){
if (cc == '3')
event = EVENT_3;
else if (cc == '5')
event = EVENT_5;
else if (cc != '3' && currentState == S1)
event = EVENT_NOT3;
else if (cc != '3' && cc != '5' && currentState == S2)
event = EVENT_NOT3NOT5;
return event;
}

int main(int argc, char *argv[]){
int currentState = 0;
int EVENT = -1;
char cc = '3';
int i = 5;

while (i--){
cc = '3';
EVENT = getEvent(EVENT, cc, currentState);
printf("Now state is %d, event is %d, ", currentState+1, EVENT+1);
Set[EVENT][currentState].Action(cc);
currentState=Set[EVENT][currentState].nextState;

cc = '5';
EVENT = getEvent(EVENT, cc, currentState);
printf("Now state is %d, event is %d, ", currentState+1, EVENT+1);
Set[EVENT][currentState].Action(cc);
currentState=Set[EVENT][currentState].nextState;

if (currentState == S3){
printf("File received!");
break;
}
}
}

考报文的定义和封装

作用

MAVlink是无人机地面站和无人机之间的通信协议

心跳:飞机和地面通信的通信通路测试

飞机状态:各个部件的状态、位置

飞行任务:导航、路线等

控制命令:返航、作业执行

实时视频:视频数据

作业控制:农药播撒等

报文的定义和封装

类型定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define MAVLINK_MAX_PACKET_LEN	 // 帧总长 = 载荷字节 + 非载荷字节
(MAVLINK_MAX_PAYLOAD_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES)
#define MAVLINK_NUM_NON_PAYLOAD_BYTES  // 非载荷字节
(MAVLINK_NUM_HEADER_BYTES + MAVLINK_NUM_CHECKSUM_BYTES )
#define MAVLINK_NUM_HEADER_BYTES  // 非载荷字节 – 首部
(1 + MAVLINK_CORE_HEADER_LEN)

#define MAVLINK_CORE_HEADER_LEN 5
#define MAVLINK_NUM_CHECKSUM_BYTES 2
#define MAVLINK_MAX_PAYLOAD_LEN 255


typedef struct __mavlink_message {
uint16_t checksum; ///< sent at end of packet (最后发送)
uint8_t magic; ///< protocol magic marker 标记 (发送时第一字节)
uint8_t len; ///< Length of payload 载荷长度
uint8_t seq; ///< Sequence of packet 数据包的顺序
uint8_t sysid; ///< ID of message sender system/aircraft 消息发送方系统/飞机的ID
uint8_t compid; ///< ID of the message sender component 消息发送方组件的ID
uint8_t msgid; ///< ID of message in payload 载荷中消息的ID
uint64_t payload64[(MAVLINK_MAX_PAYLOAD_LEN + 9) / 8]; // 载荷,8字节为单位
} mavlink_message_t

心跳信息

心跳信息是mavlink消息的载荷数据,放在 Payload(~255) 中,占9个字节

报文类型:

1
2
3
4
5
6
7
8
typedef struct __mavlink_heartbeat_t {
uint32_t custom_mode; /* 标志位 */
uint8_t type; /* 飞行器类型 */
uint8_t autopilot; /* 自动驾驶类别 */
uint8_t base_mode /* 飞行器模式位图 */
uint8_t system_status; /* 飞行器状态 */
uint8_t mavlink_version;/* 版本 */
} mavlink_heartbeat_t

定义变量

类型 变量
消息类型 mavlink_message_t 消息变量定义 mavlink_message_t msg;
心跳类型 mavlink_heartbeat_t 心跳变量定义 mavlink_heartbeat_t heartbeat;

四、实验

实验1 LINUX串口通信程序设计 (第二讲)

LINUX串口设备通信接口程序,字串的接收、存储和转发

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
// lflserial.c

//接收串口数据
//串口接收,返回接收数
int serRev(int fd,char *buff,int len)
{
if (fd>0)
{
return(read(fd,buff,len));
}else
{
return(0);
}
}

//串口发送数据
//串口发送,返回发送数
int serSend(int fd,char *buff,int len)
{
if (fd>0)
{
return(write(fd,buff,len));
}else
{
return(0);
}
}

// 对外接口 Dev为设备
//打开COM:DEV:设备名,Speed:速度,DataBits:位数,StopBits:停止位,Parity:校验位
//例:"dev/ttyS1",115200,8,1,'N'
int serOpenCOM(char *DEV,int Speed,int DataBits,int StopBits,int Parity)
{
int fd;
fd = serOpenDev(DEV);

if (fd>0) {
if (serSetSpeed(fd,Speed)) {
if (serSetParity(fd,DataBits,StopBits,Parity))
return(fd);
return(0);
} else
return(0);
} else
return(0);
}

// 关闭串口
int serCloseCOM(int fd)
{
return(serCloseDev(fd));
}

发送

1
2
3
4
5
6
7
// tty_send_file.c

while (1) {
s_len = read(fd, buff, 1024);
if (s_len <= 0) break;
serSend(tty_fd, buff, s_len); //串口接收,返回接收数
}

接收

1
2
3
4
5
6
7
8
// tty_recv_file.c

char r_buffer[1024];
bzero(r_buffer, sizeof r_buffer);
while (1) { // 循环读取数据
r_len = serRev(r_fd, r_buffer, sizeof r_buffer); // 串口接收,返回接收数
write(rd, r_buffer, r_len);
}

实验2 报文封装与解析程序设计

设计一个报文格式,定义报文类型,封装一个报文,识别报文以及解析其内容

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// test-packet-struct-sizeof.c

#include <stdio.h>

// ★
typedef unsigned char u_int8_t; // 8位
typedef unsigned short u_short; // 16位
typedef unsigned int u_int32_t; // 32位
// ★
struct ip_addr {
u_int32_t IP_addr; //IP地址,占32位
};
// ★
struct in6_addr {
union {
u_int8_t Byte[16];
u_short Word[8];
} u; // 只要是128位都是对的
};

struct packet_t {
u_int8_t Ver:4;
u_int8_t Com:4;
u_int8_t ComArg;
u_short PacketDataLength;
struct ip_addr IPv4Address;
// u_int8_t PacketData[1]; // 不定义或定义为1个字节算对
};

typedef struct _PacketHdr_t
{
u_int32_t Ver:4;
u_int32_t Class:8;
u_int32_t FlowLabel:20;
u_short PayloadLength;
u_short Reverse;
struct in6_addr IPv6Address;
} PacketHdr_t;



struct ip_hdr
{
unsigned short int IP_v:4; // 版本号码
unsigned short int IP_hl:4; // 首部长度
u_int8_t IP_tos; // 服务类型
u_short IP_len; // 数据报文总长度
u_short IP_id; // 标识
u_short IP_off; // 分段偏移
u_int8_t IP_ttl; // 存活时间
u_int8_t IP_p; // 协议号码
u_short IP_sum; // 检验和
struct ip_addr IP_src, IP_dst;
// 源IP地址和目的IP地址
};

struct ip_hdr_c
{
u_int8_t IP_v:4; // 版本号码
u_int8_t IP_hl:4; // 首部长度
u_int8_t IP_tos; // 服务类型
u_short IP_len; // 数据报文总长度
u_short IP_id; // 标识
u_short IP_off; // 分段偏移
u_int8_t IP_ttl; // 存活时间
u_int8_t IP_p; // 协议号码
u_short IP_sum; // 检验和
struct ip_addr IP_src, IP_dst;
// 源IP地址和目的IP地址
};

struct ip_hdr_l
{
unsigned long IP_v:4; // 版本号码
unsigned long IP_hl:4; // 首部长度
u_int8_t IP_tos; // 服务类型
u_short IP_len; // 数据报文总长度
u_short IP_id; // 标识
u_short IP_off; // 分段偏移
u_int8_t IP_ttl; // 存活时间
u_int8_t IP_p; // 协议号码
u_short IP_sum; // 检验和
struct ip_addr IP_src, IP_dst;
// 源IP地址和目的IP地址
};

main()
{
PacketHdr_t Packet6;
struct packet_t Packet4;
struct ip_hdr IP_Head;
struct ip_hdr_c IP_Head_c;
struct ip_hdr_l IP_Head_l;

printf("\n");
printf("\nsizeof struct ip_addr = %d\n", sizeof (struct ip_addr)); // 24
printf("\nsizeof PacketHdr_t = %d\n", sizeof Packet6); // 24
printf("\nsizeof packet_t = %d\n", sizeof Packet4); // 8
printf("\nsizeof IP_Head_t = %d\n", sizeof IP_Head); // 20
printf("\nsizeof IP_Head_t_c = %d\n", sizeof IP_Head_c); // 20
printf("\nsizeof IP_Head_t_l = %d\n", sizeof IP_Head_l); // 20
}

实验3 简单的PPP程序设计 (第三讲)

长度帧与PPP帧设计与定义,报文接收识别、转发(设计队列与路由表)

1.长度帧 length-frame

帧格式:

帧定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 帧首类型
typedef struct frame_head {
char cFlag[2]; // 头标识 + 长度 = 帧头
short shortLength;
} t_frame_head;

// 帧类型
typedef struct frame {
t_frame_head frameHead;
char frameData[0]; // 数据
} t_frame;

#define MAX_DATA_LENGTH 1500 // 需要发送的数据总长
// 帧总长 = 帧头 + 数据长度
#define MAX_FRAME_LENGTH (sizeof (t_frame_head *) + MAX_DATA_LENGTH)

void length_framePack(t_frame *framePoint, char *dataP, int dataLength);
void length_frameUnpack(t_frame *framePoint, char *dataP, int *data_lengthP);
void length_frameRecognition(int fd, t_frame *framePoint);

报文收发主逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// length-frame-send-file.c
while (1) {
s_len = read(fd, buff, N);
if (s_len <= 0) break;
length_framePack((t_frame *)&frame[0], buff, s_len);
serSend(tty_fd, frame, s_len + sizeof (t_frame_head));
}

// length-frame-recv-file.c
while (1) {
length_frameRecognition(tty_fd, (t_frame *)&frame[0]);
length_frameUnpack((t_frame *)&frame[0], buff, &s_len); // 帧指针赋值
if (s_len <= 0) break;
write(fd, buff, s_len);
}

报文识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// length-frame-recognition.c

// 帧的识别
void length_frameRecognition(int fd, t_frame *framePoint)
{
char cc[2], tc;
short len;

again:
cc[0] = 0; cc[1] = 0;
while (cc[0] != 0x7e || cc[1] != 0x7e)
readFromUART(fd, &cc[0], 2);
readFromUART(fd, (char *)&len, 2);

if (len > MAX_DATA_LENGTH)
goto again;
// 阻塞式读取len长度的字节
readFromUART(fd, framePoint->frameData, len); // 读len字节
// 帧封装
framePoint->frameHead.cFlag[0] = 0x7e;
framePoint->frameHead.cFlag[1] = 0x7e; // 这两句意义不大了
framePoint->frameHead.shortLength = len;
}

帧封装与解封

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// length-frame-recognition.c

// 帧封装(发)
void length_framePack(t_frame *framePoint, char *dataP, int dataLength)
{
framePoint->frameHead.cFlag[0] = 0x7e;
framePoint->frameHead.cFlag[1] = 0x7e;
framePoint->frameHead.shortLength = dataLength;
memcpy(framePoint->frameData, dataP, dataLength);
}

// 帧解封(收)
void length_frameUnpack(t_frame *framePoint, char *dataP, int *data_lengthP)
{
register int l;
l = framePoint->frameHead.shortLength;
memcpy(dataP, framePoint->frameData, l);
*data_lengthP = l;
}

问题:从串口读多少个帧才知道是一个文件结束

可以有三个方法:

1)数据的最后增加0000或NNNN等特殊数值;

例:r_fd = serOpenCOM(argv[1], 115200, 8, 1, 'N'); ,其中字符“N”为Parity(校验位)。

2)在发整个数据前先发数据整个文件数据长度;

3)增加一个特殊帧,比如长度为0的帧。

如 len == 0 则结束 -> 封装长度为0的帧 -> 把帧frame送到串口文件 -> 结束


Tips: Please indicate the source and original author when reprinting or quoting this article.