进程通信与socket编程实践之猜数字小游戏

socket是实现进程通信的一种重要方式,本文将通过socket编程实现服务器进程与客户端进程之间的通信,并在通信之外实现猜数字的小游戏。

1. 设计思路

本文设计的C/S结构的猜数字游戏功能如下:服务器端自动生成一个1-100之间的随机数字,用户在客户端根据提示输入所猜的数字,若用户猜的数字和服务器所生成的数字相同,则游戏通关,用户可选择是否进入下一轮。当用户输入负数时,退出游戏。

设计要点如下:

  1. 服务端通信部分: 为实现客户端与服务器进程的相互通信,在服务端,首先创建一个socket,设置socket属性,并调用函数bind()绑定IP地址、端口等信息;然后调用函数listen()开启监听,调用函数accept()阻塞,等待客户端的连接请求;接下来,调用函数recv()send()收发数据;最后关闭网络连接和监听。

  2. 客户端通信部分: 在客户端,也是先调用函数socket()创建一个socket,设置socket属性;然后调用函数connect()连接服务器;调用函数recv()send()收发数据,最后关闭网络连接。服务端与客户端的通信过程如下图所示。
    服务器与客户端的通信过程

  3. 服务端处理方式: 服务端采用多线程的方式处理客户端的请求。主线程每收到一个来自客户端的TCP连接请求,就调用pthread_create()函数创建一个子线程与之对应连接。一个线程处理一个客户端,从而实现了一个服务器与多个客户端之间的通信。

  4. 猜数字功能设计: server生成一个1到100之间的随机数,client去猜。client把每次猜的数字发给server。server收到后,会把猜的数字偏大、偏小、或是猜对的提示发给client。如果client不想继续猜了,发给server一个负数,则退出游戏。否则,一直到client猜对数字,用户选择不再进行下一轮,游戏结束。

2. 程序代码

2.1 服务器端server.c

服务器端代码server.c的具体实现如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <errno.h>  
#include <string.h>  
#include <sys/types.h>  
#include <netinet/in.h>  
#include <sys/socket.h>  
#include <sys/wait.h>  
#include <arpa/inet.h>
#include <pthread.h>
#include <time.h>#define PORT 1500//端口号   
#define BACKLOG 5/*最大监听数*/   
#define MAX_DATA 1024//接收到的数据最大长度  /**判断client猜的数字answer和server想的数字num是否相等*若相等,返回1;不相等,则返回0*/
int isEqual(int c, int answer, int num) {if (answer < num) {send(c, "Too small! Please guess again:", MAX_DATA, 0);printf("Thinking: %d is too small!\n", answer);return 0;} else if (answer > num) {send(c, "Too big! Please guess again:", MAX_DATA, 0);printf("Thinking: %d is too big!\n", answer);return 0;}else if (answer == num) {send(c, "Bingo!", MAX_DATA, 0);printf("Thinking: Bingo, %d is the right answer!\n", answer);return 1;} 
}void guess_num(int *arg) {int c = *arg;char myrecv[MAX_DATA];  //储存收到的信息char mysend[MAX_DATA];  //储存发出的信息srand((int)time(NULL));int num = rand() % 100 + 1;  //server想的数字为[1,100)的随机数send(c, "I'm thinking a number between 1 and 100! Guess what I think?", MAX_DATA, 0);int bingo = 0;  //bingo标志是否猜对,若猜对,bingo=0;否则,bingo=1while (bingo == 0) {memset(myrecv, '\0', sizeof(myrecv));memset(mysend, '\0', sizeof(mysend));recv(c, myrecv, MAX_DATA, 0);printf("The client guesses %s\n", myrecv);int answer = atoi(myrecv);if (answer < 0) {  //若client输入了一个负数,则退出游戏send(c, "Exit game...", MAX_DATA, 0);printf("Sent: Exit game...\n");break;}bingo = isEqual(c, answer, num);}pthread_exit(NULL);
}int main() {  int sockfd, new_fd;/*socket句柄和建立连接后的句柄*/  struct sockaddr_in my_addr;/*本方地址信息结构体,下面有具体的属性赋值*/  struct sockaddr_in their_addr;/*对方地址信息*/  int sin_size;  sockfd = socket(AF_INET,SOCK_STREAM,0);//建立socket   if (sockfd == -1) {  printf("socket failed:%d", errno);  return -1;  }  my_addr.sin_family = AF_INET;/*该属性表示接收本机或其他机器传输*/  my_addr.sin_port = htons(PORT);/*端口号*/  my_addr.sin_addr.s_addr = htonl(INADDR_ANY);/*IP,括号内容表示本机IP*/  bzero(&(my_addr.sin_zero), 8);/*将其他属性置0*/  if (bind(sockfd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) < 0) {//绑定地址结构体和socket  printf("bind error");  return -1;  }  printf("bind OK\n");listen(sockfd, BACKLOG);//开启监听 ,第二个参数是最大监听数  printf("listening\n"); while (1) {  sin_size = sizeof(struct sockaddr_in);  new_fd = accept(sockfd, (struct sockaddr*)&their_addr, &sin_size);//在这里阻塞知道接收到消息,参数分别是socket句柄,接收到的地址信息以及大小   if (new_fd == -1) {  continue;  } else {printf("accept: c = %d, ip = %s, port = %d\n", new_fd, inet_ntoa(my_addr.sin_addr), ntohs(my_addr.sin_port));send(new_fd, "Connect sucess\n", MAX_DATA, 0);//发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)pthread_t id;  //新建线程,可连接多个客户端pthread_create(&id, NULL, (void *)guess_num, &new_fd);   }}  close(sockfd);return 0;  
}  

