有关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); //设置标准输入流为无缓存 

转载于:https://www.cnblogs.com/-colin/p/7987450.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值