zoukankan      html  css  js  c++  java
  • 有关fgets和fcntl的讨论-待整理更新

    问题引出

    一个client程序:select 超时监听 sockfd套接字 和 STDIN_FILENO标准输入:若sockfd可读则接收server报文;若标准输入可读(按下回车),则开始用fgets/fscanf等函数从标准输入捕获字符后发送给server;若select 2秒超时,client发送一次心跳包给server。要求不能开其他的进程或线程,也不能使用定时器和信号,即单线程client。

            现在问题来了,在使用fgets/fscanf时,会阻塞select函数,这样一来client就无法进入超时流程发送心跳包;若将标准输入设置为非阻塞,那么fgets/fscanf立即返回,捕获的内容为空,达不到要求。
            请问,有没有一种方法能让程序一边等待fgets/fscanf的捕获,一边循环非阻塞执行select函数?


    尝试方法

    Method1

    用过linux下实现的kbhit,若没有按键输入则跳出这个函数,有输入则getchar到一个数组中存放然后退出函数,若捕获到回车键再把数组内容填充到发送报文。这样就是一个非阻塞的输入函数。
            case1:select还是监听标准输入,只有按下回车键,程序才可能进入select的标准输入可读流程。这样要想输入一个字符必须先敲一个回车,显然不行。
            case2:select不监听标准输入,在循环中,先执行select,再执行kbhit。这种方式能行,但是响应太慢,因为select是2秒超发心跳,所以select是半阻塞的,也就是说要想执行kbhit,必须等待2秒。

    最终放弃此方法。

    1. //示例代码  
    1. #include <stdio.h>  
    2. #include <fcntl.h>  
    3. #include <termios.h>  
    4.   
    5.   
    6. int kbhit(void) //return 0: no keyboard hit; return 1: keyboard hit  
    7. {  
    8.     struct termios oldt, newt;  
    9.     int ch;  
    10.     int oldf;  
    11.     tcgetattr(STDIN_FILENO, &oldt);  
    12.     newt = oldt;  
    13.     newt.c_lflag &= ~(ICANON | ECHO);  
    14.     tcsetattr(STDIN_FILENO, TCSANOW, &newt);  
    15.     oldf = fcntl(STDIN_FILENO, F_GETFL, 0);  
    16.     fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);  
    17.     ch = getchar();  
    18.     tcsetattr(STDIN_FILENO, TCSANOW, &oldt);  
    19.     fcntl(STDIN_FILENO, F_SETFL, oldf);  
    20.     if(ch != EOF)  
    21.     {  
    22.         ungetc(ch, stdin);  
    23.         return 1;  
    24.     }  
    25.     return 0;  
    26. }  
    27.   
    28.   
    29. int main()  
    30. {  
    31.     unsigned char line[128] = {0};  
    32.     unsigned char ch;  
    33.     unsigned int i = 0;  
    34.   
    35.     while(1)  
    36.     {  
    37.         task_1(); //其它任务  
    38.   
    39.         if(kbhit()) { //有键按下  
    40.             if((ch = getchar() != 0x0a) { //回车键没按下  
    41.                 line[i++] = ch;  
    42.             } else { //回车键按下  
    43.                 memset(line, 0, 128);  
    44.                 i = 0;  
    45.                 task_2(); //此处解析line数组内容  
    46.             }  
    47.         }  
    48.     }   
    49.   
    50.     return 0;  
    51. }  

    Method2

    在进行select循环之前就把要输入的内容提前录入,然后回车。之后程序进入到select的标准输入可读流程,在这里使用fgets将之前录入的内容全部读入一个buf[],然后解析字符串,填入到发送报文;即只要不输入回车键,程序就无法进入select的标准输入可读流程,自然不会影响2秒超时发保活包。

    这种方法实验效果能达到题目要求。


    Method3

    fgets为行缓存IO函数,遇到回车键才把处理缓冲中的数据流。将标准输入流stdin改为无缓冲方式,这样只要有字符敲入(即使没有敲入回车)select就会监听到标准输入可读。在相应流程中使用getchar()将捕获的输入存放于buf[],然后退出流程继续监听。若捕获到0x0a,表明输入的结束,之后同方法2一样解析和发送。

    这种方法实验效果也能达到题目要求。

      1. setvbuf(stdin, NULL, _IONBF, 0); //设置标准输入流为无缓存 
  • 相关阅读:
    html页面自适应宽度
    Angularjs Select获取数组下标
    spring boot集成mybatis分页插件
    百度分享实现https
    nginx自定义错误页面
    nginx代理tomcat
    Nginx配置SSL证书
    Nginx安装
    xmlns:amq="http://activemq.apache.org/schema/core"报错
    ActiveMQ
  • 原文地址:https://www.cnblogs.com/-colin/p/7987450.html
Copyright © 2011-2022 走看看