一、背景
上一篇文章中我讨论了终端连接的属性,包括如何设置终端连接的属性以及如何编写终端属性修改命令stty。但并没有进行实践。这篇文章中将进行终端控制的应用实践。自己编写一个简单的ATM机密码输入界面。
二、密码输入界面的问题与解决(代码在文末)
1.初始版
这个密码输入界面一开始会先弹出一个欢迎的话语,然后要求客户输入密码,这里我设置的密码是123456,如果输入错误,则会显示提示话语sorry……如果输入正确,那么会显示很高兴为您服务。
这个版本之所以是初始版,是因为它非常不符合ATM机的需求。
此版本的问题在于:
(1)密码是暴露于光天化日之下的。
(2)当正确的密码输入完成后还必须跟上一个回车键。
(3)只要客户不输入密码,系统就一直处于这种状态,没有输入密码的时间限制。
针对这些问题,以下各个版本将会逐渐修正改进。
2.修复问题(1)
想要修复问题(1),必须对终端连接属性进行修改,使用tcgetattr与tcsetattr系统调用。将回显去掉。去掉回显:buff.c_lflag &=~ ECHO
如上所示,回显关掉了。
3.修复问题(2)
为修复问题(2),需要关闭终端连接属性中的缓冲功能,这样每输入一个字符也就不会在缓冲区存储了,如此最后也就不需要使用回车键来输入缓冲区中的字符了。字符会直接由终端进入程序中,但这样也就没有给客户修正密码输入的机会了,客户输入密码时必须小心些。关闭缓冲区:buff.c_lflag &=~ ICANON
为了看的清楚点,我把回显又打开了。这个问题与问题(1)是类似的,只是修改终端连接属性。
4.修复问题(3)
问题(3)在于目前的输入模式属于阻塞输入模式,当客户不能满足程序需求的字符数量时程序就一直处于阻塞状态等待客户继续输入。而阻塞不仅是终端连接的属性,并且也是文件连接的属性。需要使用系统调用fcntl来将终端设置为非阻塞输入模式。结果如下:
如上图所示是前两次输入错误,第三次输入正确的结果。
上图所示是第一次输入错误,客户在输入错误后就不再进行输入了。但过了10秒后程序自动结束。
5.一个简单的ATM密码输入界面
此密码输入界面修复了前面的三个问题。既没有回显,也不需输入回车键,且有超时自动关闭的功能。
三、实现的代码
1.原版代码
#include
#include
#include
#define ASK "WELCOME TO OUR BANK"
#define TRIES 2
int execute(char *question,int times);
int main()
{
int response;
response=execute(ASK,TRIES);
return response;
}
int execute(char *question,int times)
{
char *ch;
ch=malloc(100);
printf("%s \n"
"please enter the password.\n"
"you have (3) chance to enter it\n",question);
while(1){
fgets(ch,100,stdin);
ch[strlen(ch)-1]='\0';
if(strcmp(ch,"123456")==0){
printf("OK!I AM HAPPY SERVICE FOR YOU\n");
return 0;
}
else if(times==0)
return 1;
else{
printf("---------------------------\n"
"Sorry,your password is wrong\n"
"or you have not entered any password\n"
"you have (%d) chance to try again\n"
"-----------------------------\a\n",times--);
}
}
}
2.修复问题(1)
#include
#include
#include
#include
#define ASK "WELCOME TO OUR BANK"
#define TRIES 2
int execute(char *question,int times);
int main()
{
int response;
keep_mode(0);
ch_tty_mode();
response=execute(ASK,TRIES);
keep_mode(1);
return response;
}
int execute(char *question,int times)
{
char *ch;
ch=malloc(100);
printf("%s \n"
"please enter the password.\n"
"you have (3) chance to enter it\n",question);
while(1){
fgets(ch,100,stdin);
ch[strlen(ch)-1]='\0';
if(strcmp(ch,"123456")==0){
printf("OK!I AM HAPPY SERVICE FOR YOU\n");
return 0;
}
else if(times==0)
return 1;
else{
printf("\n---------------------------\n"
"Sorry,your password is wrong\n"
"or you have not entered any password\n"
"you have (%d) chance to try again\n"
"-----------------------------\a\n",times--);
}
}
}
ch_tty_mode()
{
struct termios tty_mode;
tcgetattr(0,&tty_mode);
tty_mode.c_lflag &=~ ECHO;
tcsetattr(0,TCSANOW,&tty_mode);
}
keep_mode(int num)
{
static struct termios keep_tty_mode;
if(num==0)
tcgetattr(0,&keep_tty_mode);
else
tcsetattr(0,TCSANOW,&keep_tty_mode);
}
3.修复问题(2)
#include
#include
#include
#include
#define ASK "WELCOME TO OUR BANK"
#define TRIES 2
int execute(char *question,int times);
int main()
{
int response;
keep_mode(0);
ch_tty_mode();
response=execute(ASK,TRIES);
keep_mode(1);
return response;
}
int execute(char *question,int times)
{
char *ch;
ch=malloc(100);
printf("%s \n"
"please enter the password.\n"
"you have (3) chance to enter it\n",question);
while(1){
fgets(ch,7,stdin);
if(strcmp(ch,"123456")==0){
printf("OK!I AM HAPPY SERVICE FOR YOU\n");
return 0;
}
else if(times==0)
return 1;
else{
printf("---------------------------\n"
"Sorry,your password is wrong\n"
"or you have not entered any password\n"
"you have (%d) chance to try again\n"
"-----------------------------\a\n",times--);
}
}
}
ch_tty_mode()
{
struct termios tty_mode;
tcgetattr(0,&tty_mode);
tty_mode.c_lflag &=~ ICANON;
tcsetattr(0,TCSANOW,&tty_mode);
}
keep_mode(int num)
{
static struct termios keep_tty_mode;
if(num==0)
tcgetattr(0,&keep_tty_mode);
else
tcsetattr(0,TCSANOW,&keep_tty_mode);
}
4.修复问题(3)
#include
#include
#include
#include
#define ASK "WELCOME TO OUR BANK"
#define TRIES 2
#define SLEEPTIME 5
int execute(char *question,int times);
int main()
{
int response;
keep_mode(0);
ch_ndelay();
response=execute(ASK,TRIES);
keep_mode(1);
return response;
}
int execute(char *question,int times)
{
char *ch;
ch=malloc(100);
printf("%s \n"
"please enter the password.\n"
"you have (3) chance to enter it\n",question);
while(1){
sleep(SLEEPTIME);
fgets(ch,8,stdin);
ch[strlen(ch)-1]='\0';
if(strcmp(ch,"123456")==0){
printf("OK!I AM HAPPY SERVICE FOR YOU\n");
return 0;
}
else if(times==0)
return 1;
else{
printf("---------------------------\n"
"Sorry,your password is wrong\n"
"or you have not entered any password\n"
"you have (%d) chance to try again\n"
"-----------------------------\a\n",times--);
}
}
}
ch_ndelay()
{
int re_flag;
re_flag=fcntl(0,F_GETFL);
re_flag |= O_NONBLOCK;
fcntl(0,F_SETFL,re_flag);
}
keep_mode(int num)
{
static int keep_file_mode;
if(num==0)
keep_file_mode=fcntl(0,F_GETFL);
else
fcntl(0,F_SETFL,keep_file_mode);
}
5.完整的代码
#include
#include
#include
#include
#include
#define ASK "WELCOME TO OUR BANK"
#define TRIES 2
#define SLEEPTIME 5
int execute(char *question,int times);
int main()
{
int response;
keep_mode(0);
ch_tty_mode();
ch_ndelay();
response=execute(ASK,TRIES);
keep_mode(1);
return response;
}
int execute(char *question,int times)
{
char *ch;
ch=malloc(100);
printf("%s \n"
"please enter the password.\n"
"you have (3) chance to enter it\n",question);
while(1){
sleep(SLEEPTIME);
fgets(ch,100,stdin);
if(strcmp(ch,"123456")==0){
printf("OK!I AM HAPPY SERVICE FOR YOU\n");
return 0;
}
else if(times==0)
return 1;
else{
printf("---------------------------\n"
"Sorry,your password is wrong\n"
"or you have not entered any password\n"
"you have (%d) chance to try again\n"
"-----------------------------\a\n",times--);
}
}
}
ch_tty_mode()
{
struct termios tty_mode;
tcgetattr(0,&tty_mode);
tty_mode.c_lflag &=~ ECHO;
tty_mode.c_lflag &=~ ICANON;
tcsetattr(0,TCSANOW,&tty_mode);
}
ch_ndelay()
{
int re_flag;
re_flag=fcntl(0,F_GETFL);
re_flag |= O_NONBLOCK;
fcntl(0,F_SETFL,re_flag);
}
keep_mode(int num)
{
static struct termios keep_tty_mode;
static int keep_file_mode;
if(num==0){
tcgetattr(0,&keep_tty_mode);
keep_file_mode=fcntl(0,F_GETFL);
}
else{
tcsetattr(0,TCSANOW,&keep_tty_mode);
fcntl(0,F_SETFL,keep_file_mode);
}
}