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

news2024/9/29 7:17:54

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=1
    while (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.coloradmin.cn/o/1409673.html

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

相关文章

linux基础学习(7):find命令

1.按照文件名搜索 find 搜索路径 选项 文件名 选项&#xff1a; -name&#xff1a;按文件名搜索 -ineme&#xff1a;不区分文件名大小写搜索 -inum&#xff1a;按inode号搜索 按文件名搜索跟按关键词搜索不一样&#xff0c;只能搜到文件名完整对应的文件 *根据文件名…

加速应用开发:低代码云SaaS和源码交付模式如何选

随着数字化转型的加速&#xff0c;企业对于快速开发和交付高质量应用的需求也越来越迫切。为了满足这一需求&#xff0c;开发者们开始探索采用低代码平台进行软件开发工作&#xff0c;以加速应用开发过程。 目前&#xff0c;市场上的低代码产品众多&#xff0c;但基本可分为简单…

特征工程之特征降维

为什么要进行特征降维&#xff1f; 特征对训练模型是非常重要的,用于训练的数据集包含一些不重要的特征,可能导致模型泛化性能 不佳 某些特征的取值较为接近&#xff0c;其包含的信息较少 希望特征独立存在对预测产生影响&#xff0c;两个特征同…

Goldsky - 使用ClickHouse和Redpanda的黄金标准架构

本文字数&#xff1a;6240&#xff1b;估计阅读时间&#xff1a;16 分钟 作者&#xff1a;ClickHouse Team 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 本文在公众号【ClickHouseInc】首发 介绍 作为一家以开源为根基的公司&#xff0c;我们发现用户通常是第一个识别…

JWT登录

JWT JSON Web Token&#xff08;JSON Web令牌&#xff09; 是一个开放标准(rfc7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任&#xff0c;因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法…

3.确认弹窗(ConfirmPopup)

愿你出走半生,归来仍是少年&#xff01; 环境&#xff1a;.NET 7 在开发中&#xff0c;最常用的弹窗之一表示确认弹窗&#xff0c;为了减少重复的开发工作&#xff0c;所以需要基于Popup进行封装。 1.布局 分为标题、确认内容、按钮三个区域&#xff0c;都是可供调整的。 &l…

二叉树堆的应用实例分析:堆排序 | TOP-K问题

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构冒险记 ✅C语言进阶之路 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 文章目录 前言一、堆排序1.1 排序思想1.2 堆排序过程&#xff08;图解&#xff09;1.3 堆排序代…

查看代码是否是在GPU上跑的

import torchif torch.cuda.is_available():print("运行在GPU上") else:print("运行在CPU上")进入容器后 如果进入容器后&#xff0c;是没法通过nvidia-smi命令查看显卡型号的&#xff0c;但是环境仍然是GPU在运行。 nvidia-smi 没进入的时候

【笔记】杨继深老师电磁兼容(EMC)课程1-3笔记

视频链接 【杨继深老师电磁兼容&#xff08;EMC&#xff09;课程最新版——精品课_哔哩哔哩_bilibili 1、什么是电磁骚扰&#xff08;EMI&#xff09;&#xff1f; &#xfeff; 第1讲 什么是辐射发射 P1 - 01:39. &#xfeff; 骚扰&#xff1a;主动性的对其他设备造成影响…

从多巴胺到老钱风,品牌如何做好人设营销

在今年开年&#xff0c;又一大旅游城市爆火&#xff0c;被网友称为“讨好型市格”的哈尔滨&#xff0c;第一次有了清晰的人设&#xff0c;哈尔滨也迎来无数游客。其实品牌玩人设营销不止今年&#xff0c;在去年就已经有趋势&#xff0c;比如i人e人营销、“多巴胺穿搭”&#xf…

激光雷达标定入门(7)海康摄像头驱动

如果你在使用海康威视摄像头时遇到了编译报错的问题&#xff0c;可能是链接库的路径配置不正确。下面是解决这个问题的步骤和原理&#xff1a; 1. 克隆海康摄像头驱动代码 首先&#xff0c;你需要将海康摄像头的驱动代码克隆到你的工作空间中。使用以下命令&#xff1a; git…

【docker】解决docker overlay2目录占用大量磁盘空间,导致验证码出不来,报错Can‘t create output stream!

问题&#xff1a; 验证码出现Cant create output stream!报错信息 排查&#xff1a; 所在服务器磁盘使用率已经到达100%&#xff0c;经排查&#xff0c;服务器目录/var/lib/docker/overlay2占用大量磁盘空间&#xff0c; 解决&#xff1a; 使用【docker system prune】命令删…

哪吒监控面板对VPS统一管理

VPS安装Nginx Proxy Manager 可视化面板 - 非必须 Nginx作用是做一个代理&#xff0c;不用代理直接安装哪吒面板也是可以的&#xff0c;但是必须要有一个域名和github账号。 1、更新下VPS系统环境&#xff1a; apt update -y && apt install -y curl socat wget sudo…

安装宝塔面板后k8s所在节点pod无法正常工作解决方法,kubernetes k8s 与宝塔面板冲突解决方法

在实际项目过程中我们使用了k8s 在生产环境中运行管理服务。 但是对服务器的状态管理我们使用了宝塔面板进行 K8s 版本1.2.8 宝塔面板 版本 8.05 操作步骤是这样的。 1.完成1.2.8 k8s的节点安装&#xff0c;并正常运行服务。 过程略 2.安装宝塔面板 ​ yum install -y …

ChromeDriver谷歌驱动最新版安装120/121/122

chromeDriver最新版本下载 最新驱动 https://googlechromelabs.github.io/chrome-for-testing/参考&#xff1a; https://blog.csdn.net/m0_57382185/article/details/134007615

中华人民共和国国民经济和社会发展第十四个五年规划和2035年远景目标纲要 --九五小庞

原文链接&#xff1a;中华人民共和国国民经济和社会发展第十四个五年规划和2035年远景目标纲要_滚动新闻_中国政府网 第二篇 坚持创新驱动发展 全面塑造发展新优势 坚持创新在我国现代化建设全局中的核心地位&#xff0c;把科技自立自强作为国家发展的战略支撑&#xff0c;面…

Cute Http File Server 使用文章

下载 官网&#xff1a;http://iscute.cn/chfs 蓝奏下载&#xff1a;https://wwts.lanpw.com/iKP1i1m9572h 开源&#xff1a;https://github.com/docblue/chfsgui 介绍 Cute Http File Server 是国内免费开源的局域网传输服务器软件。 可以不用借助QQ、某信软件传输文件&am…

AI绘图软件:探索未来的创意工具

AI绘图软件有很多&#xff0c;以下是一些比较知名的AI绘图软件&#xff1a; Adobe Photoshop&#xff1a;全球最流行的图像编辑软件之一&#xff0c;具备多种AI功能&#xff0c;如智能修复、智能笔刷等。Corel Painter&#xff1a;一款专业的数字艺术软件&#xff0c;有AI功能…

SpringBoot,TDengine时序数据库,实现物联网,车联网大批量数据更新最佳实践。

简介 TDengine 是一款专为物联网、工业互联网等场景设计并优化的大数据平台&#xff0c;它能安全高效地将大量设备、数据采集器每天产生的高达 TB 甚至 PB 级的数据进行汇聚、存储、分析和分发&#xff0c;对业务运行状态进行实时监测、预警&#xff0c;提供实时的商业洞察。其…

.NET 跨平台图形库 SkiaSharp 基础应用

写在前面 SkiaSharp 是适用于 .NET 和 C# 的 2D 图形系统&#xff0c;由开源 Skia 图形引擎提供支持&#xff0c;在 Google 产品中广泛使用。 可以在应用程序中使用 SkiaSharp Xamarin.Forms 绘制 2D 矢量图形、位图和文本。支持跨平台&#xff0c;Windows、Linux、Anroid、IO…