《网络应用程序设计》——多用户交互性文字游戏

建议:理解好TCP,UDP两种协议的套接口编程,在拿到题目时,根据题目再选择一种方式,然后在代码上进行更改来满足题目要求。

上机测试部分(题目要求)
设计一个多用户交互性文字游戏,具体要求如下:
⑴ 实现单个用户进入游戏界面,并描述游戏中单一场景下的,当前状况。
欢迎XXX用户进入游戏大厅,大厅内设置2个任务(简单可交互性任务),请用户可以选择完成。用户可以选择完成相应任务,得到相应积分,能累计积分,并通报,通关说明界面。
⑵单用户下,设计多场景下,并能实现多场景的切换,可以实现多任务的完成,并累计积分。
⑶设计多用户协作任务,多用户进出游戏的情景通报,多用户积分更新通报。

(编程完成以后,要求课下写一份报告。)题目要求比较泛,也没有要求具体是什么形式的游戏,全看临场发挥以及个人想法,所以看不懂也没关系,主要要会灵活运用两种协议的套接口编程。以下只是我个人看到题目想到的idea,仅供参考。(当然你也可以有其他的想法。)

设计思路:
服务端里设置两个关卡,每个关卡有两个任务(每个任务的分值不同),用户第一次登录都是从第一关卡开始闯关,完成第一关卡才能够进入第二关卡,完成所有关卡则顺利通关。用户的标识是用户的ID(我们假设用户的ID不会重复),用户在进行游戏前都要先向服务器发出要玩游戏的请求(game命令),再输入自己的ID号,才可以进行游戏。服务器端会在游戏大厅通报进入和退出大厅的用户,并随时通告每个用户的得分情况。

实现方法:
服务器端的实现主要是采用复杂UDP并发服务器,为每个发出请求的用户创建一个子进程为其服务,父进程继续等待用户的请求数据到达。服务器端使用score数组来记录每个用户的总积分,每个用户的ID为数组的下标,方便服务器通报用户积分情况和用户查询自己的积分;使用task数组来标记每关中每个任务的完成情况,也可以避免用户重复答题,重复加分(0:未完成,1:已完成);使用cut数组来标记该用户的每关完成情况(0:未通关,1:已通关),全部为1,说明用户全部通关,即闯关成功。
多个字符串数组只是为了让用户清楚菜单以及自己的闯关结果。

程序流程图:
客户端与服务器端:

在这里插入图片描述
第一关的大致流程:
(第二关类似)
在这里插入图片描述
第一关循环:
(第二关类似)
(下图太长,截成两张了)
在这里插入图片描述
在这里插入图片描述
主要代码

客户端:(gamecli.c)
int main(int argc, const char *argv[])  
{  
    int sockfd;  
    int n;  
    char buf[1024];  
    struct sockaddr_in server_addr;  
    struct sockaddr_in peer_addr;  
    int addrlen = sizeof(struct sockaddr);  
    if(argc < 3){  
        fprintf(stderr,"Usage : %s ip port.\n",argv[0]);  
        exit(EXIT_FAILURE);  
    }  
    sockfd = socket(AF_INET,SOCK_DGRAM,0);  
    if(sockfd < 0){  
        perror("socket");  
    }  
    bzero(&server_addr,sizeof(server_addr));  
    server_addr.sin_family = AF_INET;  
    server_addr.sin_port = htons(atoi(argv[2]));  
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);  
    while(1)  
    {  
        printf(">");  
        fgets(buf,1024,stdin);  
        buf[strlen(buf)-1] = '\0';  
        //第一次输入game命令,向服务器说明你要玩游戏
        n = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&server_addr,addrlen);  
	    if(n < 0){  
            perror("recvfrom");  
        }
        n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&server_addr,&addrlen);  
        if(n < 0){  
            perror("recvfrom");  
        }  
        buf[n] = '\0';
		printf("%s\n",buf);  
       
  	    n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&server_addr,&addrlen);  
        if(n < 0){  
            perror("recvfrom");  
        }  
        buf[n] = '\0';
		printf("%s\n",buf);  
    }  
    return 0;  
} 
服务器端(gameser.c)
void handler(int signum)  
{  
    waitpid(-1,NULL,WNOHANG);  
    return ;
}  
time_t ticks; 
char tt[1024]; //专门用来显示用户进入游戏的时间
int score[1024];//记录每个用户的积分,数组下标是用户的ID
char welcome[]="welcome to the game world!";
char whatid[]="What is your ID?";
char request[]="enter your request:";
char commond[]="1:task1(10scores)\n2:task2(20scores)\n3:scores\n4:quit";
char commond2[]="1:task1(30scores)\n2:task2(40scores)\n3:scores\n4:quit";
char commond3[]="1:scores\n2:quit";
char answer[]="Please enter your answer";
char wrong[]="Wrong answer\nDon't be down-hearted.Try again!";
char right[]="Right answer\nChoose new options";

