进程通信与socket编程实践之猜数字小游戏
socket是实现进程通信的一种重要方式,本文将通过socket编程实现服务器进程与客户端进程之间的通信,并在通信之外实现猜数字的小游戏。
1. 设计思路
本文设计的C/S结构的猜数字游戏功能如下:服务器端自动生成一个1-100之间的随机数字,用户在客户端根据提示输入所猜的数字,若用户猜的数字和服务器所生成的数字相同,则游戏通关,用户可选择是否进入下一轮。当用户输入负数时,退出游戏。
设计要点如下:
-
服务端通信部分: 为实现客户端与服务器进程的相互通信,在服务端,首先创建一个socket,设置socket属性,并调用函数
bind()绑定IP地址、端口等信息;然后调用函数listen()开启监听,调用函数accept()阻塞,等待客户端的连接请求;接下来,调用函数recv()和send()收发数据;最后关闭网络连接和监听。 -
客户端通信部分: 在客户端,也是先调用函数
socket()创建一个socket,设置socket属性;然后调用函数connect()连接服务器;调用函数recv()和send()收发数据,最后关闭网络连接。服务端与客户端的通信过程如下图所示。

-
服务端处理方式: 服务端采用多线程的方式处理客户端的请求。主线程每收到一个来自客户端的TCP连接请求,就调用
pthread_create()函数创建一个子线程与之对应连接。一个线程处理一个客户端,从而实现了一个服务器与多个客户端之间的通信。 -
猜数字功能设计: 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. 运行结果
-
若可执行文件未生成,首先使用
gcc server.c -lpthread -o server与gcc client.c -lpthread -o client命令生成可执行文件server与client;若可执行文件已存在,转步骤2。(注意: 由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数。) -
使用
./server命令运行服务端程序,如下图所示。

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

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