2.2 客户端client.c

客户端代码client.c的具体实现如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <errno.h>  
#include <string.h>  
#include <sys/types.h>  
#include <netinet/in.h>  
#include <sys/socket.h>  
#include <sys/wait.h>  
#include <pthread.h>#define DEST_PORT 1500//目标地址端口号   
#define DEST_IP "127.0.0.1"/*目标地址IP,这里设为本机*/   
#define MAX_DATA 1024//接收到的数据最大长度   int main() {  int sockfd, new_fd;/*cocket句柄和接受到连接后的句柄 */  struct sockaddr_in dest_addr;/*目标地址信息*/  char myrecv[MAX_DATA];//储存接收数据char mysend[MAX_DATA];//储存发送数据   sockfd = socket(AF_INET,SOCK_STREAM,0);/*建立socket*/  if (sockfd==-1) {  printf("socket failed:%d",errno);  }  //参数意义见上面服务器端   dest_addr.sin_family = AF_INET;  dest_addr.sin_port = htons(DEST_PORT);  dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);  bzero(&(dest_addr.sin_zero), 8);  if (connect(sockfd, (struct sockaddr*)&dest_addr, sizeof(struct sockaddr)) == -1) {//连接方法,传入句柄,目标地址和大小   printf("connect failed:%d",errno);//失败时可以打印errno   } else {   memset(myrecv, '\0', sizeof(myrecv));recv(sockfd,myrecv,MAX_DATA,0);  //接收"Connect sucess"printf("Received: %s\n", myrecv); recv(sockfd,myrecv,MAX_DATA,0);  //接收"I'm thinking a number ..."printf("Received: %s\n", myrecv); while (1) {memset(mysend, '\0', sizeof(mysend));scanf("%s", mysend);send(sockfd, mysend, MAX_DATA, 0);memset(myrecv, '\0', sizeof(myrecv));recv(sockfd, myrecv, MAX_DATA, 0); if(strcmp(myrecv, "Bingo!") == 0 ||strcmp(myrecv, "Exit game...") == 0) {printf("%s\n", myrecv);break;}printf("Received: %s\n", myrecv);} }  close(sockfd);//关闭socket   return 0;  
}   

3. 运行结果

  1. 若可执行文件未生成,首先使用gcc server.c -lpthread -o servergcc client.c -lpthread -o client命令生成可执行文件serverclient;若可执行文件已存在,转步骤2。(注意: 由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数。)

  2. 使用./server命令运行服务端程序,如下图所示。
    在这里插入图片描述

  3. 使用./client命令运行客户端程序。启动3个客户端,均显示已和服务器连接成功,如下图所示。
    在这里插入图片描述

  4. 用户根据提示(“Too small”/“Too big”)在客户端重复输入所猜数字,直至猜对,屏幕输出“Bingo”。若用户一直未猜对,输入任意负数即可退出游戏,客户端程序结束。如下图所示。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/645108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