char task1[]="5*5=?";//25
char task2[]="3+5+8+1+20+11+22=?";//70
char task3[]="3*4*5*6=?";//360
char task4[]="3+5+8+1+20+11+22+11+22+22+33=?";//180

char already_finish[]="You have finished the task!Enter your commond again.";
int id;//当前用户ID
char buff[1024];
int do_client(struct sockaddr_in peer_addr,char *buf)  
{  
    int task[1024];//每一关卡的任务列表
	int cut[3];//关卡
	memset(cut,0,sizeof(cut));
    int n,i;  
    int sockfd;  
    int addrlen = sizeof(struct sockaddr);  
	int cmd;//收到的用户命令
	int ans;
    sockfd = socket(AF_INET,SOCK_DGRAM,0);  
    if(sockfd < 0){  
        perror("socket");  
    }  
    //发欢迎消息给客户端  
    sendto(sockfd,welcome,strlen(welcome),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
    //询问ID
	sendto(sockfd,whatid,strlen(whatid),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
	n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&peer_addr,&addrlen);  
    if(n < 0)
        perror("recvfrom");  
    buf[n] = '\0'; 
    id=atoi(buf);   /*将字符串转换为整型值。*/	
	printf("welcome the user :%s  ",buf); //客户发来自己的ID号
	ticks = time(NULL);
    snprintf(tt, sizeof(tt), "%.24s\r\n", ctime(&ticks));
    printf("time: %s\n",tt);//显示客户登录时间
	//关卡1
	memset(task,0,sizeof(task));
//第一关菜单(客户刚登上来都是第一关)
	sendto(sockfd,request,strlen(request),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  

	while(1){
        if(score[id]>=30)
		{
			printf("#user %d made the first cut!!!\n",id);//用户完成第一关
		    cut[1]=1;
			break;
		}	
		sendto(sockfd,commond,strlen(commond),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
		n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&peer_addr,&addrlen);  
        if(n < 0)
            perror("recvfrom");
        buf[n] = '\0'; 
		cmd=atoi(buf);
        if(cmd==1)
		{
			if(task[1]==0){	
		   sendto(sockfd,task1,strlen(task1),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
			   sendto(sockfd,answer,strlen(answer),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
			   n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&peer_addr,&addrlen);
			   ans=atoi(buf);
			   if(ans==25){//正确答案
				  task[1]=1;
		          score[id]+=10;
		          printf("user %d finished task1:+10 (total: %d)\n",id,score[id]); 
			 sendto(sockfd,right,strlen(right),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
				 
			   }
               else{
				 sendto(sockfd,wrong,strlen(wrong),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
				  
			  }				   
			}			   
		    else{//已完成这个任务
				sendto(sockfd,already_finish,strlen(already_finish),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr)); 
				
			}
			
		}
		else if(cmd==2)
		{
			if(task[2]==0){	
			   sendto(sockfd,task2,strlen(task2),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
		       sendto(sockfd,answer,strlen(answer),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
			   n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&peer_addr,&addrlen);
		        ans=atoi(buf);
			    if(ans==70){
				    task[2]=1;
				    score[id]+=20;
				 printf("user %d finished task2:+20 (total: %d)\n",id,score[id]);
                    sendto(sockfd,right,strlen(right),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
			        
		       }
			   else{
				   sendto(sockfd,wrong,strlen(wrong),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
			      
			   }
			}
			else {
				sendto(sockfd,already_finish,strlen(already_finish),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr)); 
			    
		    }
		}
		else if(cmd==3)//用户查询自己积分
		{
			int s=score[id];
			sprintf(buf,"%d",s);
			int len=strlen(buf);		
		    buf[len]='\0';
            sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
		    
		}
		else if(cmd==4)
			return 0;	
	}
	memset(task,0,sizeof(task));//关卡2任务
	//进入关卡2
	while(1){
        if(score[id]>=100)
		{
			printf("#user %d made the second cut!!!\n",id);//用户完成第2关
		    cut[2]=1;
			break;
		}	
		sendto(sockfd,commond2,strlen(commond2),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
		n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&peer_addr,&addrlen);  
        if(n < 0)
            perror("recvfrom");  
        buf[n] = '\0'; 
		cmd=atoi(buf);
        if(cmd==1)
		{
			if(task[1]==0){
			    sendto(sockfd,task3,strlen(task3),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
		        sendto(sockfd,answer,strlen(answer),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
				n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&peer_addr,&addrlen);
			    ans=atoi(buf);
			    if(ans==360){
				   score[id]+=30;
				   printf("user %d finished task1:+30 (total: %d)\n",id,score[id]);
				   task[1]=1;
				   sendto(sockfd,right,strlen(right),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
			       
			    }
				else{
				   sendto(sockfd,wrong,strlen(wrong),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
			      
			   }
			}
            else if(task[1]==1)//已经完成这个任务
			{
			   sendto(sockfd,already_finish,strlen(already_finish),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));
			  
			}
		}
		else if(cmd==2)
		{
			if(task[2]==0){
			    sendto(sockfd,task4,strlen(task4),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
		        sendto(sockfd,answer,strlen(answer),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
			    n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&peer_addr,&addrlen);
		        ans=atoi(buf);
			    if(ans==180){
				    score[id]+=40;
				 printf("user %d finished task2:+40 (total: %d)\n",id,score[id]);
                    task[2]=1;
					sendto(sockfd,right,strlen(right),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
			       
		        }
				else{
				   sendto(sockfd,wrong,strlen(wrong),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
			       
			   }
			}
			else if(task[2]==1)//已经完成这个任务
			{
		        sendto(sockfd,already_finish,strlen(already_finish),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));
			   
			}
		}
		else if(cmd==3)//用户查询自己积分
		{
			int s=score[id];
			sprintf(buf,"%d",s);
			int len=strlen(buf);		
		    buf[len]='\0';
            sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
		   
		}
		else if(cmd==4)
			return 0;	
	}
	if(cut[1]==1&&cut[2]==1)
        printf("#user %d finished all tasks!!!\n",id);
	while(1)//用户已完全通关,因为已完成的游戏不能重复玩,所以现在菜单只有退出和查询积分两个选项
	{
		sendto(sockfd,commond3,strlen(commond3),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
	    n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&peer_addr,&addrlen);  
        if(n < 0)
            perror("recvfrom");  
        buf[n] = '\0'; 
		cmd=atoi(buf);
		if(cmd==1)//用户查询自己积分
		{
			int s=score[id];
			sprintf(buf,"%d",s);
			int len=strlen(buf);		
		    buf[len]='\0';
            sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));  
		   
		}
		else if(cmd==2)
			return 0;
	}
    return 0;  
}  
//./server ip port  
int main(int argc, const char *argv[])  
{  
    memset(score,0,sizeof(score));
    int sockfd;  
    int n;  
    int pid;  
    char buf[1024];  
    struct sockaddr_in server_addr;  
    struct sockaddr_in peer_addr;  
    int addrlen = sizeof(struct sockaddr);  
if(argc < 3)
{  
        fprintf(stderr,"Usage : %s ip port.\n",argv[0]);  
        exit(EXIT_FAILURE);  
    }  
  
    if(signal(SIGCHLD,handler) == SIG_ERR)  {  
        perror("signal");  
    }
    sockfd = socket(AF_INET,SOCK_DGRAM,0);  
    if(sockfd < 0){  
        perror("socket");  
    }  
    int on = 1; 
    if((setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)  {  
	perror("setsockopt failed");
       exit(1);  
    }  
    //绑定地址  
    bzero(&server_addr,sizeof(server_addr));  
    server_addr.sin_family = AF_INET;  
    server_addr.sin_port   = htons(atoi(argv[2]));  
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);  
    if(bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)  
        perror("bind");  
    
	printf("Word game world\n");
    while(1)  
    {  
        n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&peer_addr,&addrlen);  
		if(n < 0){  
            perror("recvfrom ");  
        }         
        buf[n] = '\0';
        if(!strcmp(buf,"game"))//用户发出游戏请求
        {			
            pid = fork();  //创建一个子进程来为用户服务
            if(pid < 0)
{  
                perror("Fail to fork");  
                exit(EXIT_FAILURE);  
            }  
            if(pid == 0){  
                close(sockfd);  
                do_client(peer_addr,buf);  
				printf("user %d leave\n",id);
                exit(EXIT_SUCCESS);  
            }  
		}
		else{
		  continue;}//继续接受请求消息
    }  
    return 0; 
} 

运行和测试结果:
①单个用户:ID为1 的用户成功通关的截图:(以及对应的服务器端截图)
在这里插入图片描述在这里插入图片描述
服务器端:
在这里插入图片描述
用户查询自己的积分:
在这里插入图片描述
注:因为已完成的任务不能重复完成,此时该客户已全部通关,所以菜单只有两个选项。
上面都是回答正确的情况,下面给出用户回答错误的情况:
在这里插入图片描述
还有用户重复答题的情况:
在这里插入图片描述
②多个用户的情况:(服务器会及时通报用户进入游戏大厅以及用户加分情况)
在这里插入图片描述

分析结果:
如果用户答对,服务器就会给该客户加上相应的积分,并在游戏大厅进行通报。当用户通过第一关的时候,就会直接进入第二关。两关都完成的时候,闯关成功。用户若是答错,用户会卡在当前关卡,直到用户成功完成该关卡的所有任务。

发布了34 篇原创文章 · 获赞 51 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_39480875/article/details/87702899