一、flyappy bird游戏
1、基于链表操作、信号机制、Ncurses库,实现了穿越管道障碍的文本界面游戏。
2、在 linux 平台下,使用 Ncurses 库实现了文本界面游戏,主要为操作链表实现管道障碍的创建、移动,并使用信号机制实现小鸟定时下落、管道定时移动。
运行效果
bird.c
#include <stdio.h>
#include <curses.h>
#include <signal.h>
#include <sys/time.h>
#include <stdlib.h>
#define BIRD '@'
#define BLANK ' '
#define PIPE '+'
/*定义关于管道的结构体*/
typedef struct Pipe{
int x;//列坐标
int y;//横坐标
struct Pipe *next;
}Pipe_node, *Pipe_list;
Pipe_list head, tail;
void creat_list();//创建链表
void show_pipe();//显示管道
void clear_pipe();//清除管道
void move_pipe();//移动管道
int bird_y, bird_x;//小鸟坐标
void show_bird();//显示小鸟
void clear_bird();//清除小鸟
void move_bird();//移动小鸟
void init_curses();//curses库初始化
int set_timer(int ms_t);//设置定时器--ms
void handler(int sig);//信号处理函数
int main(int argc, const char *argv[])
{
bird_y = 15;//行
bird_x = 10;//列
init_curses();
signal(SIGALRM, handler);
set_timer(500);//500ms
srand(time(0));//随机种子
creat_list();
show_pipe();
show_bird();
move_bird();
return 0;
}
void init_curses()//curses库初始化
{
initscr();//进入curses模式
curs_set(0);//禁止光标显示
noecho();//禁止输入字符显示
keypad(stdscr,1);//启动功能按键
start_color();//启动颜色机制
init_pair(1,COLOR_WHITE, COLOR_RED);//小鸟颜色设置
init_pair(2,COLOR_WHITE, COLOR_GREEN);//管道颜色设置
}
int set_timer(int ms_t)//设置定时器--ms
{
struct itimerval timer;
long t_sec,t_usec;
int ret;
t_sec = ms_t / 1000; //s
t_usec = (ms_t % 1000) * 1000;//us
timer.it_value.tv_sec = t_sec;
timer.it_value.tv_usec = t_usec;//首次启动定时值
timer.it_interval.tv_sec = t_sec;
timer.it_interval.tv_usec = t_usec;//定时时间间隔
ret = setitimer(ITIMER_REAL, &timer, NULL);
return ret;
}
void handler(int sig)
{
Pipe_list p, new;
int i,j;
/*小鸟下落*/
clear_bird();
bird_y++;
show_bird();
/*游戏结束判断*/
if((char)inch() == PIPE)
{
set_timer(0);
endwin();
exit(1);
}
p = head->next;
if(p->x == 0)
{
head->next = p->next;
for(i = p->x; i < p->x+10; i++)
{
/*上半部分管道*/
for(j=0; j<p->y; j++)
{
move(j,i);
addch(BLANK);
}
/*下半部分管道创建*/
for(j = p->y+5; j < 25; j++)
{
move(j,i);
addch(BLANK);
}
refresh();
}
free(p);
new = (Pipe_list)malloc(sizeof(Pipe_node));
new->x = tail->x + 20;
new->y = rand() % 11 + 5;
new->next = NULL;
tail->next = new;
tail = new;
}
/*管道移动*/
clear_pipe();
move_pipe();
show_pipe();
}
void show_bird()//显示小鸟
{
attron(COLOR_PAIR(1));
move(bird_y,bird_x);
addch(BIRD);
refresh();
attroff(COLOR_PAIR(1));
}
void clear_bird()//清除小鸟
{
move(bird_y,bird_x);
addch(BLANK);
refresh();
}
void move_bird()//移动小鸟
{
char key;
while(1)
{
key = getch();
if(key == ' ')
{
clear_bird();
bird_y--;
show_bird();
/*游戏结束判断*/
if((char)inch() == PIPE)
{
set_timer(0);
endwin();
exit(1);
}
}
}
}
void creat_list()//创建链表
{
int i;
Pipe_list p, new;
head = (Pipe_list)malloc(sizeof(Pipe_node));
head->next = NULL;
p = head;
for(i = 0; i < 5; i++)
{
new = (Pipe_list)malloc(sizeof(Pipe_node));
new->x = (i + 1) * 20;
new->y = rand() % 11 + 5; // (5-15行)
new->next = NULL;
p->next = new;
p = new;
}
tail = p;
}
void show_pipe()//显示管道
{
Pipe_list p;
int i,j;
p = head->next;
attron(COLOR_PAIR(2));
while(p)
{
for(i = p->x; i < p->x+10; i++)
{
/*上半部分管道*/
for(j=0; j<p->y; j++)
{
move(j,i);
addch(PIPE);
}
/*下半部分管道创建*/
for(j = p->y+5; j < 25; j++)
{
move(j,i);
addch(PIPE);
}
}
refresh();
p = p->next;
}
attroff(COLOR_PAIR(2));
}
void clear_pipe()//清除管道
{
Pipe_list p;
int i,j;
p = head->next;
while(p)
{
for(i = p->x; i < p->x+10; i++)
{
/*上半部分管道*/
for(j=0; j<p->y; j++)
{
move(j,i);
addch(BLANK);
}
/*下半部分管道创建*/
for(j = p->y+5; j < 25; j++)
{
move(j,i);
addch(BLANK);
}
}
refresh();
p = p->next;
}
}
void move_pipe()//移动管道
{
Pipe_list p;
p = head->next;
while(p)
{
p->x--;
p = p->next;
}
}
二、交互功能
基于socket进程间通信,实现服务端、客户端
1、在搭建的框架中实现并发,实现多客户端的注册登录
2、实现管理员(用户名:root,密码:1)和普通用户区别对待, 注意权限控制
Makefile
all: server client
client : client.c
gcc client.c -o client
server : server.c
gcc server.c -o server -lsqlite3
clean :
rm server client
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <stdlib.h>
#define N 32
#define R 1 //user - register
#define L 2 //user - login
#define Q 3 //user - query
#define H 4 //user - history
/*定义通信双方的信息结构体*/
typedef struct{
int type;
char name[N];
char data[256];//password or word
int flag;//root用户或普通用户
}MSG;
int do_register(int serverfd,MSG *msg);
int do_login(int serverfd,MSG *msg);
int do_query(int serverfd,MSG *msg);
int do_history(int serverfd,MSG *msg);
// ./client <addr> <port>
int main(int argc, const char *argv[])
{
int serverfd;
struct sockaddr_in addr;
MSG msg;
int n;
/*验证输入*/
if(argc != 3){
printf("Usage:%s<addr><port>",argv[0]);
return -1;
}
/*创建套接字*/
if((serverfd = socket(AF_INET,SOCK_STREAM,0)) < 0){
perror("fail to socket");
return -1;
}
/*设置网络结构体*/
bzero(&addr,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
/*向服务端发起连接请求*/
if((connect(serverfd,(struct sockaddr *)&addr,sizeof(addr))) < 0){
perror("fail to connect");
return -1;
}
printf("connect success.\n");
/*登录前选项*/
while(1){
printf("***************************\n");
printf("1.register 2.login 3.quit\n");
printf("***************************\n");
printf("please choose:\n");
scanf("%d",&n);
switch(n){
case 1:
do_register(serverfd,&msg);
break;
case 2:
if(do_login(serverfd,&msg) == 1){
goto next;
}
break;
case 3:
close(serverfd);
exit(0);
break;
default:
printf("Invalid data cmd.\n");
}
}
/*登陆后选项*/
next:
while(1){
printf("**************************************\n");
printf("1.query_word 2.history_record 3.quit\n");
printf("**************************************\n");
printf("please choose:\n");
scanf("%d",&n);
switch(n){
case 1:
do_query(serverfd,&msg);
break;
case 2:
do_history(serverfd,&msg);
break;
case 3:
close(serverfd);
exit(0);
break;
default:
printf("Invalid data cmd.\n");
}
}
return 0;
}
int do_register(int serverfd,MSG *msg){
msg->type = R;
printf("Input name:\n");
scanf("%s",msg->name);
getchar();
printf("Input password:\n");
scanf("%s",msg->data);
getchar();
if(send(serverfd,msg,sizeof(MSG),0) < 0 ){
perror("fail to send");
return -1;
}
if(recv(serverfd,msg,sizeof(MSG),0) < 0){
perror("fail to recv");
return -1;
}
printf("%s\n",msg->data);
/*register ok. or usr name already exist.*/
return 1;
}
int do_login(int serverfd,MSG *msg){
msg->type = L;
printf("Input name:\n");
scanf("%s",msg->name);
getchar();
printf("Input password:\n");
scanf("%s",msg->data);
getchar();
if(strncmp(msg->name,"root",5)==0 && strncmp(msg->data,"1",2)==0){
printf("Administrator login in.\n");
msg->flag = 1;
}
if(send(serverfd,msg,sizeof(MSG),0) < 0 ){
perror("fail to send");
return -1;
}
if(recv(serverfd,msg,sizeof(MSG),0) < 0){
perror("fail to recv");
return -1;
}
if(strncmp(msg->data,"login success.",15) == 0){
printf("%s\n",msg->data);
return 1;
}else{
printf("%s\n",msg->data);
}
/*name or password wrong. or login success.*/
return 0;
}
int do_query(int serverfd,MSG *msg){
msg->type = Q;
puts("-------------");
while(1){
printf("Input word: (quit:'#')\n");
scanf("%s",msg->data);
getchar();
if(strncmp(msg->data,"#",1) == 0){
break;
}
if(send(serverfd,msg,sizeof(MSG),0) <0){
perror("fail to send");
return -1;
}
if(recv(serverfd,msg,sizeof(MSG),0) < 0){
perror("fail to recv");
return -1;
}
printf("%s\n",msg->data);
return 0;
}
return 0;
}
int do_history(int serverfd,MSG *msg){
msg->type = H;
if(send(serverfd,msg,sizeof(MSG),0) < 0){
perror("fail to send");
return -1;
}
//接受服务器传来的信息
while(1){
if(recv(serverfd,msg,sizeof(MSG),0) < 0){
perror("fail to recv");
return -1;
}
if(msg->data[0] == '\0'){
break;
}
printf("%s\n",msg->data);
}
return 0;
}
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sqlite3.h>
#include <string.h>
#include <time.h>
#define DATABASE "mydata.db"
#define N 32
#define R 1 //user - register
#define L 2 //user - login
#define Q 3 //user - query
#define H 4 //user - history
/*定义通信双方的信息结构体*/
typedef struct{
int type;
char name[N];
char data[256];//password or word
int flag;//root用户或普通用户
}MSG;
int do_client(int acceptfd,sqlite3 *db);
void do_register(int acceptfd,MSG *msg,sqlite3 *db);
int do_login(int acceptfd,MSG *msg,sqlite3 *db);
int do_query(int acceptfd,MSG *msg,sqlite3 *db);
int do_history(int acceptfd,MSG *msg,sqlite3 *db);
int get_date(char *date);
int history_callback(void *arg,int ncolumns,char **f_value,char **f_name);
int do_searchword(int acceptfd,MSG *msg,char word[]);
// ./server <addr> <port>
int main(int argc, const char *argv[])
{
sqlite3 *db;
char *errmsg;
char sql[128];
int clientfd;
struct sockaddr_in addr;
int n;
int acceptfd;
pid_t pid;
/*打开数据库*/
if((sqlite3_open(DATABASE,&db)) != SQLITE_OK){
printf("%s\n",errmsg);
return -1;
}else{
printf("open DATABASE success.\n");
}
//mydata : name,password
sprintf(sql,"create table if not exists mydata \
(name char primary key,data char);");
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){
printf("errmsg");
return -1;
}else{
printf("create table mydata success.\n");
}
//record : name,date,history data
sprintf(sql,"create table if not exists record \
(name char,date char,data char);");
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){
printf("errmsg");
return -1;
}else{
printf("create table record success.\n");
}
/*验证输入*/
if(argc != 3){
printf("Usage:%s<addr><port>",argv[0]);
return -1;
}
/*创建套接字*/
if((clientfd = socket(AF_INET,SOCK_STREAM,0)) < 0){
perror("fail to socket");
return -1;
}
/*设置网络结构体*/
bzero(&addr,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
/*绑定通信结构体*/
if((bind(clientfd,(struct sockaddr *)&addr,sizeof(addr))) < 0){
perror("fail to bind");
return -1;
}
/*设置套接字为监听模式*/
if(listen(clientfd,5) < 0){
perror("fail to listen");
return -1;
}
/*处理僵尸进程*/
signal(SIGCHLD,SIG_IGN);
/*接受连接请求*/
while(1){
if((acceptfd = accept(clientfd,NULL,NULL)) < 0){
perror("fail to accept");
return -1;
}else{
printf("connect success.\n");
}
if((pid = fork()) < 0){
perror("fail to fork");
return -1;
}else if(pid == 0){//子进程,处客户端具体的请求
//printf("child process\n");
close(clientfd);
do_client(acceptfd,db);
}else{//父进程,接收客户端的请求
close(acceptfd);
}
}
return 0;
}
int do_client(int acceptfd,sqlite3 *db){
MSG msg;
while(recv(acceptfd,&msg,sizeof(MSG),0) > 0){
//printf("recv success.\n");
printf("type:%d\n",msg.type);
switch(msg.type){
case R:
do_register(acceptfd,&msg,db);
break;
case L:
do_login(acceptfd,&msg,db);
break;
case Q:
do_query(acceptfd,&msg,db);
break;
case H:
do_history(acceptfd,&msg,db);
break;
default:
printf("Invalid data cmd.\n");
}
}
printf("client exit.\n");
close(acceptfd);
exit(0);
return 0;
}
void do_register(int acceptfd,MSG *msg,sqlite3 *db){
char sql[128] = {};
char *errmsg;
sprintf(sql,"insert into mydata values('%s','%s');",msg->name,msg->data);
printf("%s\n",sql);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){
//printf("%s\n",errmsg);
strcpy(msg->data,"usr name already exist.");
}else{
strcpy(msg->data,"register ok.");
}
send(acceptfd,msg,sizeof(MSG),0);
return ;
}
int do_login(int acceptfd,MSG *msg,sqlite3 *db){
char sql[128] = {};
char **result;
int nrow;
int ncolumn;
char *errmsg;
//管理员登录
if(msg->flag == 1){
strcpy(msg->data,"login success.");
send(acceptfd,msg,sizeof(MSG),0);
}
/* if(msg->name == "root" && msg->data == 1){
strcpy(msg->data,"root login success.");
send(acceptfd,msg,sizeof(MSG),0);
}
*/
//printf("login ...\n");
sprintf(sql,"select * from mydata where name = '%s' and data = '%s';",msg->name,msg->data);
if(sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg) != SQLITE_OK){
perror("errmsg");
return -1;
}else{
printf("select table success.\n");
}
if(nrow == 0){
strcpy(msg->data,"name or password wrong.");
send(acceptfd,msg,sizeof(MSG),0);
}
if(nrow == 1){
strcpy(msg->data,"login success.");
send(acceptfd,msg,sizeof(MSG),0);
}
return 0;
}
int do_searchword(int acceptfd,MSG *msg,char word[]){
FILE *fp;
int len = 0;
char temp[512] = {};
int result;
char *p;
if((fp = fopen("dict.txt","r")) == NULL){
perror("fail to fopen");
strcpy(msg->data,"fail to open dict.txt");
send(acceptfd,msg,sizeof(MSG),0);
return -1;
}else{
printf("open dict.txt success.\n");
}
//打印出客户端要查询的单词
len = strlen(word);
printf("%s,len = %d\n",word,len);
//读文件,来查询单词
while(fgets(temp,512,fp) != NULL){
result = strncmp(temp,word,len);
if(result < 0){
continue;
}
if(result > 0 || ((result == 0)&&(temp[len] != ' '))){
break;
}
//此时result=0,且temp[len]=' ',表明相等
p = temp + len;
while(*p == ' '){
p++;//一直找到单词后面的解释
}
//找到了注释,跳跃所有空格
strcpy(msg->data,p);
printf("found word:%s\n",msg->data);
//注释拷贝完后,应该关闭文件
fclose(fp);
return 1;
}
fclose(fp);
return 0;
}
int do_query(int acceptfd,MSG *msg,sqlite3 *db){
char word[64];
int found = 0;
char sql[128] = {};
char date[128] = {};
char *errmsg;
//拿出msg结构体中的单词
strcpy(word,msg->data);
found = do_searchword(acceptfd,msg,word);
printf("查询单词中...\n");
//表示找到了单词,此时将用户名,时间,单词插入到历史记录表中
if(found == 1){
printf("查询到一个单词.\n");
get_date(date);
sprintf(sql,"insert into record values('%s','%s','%s');",msg->name,date,word);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){
printf("%s\n",errmsg);\
return -1;
}else{
printf("Insert record done.\n");
}
}else{//没有找到
strcpy(msg->data,"Not found.");
}
send(acceptfd,msg,sizeof(MSG),0);
return 0;
}
int do_history(int acceptfd,MSG *msg,sqlite3 *db){
char sql[128] = {};
char *errmsg;
//root用户查看全部历史记录,普通用户查询自己的历史记录
if(msg->flag == 1){
sprintf(sql,"select * from record ");
}else if(msg->flag != 1){
sprintf(sql,"select * from record where name = '%s';",msg->name);
}
if(sqlite3_exec(db,sql,history_callback,(void *)&acceptfd,&errmsg) !=SQLITE_OK){
printf("%s\n",errmsg);
}else{
printf("query record done.\n");
}
msg->data[0] = '\0';
send(acceptfd,msg,sizeof(MSG),0);
return 0;
}
int get_date(char *date){
time_t current_time;
struct tm *local_tm;
time(¤t_time);//获取当前时间的秒数
local_tm = localtime(¤t_time);//进行时间格式转换
sprintf(date,"%d-%d-%d %d:%d:%d",local_tm->tm_year+1900,local_tm->tm_mon+1,\
local_tm->tm_mday,local_tm->tm_hour,local_tm->tm_min,local_tm->tm_sec);
printf("get date:%s\n",date);
return 0;
}
/*得到查询结果,并且将历史记录发送给客户端*/
int history_callback(void *arg,int ncolumns,char **f_value,char **f_name){
//record,name,date,word
MSG msg;
int acceptfd;
acceptfd = *((int *)arg);
sprintf(msg.data,"%s,%s,%s",f_value[0],f_value[1],f_value[2]);
send(acceptfd,&msg,sizeof(MSG),0);
return 0;
}