100T数据存进服务器分几步?

大家好&#xff0c;我是豆小匠。 这期来聊聊数据存储相关的问题&#xff0c;包括&#xff1a; 容量评估。技术选型。容灾处理。 另外&#xff0c;文末赠送免费定制红包封面哦&#xff01; 1. 容量评估 通过对容量&性能的评估&#xff0c;可以把业务需求转化成技术语言描…

鲲鹏微认证——openEuler开源操作系统迁移实践

文章目录 为什么要系统搬迁为什么选择欧拉欧拉系统迁移概述实施路径工具实战 为什么要系统搬迁 2020年12月&#xff0c;CentOs作为由开源社区免费提供的操作系统&#xff0c;宣布将对CentO58于2021年底停止服务&#xff0c;CentO57则于2024年6月底停止服务。 这将直接导致操作…

Linux系统SSH远程管理服务

目录 一、SSH服务介绍 1、SSH协议是什么&#xff1f; 2、SSH的优点 3、SSH的客户端与服务端 4、SSH的原理 4.1 公钥首次连接原理 4.2 ssh加密通讯原理 4.2.1 对称加密 4.2.2 非对称加密 4.2 ssh远程登录 二、服务端配置 1、常见配置项 1.1 修改默认端口 1.2 禁止…

未来已来:AI引领智能时代的多领域巨变

大家好&#xff0c;今天我们将深入探讨人工智能如何彻底改变我们的生活方式&#xff0c;领略未来的无限可能性。 1. 医疗革新&#xff1a;AI担任超级医生 医疗领域是AI最引人注目的战场之一。智能医学影像诊断系统&#xff0c;不仅能够精准识别病变&#xff0c;还能辅助医生提…

VS Code使用Git管理开发项目流程

以VSCode远程连接虚拟机开发为例&#xff0c;已经配置好SSH 在Github上搜索心仪的项目&#xff0c;比如权限管理 点击fork到自己账户仓库 虚拟机下创建一个目录 1)mkdir java_test 2)切换到java_test 初始化并克隆项目 1&#xff09; git init:初始化仓库 2&#xff09; g…

掼蛋功能之识别性格篇

常说&#xff1a;千人千面。大多数人一到牌局的场面&#xff0c;往往精神便会放松&#xff0c;面貌神情不再收敛&#xff0c;一言一行体现出的性格暴露无疑&#xff0c;具体表现为以下几种&#xff1a; 1、浮躁冲动型&#xff1a;此类人多数不讲究团队配合&#xff0c;自顾自出…

UE5 - Polycam扫描文件导入插件

Polycam是利用Gaussian Splatting进行3D重建的3D扫描相关软件&#xff0c;其对应有UE引擎的插件&#xff08;Plugin_XV3dGS&#xff09;可以把相关格式的文件导入到引擎&#xff1b; 首先Polycam的官网为&#xff1a;My Captures | Polycam 可以下载各种用户扫描文件&#xff…

vivado I/O和时钟规划设计流程步骤

I/O和时钟规划设计流程步骤 下图显示了左侧的项目设计流程步骤。水平箭头表示项目设计流程中可以执行I/O和时钟规划的点。中的步骤I/O和时钟规划设计流程如右图所示。 项目设计流程从一个空的I/O规划项目、RTL设计项目或合成后网表项目。使用这些项目类型中的任何一种&#xf…

PPO学习

openai用tf实现的真的看不懂&#xff0c;大佬的世界… PPO的详细细节 1. 奖励模型和策略的价值头将 query 和 response 的连接作为输入 奖励模型和策略的价值头 不 仅仅查看响应。相反&#xff0c;它将 query 和 response 连接在一起&#xff0c;作为 query_response def ge…

Python进阶知识:整理6 -> 正则表达式

