网站首页 > 博客文章 正文
状态机的简单应用
本文主要从三个方面来说状态机的应用
状态机的简单介绍
从项目需求上来说
结合代码来说
简单介绍
表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型,其实这些都是书面语,解释的已经很清楚了;通俗的来说状态机就是在各个状态之间循环,精髓是循环
项目需求
做的这个项目主要是视频采集相关的,主要分为三块儿内容,手机APP端,云端,设备端;当用户需要看视频时候可以通过手机APP点击按钮来查看想看的内容,比如用户需要看视频就需要点击拉视频流的按钮,进而发送命令到云端,云端经过处理然后把命令发给设备端,设备端执行相应的功能,由于我主要是做设备端这一块儿,我就从设备端这一块儿简短介绍一下主要需求
开机自启动,设备开机应该自动连接到云端(主要用到/etc/rc.local文件)
设备端应该能控制摄像头的一些动作(主要用到串口编程的知识,多线程编程)
当服务端出现异常应该保证设备端不会退出,当服务端恢复时应该能主动连接上(主要用状态机来实现)
远程更新(主要用到的是ssh反向端口转发知识)
流量监控(主要用到的是状态机,AT指令的知识)
下面以一张图来说明一下状态机
状态机
图画的有点儿乱,在这里解释一下,设备端程序主要分为四大部分,也就是四大状态
连接,注册登陆,处理命令,心跳;
连接到服务端,如果服务端出现异常设备端程序就会一直在这一部分循环直到服务端恢复正常
连接到服务端以后,就是登陆注册,设备端把用户名,密码发到服务端验证,如果验证不成功就接着验证,如果验证10次还没成功就返回到连接状态,如果成功就进行下一步处理命令
如果处理失败10次就返回到连接状态,处理完命令5秒没有再收到任何命令就进到心跳状态
如果设备端登陆成功后在5秒内没有收到服务端发过来的任何消息那么进到心跳状态
总之无论服务端有何异常断开的行为设备端都不会断开,一直下循环
结合代码
首先定义几个状态
enum{
START=0,
LOGGING=1,
PROCESSING=2,
HEARTBEAT=3
};
定义以后就是具体实现了,因为状态机的精髓就是循环,所以我们的状态都在一个while里面,这是第一个状态
while(1)
{
switch(state){
case START:
if(sockfd !=0)
close(sockfd);
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1) {
perror("sock");
state = START; //如果建立套接字错误返回到START状态
break;
}
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(atoi(argv[2]));
inet_pton(AF_INET,argv[1],&address.sin_addr);
len = sizeof(address);
if((result = connect(sockfd, (struct sockaddr *)&address, len))==-1) {
perror("connect");
state = START; //如果连接不上就返回到START状态
break;
}
state = LOGGING; //如果建立套接字,连接都正确就跳到下一登陆状态
state_logging_c=0;
break;
登陆状态
case LOGGING:
ret = login_c(sockfd);//1 stands for success
state_logging_c+=1;
if(ret < 0)//failure
{
if(state_logging_c > 10) //如果登陆十次都登录不上,那么就返回到START状态
state = START;
else
state = LOGGING;
break;
}
printf("LOGIN SUCESS!\n");
state = PROCESSING; //如果登陆成功就进入到下一个处理命令状态
state_processing_c = 0;
state_processing_fail = 0;
break;
处理命令的状态
case PROCESSING:
state_processing_c++;
FD_ZERO(&rfds);
FD_SET(0, &rfds);
maxfd = 0;
FD_SET(sockfd, &rfds);
if (sockfd > maxfd)
maxfd = sockfd;
tv.tv_sec = 5; //if server don't send any commnad to us within 5s, then we send a hearbeat to the server
tv.tv_usec = 0;
retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
if(retval == -1){
printf("select is error\n");
if(state_processing_c >10) //如果调用select错误十次,那么也返回到START状态
state = START;
else
state = PROCESSING;//如果在十次只能有成功的就接着返回到PROCESSING状态循环
break;
}
else if(retval ==0){ //retval=0说明超时,说明在5秒之内没有收到任何命令,那么就进入到心跳状态
state = HEARTBEAT;
break;
}
//now retval >0 ,we have command to dealwith
if (FD_ISSET(sockfd, &rfds))
{
bzero(buffer, MAXBUF+1);
// len = recv(sockfd, buffer, MAXBUF, 0);
len = recv(sockfd, buffer, sizeof(buffer),0);
printf("len = %d\n",len);
if (len > 0)
{
keepflag = 0;
//处理服务端发过来的命令上下左右
process_command(buffer,sockfd);
}
else
{
sleep(3);
state_processing_fail++;
if(state_processing_fail > 10) //如果命令处理十次都失败,那就返回到START状态重新连接
{
state=START;
printf("we switch to START state because we cannot receive message from server\n");
break;
}
printf("Failed to receive the message! \n");
}
}
心跳状态(心跳的作用就是当没有命令发送的时候确定服务端是否正常)
case HEARTBEAT:
len = send(sockfd, heartbeat, strlen(heartbeat), 0); //发送心跳包
if (len < 0)
{
printf("failed to send heartbeat !\n");//in this case, we also add keepflag by 1,to switch the state to START
}
else
{
printf("%d\n",keepflag);
printf("error = %d\n",errno);//use the debug
printf("heartbeat send!\n");
keepflag += 1;
if(keepflag > 10){ //发了十次心跳包服务端都没有回应,默认服务端已经挂了,就返回到START状态
state = START;
printf("we switch to START state because we sent 20 times heartbeat that but the server not sent us\n");
keepflag = 0;
break;
}
}
state = PROCESSING; //如果接收到命令就跳到处理命令状态
state_processing_c =0;
state_processing_fail =0;
break;
default:
state = START;
break;
}//switch state machine
}//while(1)
exit(0);
}
这是很简单的应用,代码没有难度,我就不一一说了,就把一些重要的注释了一下,感觉无论简单复杂,要有场景,在场景里学习是可以事半功倍的
猜你喜欢
- 2024-10-01 操作系统 : 按优先数调度算法实现处理器调度(C++)
- 2024-10-01 c++ 疑难杂症(13) allocator(c++ catch all exception)
- 2024-10-01 百度C++工程师的那些极限优化(内存篇)
- 2024-10-01 C++20 香不香?从四大新特性看起(c++20支持)
- 2024-10-01 用C++11打造智能观察者模式:详解实现步骤完整示例代码
- 2024-10-01 网络编程:手绘TCP状态机(tcp状态转换图详解)
- 2024-10-01 C++为什么不提倡使用单例模式?(c++为什么不用printf)
- 2024-10-01 智能系统机器人!C++实现三阶魔方自动求解程序源码
- 2024-10-01 ChaosBlade 发布对 C++ 应用混沌实验的支持
- 2024-10-01 C/C++编程笔记:C++智能指针及其类型的介绍!重点分析
你 发表评论:
欢迎- 07-07Xiaomi Enters SUV Market with YU7 Launch, Targeting Tesla with Bold Pricing and High-Tech Features
- 07-07Black Sesame Maps Expansion Into Robotics With New Edge AI Strategy
- 07-07Wuhan's 'Black Tech' Powers China's Cross-Border Push with Niche Electronics and Scientific Firepower
- 07-07Maven 干货 全篇共:28232 字。预计阅读时间:110 分钟。建议收藏!
- 07-07IT运维必会的30个工具(it运维工具软件)
- 07-07开源项目有你需要的吗?(开源项目什么意思)
- 07-07自动化测试早就跑起来了,为什么测试管理还像在走路?
- 07-07Cursor 最强竞争对手来了,专治复杂大项目,免费一个月
- 最近发表
-
- Xiaomi Enters SUV Market with YU7 Launch, Targeting Tesla with Bold Pricing and High-Tech Features
- Black Sesame Maps Expansion Into Robotics With New Edge AI Strategy
- Wuhan's 'Black Tech' Powers China's Cross-Border Push with Niche Electronics and Scientific Firepower
- Maven 干货 全篇共:28232 字。预计阅读时间:110 分钟。建议收藏!
- IT运维必会的30个工具(it运维工具软件)
- 开源项目有你需要的吗?(开源项目什么意思)
- 自动化测试早就跑起来了,为什么测试管理还像在走路?
- Cursor 最强竞争对手来了,专治复杂大项目,免费一个月
- Cursor 太贵?这套「Cline+OpenRouter+Deepseek+Trae」组合拳更香
- 为什么没人真的用好RAG,坑都在哪里? 谈谈RAG技术架构的演进方向
- 标签列表
-
- ifneq (61)
- 字符串长度在线 (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)