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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

未来已来:概念车展漫游可视化的震撼之旅

随着科技的飞速发展&#xff0c;汽车行业正经历着前所未有的变革。而在这场变革中&#xff0c;概念车展无疑是一个引领潮流、展望未来的重要舞台。 想象一下&#xff0c;你站在一个巨大的展厅中&#xff0c;四周陈列着各式各样的概念车。它们有的造型独特&#xff0c;有的功能先…

Protein carbonyl ELISA kit羰基化蛋白ELISA试剂盒

蛋白质羰基化是蛋白质氧化损伤的一种&#xff0c;是氧化应激中一种不可逆的化学修饰。蛋白质羰基化会引起蛋白质结构的改变&#xff0c;使其失去原有的生物学功能&#xff0c;最终导致细胞和组织功能紊乱。蛋白质羰基化不仅是氧化损伤的一项重要指标&#xff0c;也参与多种疾病…

客户大批量保密文件销毁,数据销毁新方案及实践 文件销毁 硬盘销毁 数据销毁 物料销毁

2023年春节前夕&#xff0c;青岛客户经理接到一个电话&#xff0c;韩国驻华机构想请我们做文件销毁&#xff0c;要求在2天内销毁800多箱纸文件。800多箱需要在短短两天内完成销毁&#xff0c;这一数字创下了淼一文件数据销毁自2009年以来的历史记录。单从业绩和营销角度看&…

AIGC是什么?GPT-4.0、DALL·E以及Midjourney等多种智能服务

AIGC&#xff08;人工智能生成内容&#xff0c;Artificial Intelligence Generated Content&#xff09;是指利用人工智能技术自动生成的文本、图像、音频和视频等内容。随着技术的进步&#xff0c;AIGC已经成为创意产业和内容创作领域的一股新兴力量。MidTool作为一款集成了多…

Spring Security 之 基本认证

基本认证 这部分提供了关于Spring Security如何为基于Servlet的应用程序提供基本HTTP认证支持的详细信息。 这部分描述了Spring Security中HTTP基本认证的工作原理。首先,我们看到WWW-Authenticate标头被发送回未经身份验证的客户端: 首先,用户对未经授权的资源 /private …

2024.1.22力扣每日一题——最大交换

2024.1.22 题目来源我的题解方法一 暴力法方法一 哈希表贪心方法三 贪心 题目来源 力扣每日一题&#xff1b;题序&#xff1a;670 我的题解 方法一 暴力法 直接暴力对数字中的每两个位置进行交换&#xff0c;然后记录交换后生成数字的最大值 时间复杂度&#xff1a;O( log ⁡…

用游戏盾会掉线吗,游戏出现掉线或者卡顿的可能有哪些原因

目前游戏类用户使用抗D盾进行防护DDOS攻击的情况非常普遍&#xff0c;有些第一次了解接触到盾的用户&#xff0c;会有担心接入盾使用后&#xff0c;玩家那边会不会掉线或者出现卡的问题。 其实关于这方面是完全不用担心&#xff0c;游戏盾都是由高防节点智能多线节点分布&…

TCP的连接和关闭的那些事

一、基础概念 1、啥是TCP&#xff1f; 它是面向连接的一种协议&#xff0c;任何数据发送之前都需要建立连接。 2、TCP/IP协议的四层中那一层&#xff1f; TCP位于运输层&#xff0c;详见下图 3、TCP协议的状态机有哪些? 在链接建立和断开不同阶段都有不同的状态&#xf…

企业邮箱遭入侵!印度制药巨头损失超4500万元

近日&#xff0c;印度制药巨头阿尔肯实验室子公司部分员工的企业邮箱遭入侵&#xff0c;导致其子公司被欺诈5.2亿卢比&#xff08;约合人民币4500万元&#xff09;。而根据截至2023年9月的季度财务报告数据&#xff0c;该公司营业收入为263.46亿卢比&#xff0c;净利润为64.65亿…

网页首页案例(使用框架:继上一篇博客结尾)