1 基础匹配用法 # 演示Python中正则表达式re模块的3个基础匹配方法 import re # 1. match()方法 从头匹配 string "hello world" result re.match("hello", string) # 如果头部没有匹配成功就直接失败了,后面就不会继续匹配了 print(result) print(r…

设计模式: 装饰模式

文章目录 一、什么是装饰模式二、装饰模式的结构三、使用场景案例分析 一、什么是装饰模式 在不改变对象原有行为的基础上&#xff0c;动态的来为该对象绑定新的行为。 二、装饰模式的结构 装饰模式结构中主要包含如下角色&#xff1a; Component&#xff08;抽象部件&…

橘子学Mybatis08之Mybatis关于一级缓存的使用和适配器设计模式

前面我们说了mybatis的缓存设计体系&#xff0c;这里我们来正式看一下这玩意到底是咋个用法。 首先我们是知道的&#xff0c;Mybatis中存在两级缓存。分别是一级缓存(会话级)&#xff0c;和二级缓存(全局级)。 下面我们就来看看这两级缓存。 一、准备工作 1、准备数据库 在此之…

《如何画好架构图》学习笔记

看了一堂《如何画好架构图》的公开课&#xff0c;结合网上的资料与经验做一些思考总结。文中的例子和图片大多是从课程中摘录的。 1. 4R架构定义 4R架构定义其实是软件架构定义经过归纳提炼后的简称。 软件架构定义&#xff1a;软件架构是指软件系统的顶层&#xff08;Rank&am…

Linux中并发程序设计

进程的创建和回收 进程概念 概念 程序 存放在磁盘上的指令和数据的有序集合&#xff08;文件&#xff09; 静态的 进程 执行一个程序所分配的资源的总称 动态的进程和程序比较 注&#xff1a;进程是存在RAM中&#xff0c;程序是存放在ROM(flash)中的进程内容 BSS段&#xff…

Spring如何使用自定义注解来实现自动管理事务?

人可以做他(她)想做的&#xff0c;但不能要他(她)想要的 一个目录 前言业务代码展示手动挡自动挡事务失效的问题代码地址 前言 在两年半以前&#xff0c;我写了一篇博客&#xff1a;框架的灵魂之注解基础篇&#xff1a; 在那篇博客的结尾&#xff0c;我埋了一个坑&#xff1a…

JAVA 学习 面试(八)集合类

集合类 集合&#xff08;Collection&#xff09; 1、 List列表 &#xff1a; 有序 可重复 1、ArrayList : 数组列表 &#xff0c;内部是通过Array实现&#xff0c;对数据列表进行插入、删除操作时都需要对数组进行拷贝并重排序&#xff0c;因此在知道存储数据量时&#xff0c…

【GAMES101】Lecture 09 重心坐标

我们之前说着色过程中以及这个计算法线的时候需要用到这个插值&#xff08;Interpolation&#xff09;&#xff0c;然后插值是通过这个重心坐标&#xff08;Barycentric Coordinates&#xff09;来实现的 目录 重心坐标 插值 重心坐标 注意哈我们这里说的三角形的重心坐标并…

RK3399平台开发系列讲解(USB篇)BusHound 工具使用介绍

🚀返回专栏总目录 文章目录 一、BusHound简介二、BusHound的下载三、BusHound设备窗口四、BUSHound发送命令窗口沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 BusHound软件是由美国perisoft公司研制的一种专用于PC机各种总线数据包监视和控制的开发工具软件,其名…

【GitHub项目推荐--一款美观的开源社区系统】【转载】

推荐一款开源社区系统&#xff0c;该系统基于主流的 Java Web 技术栈&#xff0c;如果你是一名 Java 新手掌握了基本 JavaEE 框架知识&#xff0c;可以拿本项目作为练手项目。 开源社区系统功能还算完善包含发布帖子、发布评论、私信、系统通知、点赞、关注、搜索、用户设置、…

What is `Filter` does?

过滤器&#xff08;Filter&#xff09;是Java Servlet规范中的一部分&#xff0c;它提供了一种在请求到达目标资源之前或响应发送给客户端之前进行预处理和后处理的能力。 通过编写自定义的过滤器类并将其注册到Web应用程序中&#xff0c;开发者可以实现诸如登录验证、权限控制…