实现基于多线程的网络抢答器程序。
(1) 系统由1个服务器端和2个以上客户端组成;
(2) 事先准备多道简单题目,服务器随机出题,客户端进行抢答;
(3) 出题后5秒内如果无人抢答,自动进入下一题;
(4) 如果已有人抢答,则其他人再回答时,答案无效,并收到服务器的提示;
(5) 回答正确加分,错误减分,最后计算总成绩,并将结果发送给各客户端。
Linux端的(采用UOS+VScode+g++):
#include <iostream> #include <cstdlib> #include <cstring> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/epoll.h> #include<pthread.h> const int BUF_SIZE = 1024; const int EPOLL_SIZE = 50; using namespace std; struct questions { string question; string answer; bool condition; }; struct score { int fd=0; int score=0; }; int FdCount=0;//记录连接的客户端数 int TitleNum=0;//当然问的问题的题号 struct score fdm[BUF_SIZE];//记录连接的客户端fd //定义问题 struct questions que[10]; void count(int fd,int state);//计算得分 void* QuestionNotify(void*);//发送给所有客户端问题 int main() { cout<<"等待客户端连接..."<<endl; int server_sock, client_sock; sockaddr_in server_addr, client_addr; socklen_t addr_size; ssize_t str_len; int i; char buf[BUF_SIZE]; epoll_event *ep_events; epoll_event event; int epfd, event_cnt; //设置套接字相关属性 server_sock = socket(PF_INET, SOCK_STREAM, 0); memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(1234); /* 捆绑 sock 描述符 */ if (bind(server_sock, (sockaddr*)&server_addr, sizeof(server_addr)) == -1) { cout<<"捆绑出错!"<<strerror(errno)<<endl; exit(1); } /* 监听 sock 描述符 */ if (listen(server_sock, 5) == -1) { cout<<"监听出错!"<<strerror(errno)<<endl; } epfd = epoll_create(EPOLL_SIZE);// 创建监听红黑树 ep_events = (epoll_event*)malloc(sizeof(epoll_event) * EPOLL_SIZE); event.events = EPOLLIN;//设置 event.data.fd = server_sock; epoll_ctl(epfd, EPOLL_CTL_ADD, server_sock, &event);//添加监听fd while (1) { event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);// 监听 if (event_cnt == -1) { cout<<"监听出错!"<<endl; break; } for (int i = 0; i < event_cnt; ++i) { if (ep_events[i].data.fd == server_sock) { addr_size = sizeof(client_addr); client_sock = accept(server_sock, (sockaddr*)&client_addr, &addr_size); event.events = EPOLLIN; event.data.fd = client_sock; epoll_ctl(epfd, EPOLL_CTL_ADD, client_sock, &event);//添加监听fd cout<<"已连接客户端: "<<client_sock<<endl; fdm[FdCount++].fd=client_sock; if(FdCount==3){ //有3个客户端连接之后,启动线程发送问题 pthread_t tid; pthread_create(&tid,0,QuestionNotify,NULL); } else if(FdCount<3){ string buffer="参与答题人数不足三人,请稍等。"; send(client_sock,buffer.c_str(),strlen(buffer.c_str()),0); } else{ string buffer="答题已经开始,请赶紧参与答题。\n"; send(client_sock,buffer.c_str(),strlen(buffer.c_str()),0); } } else { str_len = read(ep_events[i].data.fd, buf, BUF_SIZE); if (str_len == 0) { epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);//添加监听fd close(ep_events[i].data.fd); cout<<"客户端退出: "<<ep_events[i].data.fd<<endl; } else { if (!strcmp(buf,que[TitleNum].answer.c_str())&&!que[TitleNum].condition) { string buffer="恭喜你,回答正确,加10分,请准备下一题吧^-^。\n"; que[TitleNum].condition=true; count(ep_events[i].data.fd,1); write(ep_events[i].data.fd, buffer.c_str(),strlen(buffer.c_str())); for(int m=0;m<FdCount;m++){ if (fdm[m].fd != 0){ buffer="已有人回答正确本题抢答结束,你目前分数为"+to_string(fdm[m].score)+"\n"; send(fdm[m].fd,buffer.c_str(),strlen(buffer.c_str()),0); } } } else if(!strcmp(buf,que[TitleNum].answer.c_str())&&que[TitleNum].condition){ string buffer="对不起抢答失败,请准备下一题吧^-^。\n"; write(ep_events[i].data.fd, buffer.c_str(),strlen(buffer.c_str())); } else{ string buffer="对不起,回答错误,减10分,最低0分,请准备下一题吧^-^。\n"; count(ep_events[i].data.fd,0); write(ep_events[i].data.fd, buffer.c_str(),strlen(buffer.c_str())); } cout<<ep_events[i].data.fd<<"回答:"<<buf<<endl; bzero(buf,sizeof(buf));//置字节字符串所有字节为零且包括'\0' } } } } close(server_sock); close(epfd); return 0; } void count(int fd,int state)//计算得分 { if(state==1){ for(int i=0;i<FdCount;i++){ if(fdm[i].fd==fd) fdm[i].score+=10;//每对一题加10分,共计一百分 } } else{ for(int i=0;i<FdCount;i++){ if(fdm[i].fd==fd&&fdm[i].score>0) fdm[i].score-=10;//每错一题减10分,最低0分 } } } void* QuestionNotify(void*)//发送所有问题 { string buffer="参与人数已达到3人,请准备答题。\n"; for(int m=0;m<FdCount;m++){ if (fdm[m].fd != 0){ send(fdm[m].fd,buffer.c_str(),strlen(buffer.c_str()),0); } } //定义问题 que[0].question="问题一:戈小戈长得帅吗(帅/不帅)?"; que[0].answer="帅"; que[1].question="问题二:网络编程老师长得帅吗(帅/不帅)?"; que[1].answer="帅"; que[2].question="问题三:戈小戈是哪个大学的?"; que[2].answer="内蒙古大学"; que[3].question="问题四:网络编程老师孩子长得可爱吗(可爱/不可爱)?"; que[3].answer="可爱"; que[4].question="问题五:戈小戈是哪个专业的(计科/软工)?"; que[4].answer="计科"; que[5].question="问题六:网络编程老师教得好吗(好/不好)?"; que[5].answer="好"; que[6].question="问题七:戈小戈是男生还是女生(男/女)?"; que[6].answer="男"; que[7].question="问题八:网络编程老师孩子是男孩还是女孩(男/女)?"; que[7].answer="男"; que[8].question="问题九:戈小戈来自哪里(安徽/江苏)?"; que[8].answer="安徽"; que[9].question="问题十:网络编程老师会给高分吗(会/不会)?"; que[9].answer="会"; for(int i=0;i<10;i++) que[i].condition=false; //发送问题 for(int i=0;i<10;i++){ sleep(2); TitleNum=rand()%10; for(int m=0;m<FdCount;m++){ if (fdm[m].fd != 0){ send(fdm[m].fd,que[TitleNum].question.c_str(),strlen(que[TitleNum].question.c_str()),0); } } clock_t start = clock(); while(1){ if(que[TitleNum].condition==true){ break; } clock_t end = (clock() - start)/CLOCKS_PER_SEC; if((int)end>10&&!que[TitleNum].condition)//判断答题是否超过5秒 { string buffer="所有人答题超时,请准备回答下一题。\n"; for(int n=0;n<FdCount;n++){ if (fdm[n].fd != 0){ send(fdm[n].fd,buffer.c_str(),strlen(buffer.c_str()),0); } } break; } } } for(int m=0;m<FdCount;m++){ if (fdm[m].fd != 0){ buffer="十道题已答完,你的总分数为"+to_string(fdm[m].score)+"\n"; send(fdm[m].fd,buffer.c_str(),strlen(buffer.c_str()),0); } } pthread_exit(NULL); } //g++ 网络编程作业8服务器端.cpp -o test -lpthread&&./test
#include <iostream> #include <cstdlib> #include <cstring> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include<pthread.h> const int BUF_SIZE = 1024; void* QuestionAnswer(void* sock);//回答问题 using namespace std; int main() { int sock; struct sockaddr_in server_addr; char message[BUF_SIZE]; // 发送的字符串长度、接收字符串的长度、每次read函数接受到字符串的长度 ssize_t str_len, recv_len, recv_cnt; sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) { cout<<"套接字错误"<<endl; } // 地址信息初始化 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; // IPV4 地址族 server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器IP地址 server_addr.sin_port = htons(1234); // 服务器端口号 // 向服务器发送连接请求 if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { cout<<"连接错误,无法连接到服务器"<<endl; } else { cout<<"已连接到服务器"<<endl; } while (1) { int nbytes=0; char mes[1024]; bzero(mes,sizeof(mes));//置字节字符串所有字节为零且包括'\0' nbytes=read( sock,mes,sizeof(mes)); if(nbytes>0) { // mes[nbytes]='\0'; cout<<"来自服务器的消息: "<<mes<<endl<<endl; } if(strstr(mes, "问题")!=NULL){ pthread_t tid; pthread_create(&tid,0,QuestionAnswer,&sock); } else if(strstr(mes, "答完")!=NULL) exit(0); } // 关闭连接 close(sock); return 0; } void* QuestionAnswer(void* sock)//回答问题 { int fd = *(int*)sock; char message[BUF_SIZE]; ssize_t str_len; cout<<"请输入答案( Q/q 退出 ): "<<endl; fgets(message, BUF_SIZE, stdin); // 如果输入q或者Q,则退出 if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) { // 关闭连接 close(fd); exit(0); } str_len = write(fd, message, strlen(message)-1); // 向服务器发送数据 bzero(message,sizeof(message));//置字节字符串所有字节为零且包括'\0' pthread_exit(NULL); return 0; } //g++ 网络编程作业8客户端.cpp -o test2 -lpthread&&./test2