当前位置: 首页 > news >正文

进程通信与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”。若用户一直未猜对,输入任意负数即可退出游戏,客户端程序结束。如下图所示。
    在这里插入图片描述

相关文章:

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

socket是实现进程通信的一种重要方式&#xff0c;本文将通过socket编程实现服务器进程与客户端进程之间的通信&#xff0c;并在通信之外实现猜数字的小游戏。 1. 设计思路 本文设计的C/S结构的猜数字游戏功能如下&#xff1a;服务器端自动生成一个1-100之间的随机数字&#x…...

AcWing 1241. 外卖店优先级(复杂模拟思路 + 代码详解)

[题目概述] “饱了么”外卖系统中维护着 N 家外卖店&#xff0c;编号 1∼N。 每家外卖店都有一个优先级&#xff0c;初始时 (0 时刻) 优先级都为 0。 每经过 1 个时间单位&#xff0c;如果外卖店没有订单&#xff0c;则优先级会减少 1&#xff0c;最低减到 0&#xff1b;而如果…...

查询文件hash值

查询文件hash值 1 Windows 查询文件hash值1.1 certutil -hashfile 文件名 2 Linux 环境查询文件hash值2.1 sha256sum 文件名2.2 md5sum 文件名 1 Windows 查询文件hash值 在某些环境要对比两个文件是否完全一致 1.1 certutil -hashfile 文件名 certutil -hashfile C:\Users\…...

[docker] Docker资源管理

一、docker资源控制 Docker通过Cgroup 来控制容器使用的资源配额&#xff0c;包括CPU、内存、磁盘三大方面&#xff0c;基本覆盖了常见的资源配额和使用量控制。Caroup 是ControlGroups的缩写&#xff0c;是Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如…...

不就业,纯兴趣,应该自学C#还是JAVA?

不就业&#xff0c;纯兴趣&#xff0c;应该自学C#还是JAVA? 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「JAVA的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff…...

【Go面试向】defer与time.sleep初探

【Go面试向】defer与time.sleep初探 大家好 我是寸铁&#x1f44a; 总结了一篇defer传参与time.sleep初探的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 请大家看下面这段代码&#xff0c;看运行结果会出现什么&#xff0c;为什么&#xff1f; 问题 demo package mainim…...

fpga外置flash程序烧录流程

Fpga外置FLASH程序烧录流程&#xff1a; step1&#xff1a; 打开vivado2019.2软件&#xff0c;找到hardware manager选项&#xff0c;进入该功能界面&#xff1b; Step2&#xff1a; 确定连接状态&#xff0c;当JTAG正确连接到板卡的调试插针后&#xff0c;会在状态窗口显示…...

什么是通配监听端口? 什么是通配监听IP?

什么是通配监听端口? 监听端口&#xff1a; 指的是服务器或服务开启的特定TCP或UDP端口号&#xff0c;等待客户端连接或发送数据。TCP/IP协议下每个端口只能由一个服务独占监听&#xff0c;一个服务或应用会指定监听特定的一个或多个端口来接收客户端的连接请求。 例如 Web…...

CentOS 安装 Ruby

1.下载 Ruby3.3 并安装 依次执行 wget https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.0.tar.gz tar -zxvf ruby-3.3.0.tar.gz cd ruby-3.3.0 ./configure make make install 2.查看版本 ruby -v...

Laya3.0 相机使用

摄像机&#xff0c;是3D场景里边最经常使用的对象了。 官方文档&#xff1a;点击这里学习 1.投影 Projection 透视&#xff1a; 模拟人眼的视觉效果&#xff0c;近大远小。模拟物理世界的规律&#xff0c;将眼睛或相机抽象成一个点&#xff0c;此时视锥体内的物体投影到视平…...

前端语音识别(webkitSpeechRecognition)

前端语音识别(webkitSpeechRecognition)-CSDN博客 Excerpt 文章浏览阅读1.8k次,点赞4次,收藏4次。浏览器实现语音转文字_webkitspeechrecognition webkitSpeechRecognition(语音识别) <span class="token comment">// 创建一个webkitSpeechRecognition实…...

