非阻塞有限状态机的实现 C语言

非阻塞有限状态机的实现 C语言

非阻塞IO – 阻塞IO

IO多路转接

其他读写函数

存储映射IO

文件锁

非阻塞 IO

简单流程:如果一个程序的自然流程是结构化的,就是简单流程。(明确能分析结构流程的)

复杂流程:如果一个程序的自然流程不是结构化的,就是复杂流程。

有限状态机的实现方法:

任务:实现一个数据中继模型。(流式套接字)

​ - - -> 设备1 <——> 设备2 < - - -

目标:

a. 读设备1——写设备2——读设备2——写设备1

如果采用阻塞的方式,假如正在读设备1,且设备1上没有数据,程序就会一直阻塞在读设备1;假设设备2有数据产生,就会无法读设备2并像设备1中写入。这种方案的阻塞是无法实现功能的。

如果采用非阻塞方式,假如在读取某一个设备时没有数据,就会去尝试读取另一个设备,就不会产生上述问题。

a.读设备1——写设备2

b.读设备2——写设备1

这种采用两个线程分工,如果采用阻塞的方式是可以实现功能,但是效果很差。

这种采用两个线程分工,如果采用非阻塞的方式是可以实现功能。

原型:

1
2
Input
dev1
dev2
Input
  1. 读dev1写dev2
  2. 读dev2写dev1

状态机:

state
read
EX
end
write

实现

简单实现:relay.c

/******************************************************************************
 * @file relay.c
 * @brief 非阻塞有限状态机数据中继简单实现,可以通过状态机实现两个终端的数据同步
 * @author wangs7__
 * @date 2020/10/05
 * 
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define TTY1    "/dev/tty11"    //设备1
#define TTY2    "/dev/tty12"    //设备2
#define BUFSIZE 1024            //缓冲区大小

//状态图
enum{
    STATE_R = 1,    //写状态
    STATE_W ,       //读状态
    STATE_Ex,       //出错状态
    STATE_T         //退出态
};

//状态机结构体
struct fsm_st {
    int state;  //状态描述变量
    int sfd;    //源文件描述符
    int dfd;    //目标文件描述符
    int len;    //读取的字节数
    int pos;    //写指针

    char buf[BUFSIZE];  //读写缓冲区
    char *errstr;       //报错提示
};

/**
 * @name fsm_driver
 * @brief 状态机驱动函数,推动状态机运转
 * @param fsm 状态机结构体指针
 * @return void
*/
static void fsm_driver(struct fsm_st *fsm){
    
    int ret;    //写函数返回值

    //开关选择语句 状态机分支
    switch (fsm->state)
    {
        //R态-------------------------------------------------------------------
        case STATE_R:
            fsm->len = read(fsm->sfd, fsm->buf, BUFSIZE);//从源文件读取字符
            if(fsm->len == 0){  //读取文件为空 转T态
                fsm->state = STATE_T;
            }
            else if (fsm->len < 0){ //读取失败
                if(errno == EAGAIN){    //假错 保持R态
                    fsm->state = STATE_R;
                }
                else{                   //真错 转Ex态
                    fsm->errstr = "read()";
                    fsm->state = STATE_Ex;
                }
            }
            else{   //读成功 转W态
                fsm->pos = 0;   //写指针初始化
                fsm->state = STATE_W;
            }
            break;
        //W态-------------------------------------------------------------------
        case STATE_W:
            ret = write(fsm->dfd, fsm->buf+fsm->pos, fsm->len);//从缓冲区向目标文件写入
            if(ret < 0){    //写错误
                if(errno == EAGAIN){    //假错 保持写
                    fsm->state = STATE_W;
                }
                else{                   //真错 转Ex态
                    fsm->errstr = "write()";
                    fsm->state = STATE_Ex;
                }
            }
            else{           //写成功
                fsm->len -= ret;       //写剩余长度
                fsm->pos += ret;       //写指针位置更新
                if(fsm->len == 0){  //写完 转读态
                    fsm->state = STATE_R;
                }
                else{               //未写完 保持写
                    fsm->state = STATE_W;
                }
            }
            break; 
        //Ex态------------------------------------------------------------------       
        case STATE_Ex:
            perror(fsm->errstr);    //报错提示
            fsm->state = STATE_T;   //转T态
            break;
        //T态-------------------------------------------------------------------
        case STATE_T:
            /* do something */
            break;
        //---------------------------------------------------------------------
        default:
            abort();
            break;
    }
}

/**
 * @name relay
 * @brief 数据中继函数
 * @param fd1 设备1文件描述符
 * @param fd2 设备2文件描述符
 * @return void
*/
void relay(int fd1, int fd2){

    int fd1_save, fd2_save; //原始文件打开状态
    struct fsm_st fsm12, fsm21; //1->>2状态机 和 2->1状态机

    //添加非阻塞打开方式
    fd1_save = fcntl(fd1, F_GETFL);
    fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
    fd2_save = fcntl(fd2, F_GETFL);
    fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);

    //初始化状态机
    fsm12.state = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;
    fsm21.state = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;

    //状态机运转
    while (fsm12.state != STATE_T ||fsm21.state != STATE_T){
        fsm_driver(&fsm12);//驱动 1->2
        fsm_driver(&fsm21);//驱动 2->1
    }

    //回复文件原始打开状态
    fcntl(fd1, F_SETFL, fd1_save);
    fcntl(fd2, F_SETFL, fd2_save);

}


//主函数
int main(int argc, char *argv[]){

    int fd1, fd2;
    fd1 = open(TTY1, O_RDWR);
    /* 如果出错 */
    write(fd1, "TTY1\n", 5);
    fd2 = open(TTY2, O_RDWR | O_NONBLOCK);
    /* 如果出错 */
    write(fd2, "TTY2\n", 5);

    //数据中继驱动函数
    relay(fd1, fd2);

    close(fd1);
    close(fd2);

    exit(0);
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值