linux - 学习项目

一、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(&current_time);//获取当前时间的秒数
	local_tm = localtime(&current_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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值