相关文章:
进程通信与socket编程实践之猜数字小游戏
socket是实现进程通信的一种重要方式,本文将通过socket编程实现服务器进程与客户端进程之间的通信,并在通信之外实现猜数字的小游戏。 1. 设计思路 本文设计的C/S结构的猜数字游戏功能如下:服务器端自动生成一个1-100之间的随机数字&#x…...
AcWing 1241. 外卖店优先级(复杂模拟思路 + 代码详解)
[题目概述] “饱了么”外卖系统中维护着 N 家外卖店,编号 1∼N。 每家外卖店都有一个优先级,初始时 (0 时刻) 优先级都为 0。 每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减到 0;而如果…...
查询文件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 来控制容器使用的资源配额,包括CPU、内存、磁盘三大方面,基本覆盖了常见的资源配额和使用量控制。Caroup 是ControlGroups的缩写,是Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如…...
不就业,纯兴趣,应该自学C#还是JAVA?
不就业,纯兴趣,应该自学C#还是JAVA? 在开始前我有一些资料,是我根据网友给的问题精心整理了一份「JAVA的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家ÿ…...
【Go面试向】defer与time.sleep初探
【Go面试向】defer与time.sleep初探 大家好 我是寸铁👊 总结了一篇defer传参与time.sleep初探的文章✨ 喜欢的小伙伴可以点点关注 💝 请大家看下面这段代码,看运行结果会出现什么,为什么? 问题 demo package mainim…...
fpga外置flash程序烧录流程
Fpga外置FLASH程序烧录流程: step1: 打开vivado2019.2软件,找到hardware manager选项,进入该功能界面; Step2: 确定连接状态,当JTAG正确连接到板卡的调试插针后,会在状态窗口显示…...
什么是通配监听端口? 什么是通配监听IP?
什么是通配监听端口? 监听端口: 指的是服务器或服务开启的特定TCP或UDP端口号,等待客户端连接或发送数据。TCP/IP协议下每个端口只能由一个服务独占监听,一个服务或应用会指定监听特定的一个或多个端口来接收客户端的连接请求。 例如 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 相机使用
摄像机,是3D场景里边最经常使用的对象了。 官方文档:点击这里学习 1.投影 Projection 透视: 模拟人眼的视觉效果,近大远小。模拟物理世界的规律,将眼睛或相机抽象成一个点,此时视锥体内的物体投影到视平…...
前端语音识别(webkitSpeechRecognition)
前端语音识别(webkitSpeechRecognition)-CSDN博客 Excerpt 文章浏览阅读1.8k次,点赞4次,收藏4次。浏览器实现语音转文字_webkitspeechrecognition webkitSpeechRecognition(语音识别) <span class="token comment">// 创建一个webkitSpeechRecognition实…...
Flutter中状态管理选项的比较:利弊探索
Flutter 应用程序开发的一个关键方面是管理状态,这确保了整个应用程序的数据一致性和更新。然而,Flutter 提供了多种状态管理解决方案,每种解决方案都有自己的优缺点。在这篇博客中,我们将探讨 Flutter 中一些流行的状态管理选项&…...
# [NOI2019] 斗主地 洛谷黑题题解
[NOI2019] 斗主地 题目背景 时限 4 秒 内存 512MB 题目描述 小 S 在和小 F 玩一个叫“斗地主”的游戏。 可怜的小 S 发现自己打牌并打不过小 F,所以他想要在洗牌环节动动手脚。 一副牌一共有 n n n 张牌,从上到下依次标号为 1 ∼ n 1 \sim n 1∼…...
踩坑(6)Redisson调用unlockAsync方法释放锁失败
问题描述 通过redisson的lockAsync异步方法获取到锁之后,再业务执行完成后调用lock.unlockAsync()无法释放当前锁,导致后续的方法被阻塞 public void asyncLock() {RLock lock redissonClient.getLock("asyncLock");RFuture<Void> fut…...
树莓派实战应用:基于人脸识别系统
引言: 随着人工智能技术的不断发展,人脸识别技术已经广泛应用于各种场景,如门禁系统、安全监控等。树莓派作为一种功能强大的迷你计算机,也可以用于搭建人脸识别检测系统。 一、项目简介 人脸识别系统是一种基于人工智能技术的身…...
5G赋能智慧文旅:科技与文化的完美结合,打造无缝旅游体验,重塑旅游业的未来
一、5G技术:智慧文旅的强大引擎 5G技术的起源可以追溯到2010年,当时世界各国开始意识到4G技术已经达到了瓶颈,无法满足日益增长的移动通信需求。2013年,国际电信联盟(ITU)成立了5G技术研究组,开…...
大模型:相关参数总结
文章目录 一、相关参数 一、相关参数 参数名称是否必填默认值描述model是调用的模型名称message是传入模型的消息max_tokens否返回tokens的数量temperature否top_p否n否表示一个问答返回几个回答的结果信息stream否false表示应答的方式,false表示返回全部的结果&am…...
腾讯云短信开发
短信服务应用申请 """ 准备工作 1)创建短信应用 - 应用管理 2)申请短信签名 - 国内短信 > 签名管理 3)申请短信模块 - 国内短信 > 正文模板管理 """python中开发腾讯云短信服务 """ 1…...
Dockerfile:如何写一个Dockerfile文件?
如何写一个Dockerfile文件? 🚨推荐参考:Dockerfile:如何写一个Dockerfile文件? 现在的项目肯定都离不开docker,只要是流水线部署就会涉及Dockerfile文件,那么如何写一个正确的编写一个Dockerfil…...
Lua 中的高级特性:模块的使用、字符串模式匹配、高阶函数和表的元方法
### 1. 模块的使用 在 Lua 中,模块是一种封装代码的方式,使得代码可以被重用。下面是一个简单的模块定义和使用的示例: lua -- 定义一个名为 mymodule 的模块 mymodule {} function mymodule.sayHello() print("Hello from my mo…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