文章目录 新认识的快捷键1.先写好组件并导入App.vue2.往组件中一个一个填内容3.整体静态完成后&#xff0c;发现某些小部分相同&#xff0c;其实可以分装成小组件4.最后通过js动态渲染 新认识的快捷键 1.Ctrl滚轮按住往下拖可以部分选中 .用同样的方法选中下面的111&#xff0…

机器学习:多元线性回归闭式解(Python)

import numpy as np import matplotlib.pyplot as pltclass LRClosedFormSol:def __init__(self, fit_interceptTrue, normalizeTrue):""":param fit_intercept: 是否训练bias:param normalize: 是否标准化数据"""self.theta None # 训练权重系…

Vue3 Teleport 将组件传送到外层DOM位置

✨ 专栏介绍 在当今Web开发领域中&#xff0c;构建交互性强、可复用且易于维护的用户界面是至关重要的。而Vue.js作为一款现代化且流行的JavaScript框架&#xff0c;正是为了满足这些需求而诞生。它采用了MVVM架构模式&#xff0c;并通过数据驱动和组件化的方式&#xff0c;使…

力扣hot100 合并区间 排序 贪心

Problem: 56. 合并区间 复杂度 时间复杂度: O ( n log ⁡ n ) O(n\log{n}) O(nlogn) 空间复杂度: O ( n ) O(n) O(n) Code class Solution {public int[][] merge(int[][] intervals){Arrays.sort(intervals, (int[] a, int[] b) -> {return a[0] - b[0];});// 按照数…

基于原生图数据库的知识图谱存储

目录 前言1 关系模型的局限1.1 语义关联的隐藏1.2 数据多样性的挑战1.3 动态性受限1.4 与自然语言描述失配 2 知识图谱与图数据库2.1 图数据库概述2.2 图的结构特征的优势2.3 跨领域图建模与查询2.4 丰富的关系语义表达与推理能力 3 图数据建模的好处3.1 自然表达3.2 易于扩展3…

uniapp canvas做的刮刮乐解决蒙层能自定义图片

最近给湖南中烟做元春活动&#xff0c;一个月要开发4个小活动&#xff0c;这个是其中一个难度一般&#xff0c;最难的是一个类似鲤鱼跃龙门的小游戏&#xff0c;哎&#xff0c;真实为难我这个“拍黄片”的。下面是主要代码。 <canvas :style"{width:widthpx,height:hei…

uniapp复选框 实现排他选项

选择了排他选项之后 复选框其他选项不可以选择 <view class"reportData" v-for"(val, index) in obj" :key"index"> <view v-if"val.type 3" ><u-checkbox-group v-model"optionValue" placement"colu…

orm-04-Spring Data JPA 入门介绍

拓展阅读 The jdbc pool for java.(java 手写 jdbc 数据库连接池实现) The simple mybatis.&#xff08;手写简易版 mybatis&#xff09; Spring Data JPA Spring Data JPA&#xff0c;作为更大的 Spring Data 家族的一部分&#xff0c;使得基于 JPA 的仓库实现变得更加容易。…

卫星影像离线瓦片如何调用?

我们曾为你分享了按区县购买卫星影像并在线调用的方法。 于是就有朋友问&#xff0c;卫星影像瓦片可以离线调用吗&#xff1f; 当然可以&#xff0c;这里就来分享一下卫星影像瓦片离线调用的方法。 卫星影像离线瓦片如何调用&#xff1f; 这里以OpenLayers、Mapbox和Cesiu…

Google 提出稀疏注意力框架Exphormer,提升图Transformer的扩展性!

引言 Graph Transformer已成为ML的重要架构&#xff0c;它将基于序列的Transformer应用于图结构数据。然而当面对大型图数据集时&#xff0c;使用Graph Transformer会存在扩展性限制。为此&#xff0c;「Google提出了一个稀疏注意力框架Exphormer&#xff0c;它使用扩展图来提…

LeetCode-2865. 美丽塔 I

题面 给你一个长度为 n 下标从 0 开始的整数数组 maxHeights 。 你的任务是在坐标轴上建 n 座塔。第 i 座塔的下标为 i &#xff0c;高度为 heights[i] 。 如果以下条件满足&#xff0c;我们称这些塔是 美丽 的&#xff1a; 1 < heights[i] < maxHeights[i] heights 是…