Flutter中状态管理选项的比较:利弊探索

Flutter 应用程序开发的一个关键方面是管理状态&#xff0c;这确保了整个应用程序的数据一致性和更新。然而&#xff0c;Flutter 提供了多种状态管理解决方案&#xff0c;每种解决方案都有自己的优缺点。在这篇博客中&#xff0c;我们将探讨 Flutter 中一些流行的状态管理选项&…...

# [NOI2019] 斗主地 洛谷黑题题解

[NOI2019] 斗主地 题目背景 时限 4 秒 内存 512MB 题目描述 小 S 在和小 F 玩一个叫“斗地主”的游戏。 可怜的小 S 发现自己打牌并打不过小 F&#xff0c;所以他想要在洗牌环节动动手脚。 一副牌一共有 n n n 张牌&#xff0c;从上到下依次标号为 1 ∼ n 1 \sim n 1∼…...

踩坑(6)Redisson调用unlockAsync方法释放锁失败

问题描述 通过redisson的lockAsync异步方法获取到锁之后&#xff0c;再业务执行完成后调用lock.unlockAsync()无法释放当前锁&#xff0c;导致后续的方法被阻塞 public void asyncLock() {RLock lock redissonClient.getLock("asyncLock");RFuture<Void> fut…...

树莓派实战应用:基于人脸识别系统

引言&#xff1a; 随着人工智能技术的不断发展&#xff0c;人脸识别技术已经广泛应用于各种场景&#xff0c;如门禁系统、安全监控等。树莓派作为一种功能强大的迷你计算机&#xff0c;也可以用于搭建人脸识别检测系统。 一、项目简介 人脸识别系统是一种基于人工智能技术的身…...

5G赋能智慧文旅:科技与文化的完美结合,打造无缝旅游体验,重塑旅游业的未来

一、5G技术&#xff1a;智慧文旅的强大引擎 5G技术的起源可以追溯到2010年&#xff0c;当时世界各国开始意识到4G技术已经达到了瓶颈&#xff0c;无法满足日益增长的移动通信需求。2013年&#xff0c;国际电信联盟&#xff08;ITU&#xff09;成立了5G技术研究组&#xff0c;开…...

大模型:相关参数总结

文章目录 一、相关参数 一、相关参数 参数名称是否必填默认值描述model是调用的模型名称message是传入模型的消息max_tokens否返回tokens的数量temperature否top_p否n否表示一个问答返回几个回答的结果信息stream否false表示应答的方式&#xff0c;false表示返回全部的结果&am…...

腾讯云短信开发

短信服务应用申请 """ 准备工作 1&#xff09;创建短信应用 - 应用管理 2&#xff09;申请短信签名 - 国内短信 > 签名管理 3&#xff09;申请短信模块 - 国内短信 > 正文模板管理 """python中开发腾讯云短信服务 """ 1…...

Dockerfile:如何写一个Dockerfile文件?

如何写一个Dockerfile文件&#xff1f; &#x1f6a8;推荐参考&#xff1a;Dockerfile&#xff1a;如何写一个Dockerfile文件&#xff1f; 现在的项目肯定都离不开docker&#xff0c;只要是流水线部署就会涉及Dockerfile文件&#xff0c;那么如何写一个正确的编写一个Dockerfil…...

Lua 中的高级特性:模块的使用、字符串模式匹配、高阶函数和表的元方法

### 1. 模块的使用 在 Lua 中&#xff0c;模块是一种封装代码的方式&#xff0c;使得代码可以被重用。下面是一个简单的模块定义和使用的示例&#xff1a; lua -- 定义一个名为 mymodule 的模块 mymodule {} function mymodule.sayHello() print("Hello from my mo…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

Bean 作用域有哪些?如何答出技术深度?

导语&#xff1a; Spring 面试绕不开 Bean 的作用域问题&#xff0c;这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开&#xff0c;结合典型面试题及实战场景&#xff0c;帮你厘清重点&#xff0c;打破模板式回答&#xff0c…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...