【网络】UDP的应用场景

news2025/2/22 13:21:35

文章目录

  • 翻译功能
  • 命令行解析
  • 网络聊天室
  • UDP之Windows与Linux

翻译功能

我们写的UDP服务端并不是只接收到数据就完了,还需要进行处理任务

我们可以在服务端udpServer.hpp中设置一个回调函数 _callback,具体的逻辑通过udpServer.cc中由外部进行传入

代码如下所示:

 		void start()
        {
            // 服务器的本质其实就是一个死循环
            char buffer[gnum];
            for(;;)
            {
                // 读取数据
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer); //必填
                ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
                if(s > 0)
                {
                    buffer[s] = 0;
                    string clientip = inet_ntoa(peer.sin_addr); 
                    uint16_t clientport = ntohs(peer.sin_port);
                    string message = buffer;

                    cout << clientip <<"[" << clientport << "]# " << message << endl;//把收到的消息打印出来
                    _callback(_sockfd, clientip, clientport, message);
                }
            }
        }

翻译功能:客户端输入一个单词,然后发送给服务端,然后去接收服务端翻译之后的结果。

首先提供一个字典dict:把鞋有英语和汉语对应的文件dicTxt加载进我们的unordered_map词典,此时的unordered_map就保存了字典的内容

dict.txt:外部文件可由自己填写补充,这里只是给个样例测试代码而已

apple:苹果
world:世界
hello:你好
goodman:你是一个好人
const std::string dictTxt="./dict.txt";//文件
unordered_map<string, string> dict;//字典

下面,初始化我们的字典:打开文件,把文件中的每一行切分成key和value,也就是英文和中文,然后把结果插入到dict中,也就是把结果放进dict中,代码如下:

static bool cutString(const string &target, string *s1, string *s2, const string &sep)
{
    //apple:苹果
    auto pos = target.find(sep);
    if(pos == string::npos) return false;
    *s1 = target.substr(0, pos); //[)
    *s2 = target.substr(pos + sep.size()); //[)
    return true;
}

static void initDict()
{
    ifstream in(dictTxt, std::ios::binary);
    if(!in.is_open())
    {
        cerr << "open file " << dictTxt << " error" << endl;
        exit(OPEN_ERR);
    }
    string line;
    std::string key, value;
    while(getline(in, line))//读取文件
    {
        if(cutString(line, &key, &value, ":"))
        {
            dict.insert(make_pair(key, value));
        }
    }

    in.close();

    cout << "load dict success" << endl;
}

在udpServer.cc中通过函数handlerMessage处理数据,然后在把处理的结果反馈给客户端:

void handlerMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
    string response_message;
    auto iter = dict.find(message);
    if(iter == dict.end()) response_message = "unknown";
    else response_message = iter->second;

    // 开始返回
    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str());

    sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr*)&client, sizeof(client));
}

客户端udpClient.hpp输入消息cin并发送sendto给服务端,然后服务端udpServer.hpp调用回调函数对消息进行翻译,翻译完成后把最终的结果在传送sendto给客户端,客户端udpClient.hpp在接收recvfrom翻译之后的结果,最终把翻译结果打印出来即可:

 		void run()
        {
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(_serverip.c_str());
            server.sin_port = htons(_serverport);

            string message;
            char cmdline[1024];
            while (!_quit)
            {
                cout<<"Please Enter#";
                cin>>message;
                sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));

                char buffer[1024];
                struct sockaddr_in temp;
                socklen_t temp_len = sizeof(temp);
                size_t n = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&temp_len);
                if(n>0) buffer[n] = 0;
                cout<<"服务器的翻译结果# "<<buffer<<endl;   
            }
        }

下面,进行测试运行结果:

//udpServer.cc
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    initDict();
    std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage, port));
}
//udpClient.cc
int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);
    unique_ptr<udpClient> ucli(new udpClient(serverip, serverport));
    ucli->initClient();
    ucli->run();
    return 0;
}

先启动服务端:

image-20230530191842224

在启动客户端输入信息,给服务端传送消息,服务端收到消息打印出来,并将翻译完成的结果返回给客户端,客户端再把翻译后的结果打印出来,这就是上面所说的整个过程:

image-20230530191942072

image-20230530192002279

命令行解析

借用popen接口:(功能相当于pipe+fork,exec*)

#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

command:传递进来的字符串,比如 ls -a -l ;type:以什么方式打开文件(r/w/a),我们通过一个函数execComand进行调用即可,只需要在udpServer.cc文件中修改传入的函数即可实现该功能:

void execCommand(int sockfd, string clientip, uint16_t clientport, string cmd)
{
    //1. cmd解析,ls -a -l
    //先禁止一下非法操作,防止有人搞破坏
    if(cmd.find("rm") != string::npos || cmd.find("mv") != string::npos || cmd.find("rmdir") != string::npos)
    {
        cerr << clientip << ":" << clientport << " 正在做一个非法的操作: " << cmd << endl;
        return;
    }

    string response;
    FILE *fp = popen(cmd.c_str(), "r");
    if(fp == nullptr) response = cmd + " exec failed";
    char line[1024];
    while(fgets(line, sizeof(line), fp))
    {
        response += line;
    }
    pclose(fp);

    // 开始返回
    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str());
    sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&client, sizeof(client));
}

测试运行:

//udpServer.cc
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    std::unique_ptr<udpServer> usvr(new udpServer(execCommand, port));
}

image-20230530194723712

image-20230530194732874

网络聊天室

我们需要去管理用户,而对于每个用户我们用IP和port来标识唯一性,那么多的用户我们可以用哈希表来进行统一管理:

class User
{
public:
    User(const string &ip, const uint16_t &port) : _ip(ip), _port(port)
    {
    }
    ~User()
    {
    }
    string ip(){ return _ip; }
    uint16_t port(){ return _port; }
private:
    string _ip;
    uint16_t _port;
};

class OnlineUser
{
public:
    OnlineUser() {}
    ~OnlineUser() {}
    void addUser(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        users.insert(make_pair(id, User(ip, port)));
    }
    void delUser(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        users.erase(id);
    }
    bool isOnline(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        return users.find(id) == users.end() ? false : true;
    }
    void broadcastMessage(int sockfd, const string &ip, const uint16_t &port, const string &message)
    {
        for (auto &user : users)
        {
            struct sockaddr_in client;
            bzero(&client, sizeof(client));

            client.sin_family = AF_INET;
            client.sin_port = htons(user.second.port());
            client.sin_addr.s_addr = inet_addr(user.second.ip().c_str());
            string s = ip + "-" + to_string(port) + "# ";
            s += message;
            sendto(sockfd, s.c_str(), s.size(), 0, (struct sockaddr *)&client, sizeof(client));
        }
    }

private:
    unordered_map<string, User> users;
};

在回调函数中,如果收到的消息是online,就把用户添加进哈希表。如果是offline,就从哈希表中删除

if (message == "online") onlineuser.addUser(clientip, clientport);
if (message == "offline") onlineuser.delUser(clientip, clientport);
OnlineUser onlineuser;
void routeMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
    if (message == "online") onlineuser.addUser(clientip, clientport);
    if (message == "offline") onlineuser.delUser(clientip, clientport);
    if (onlineuser.isOnline(clientip, clientport))
    {
        // 消息的路由
        onlineuser.broadcastMessage(sockfd, clientip, clientport, message);
    }
    else
    {
        struct sockaddr_in client;
        bzero(&client, sizeof(client));

        client.sin_family = AF_INET;
        client.sin_port = htons(clientport);
        client.sin_addr.s_addr = inet_addr(clientip.c_str());

        string response = "你还没有上线,请先上线,运行: online";

        sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
    }
}

多线程:客户端udpClient.hpp不能立即收到消息打印出来,为了解决这个问题我们可以使用多线程,一个线程专门接收消息,一个线程专门发送消息:让主线程负责发送消息,子线程负责接收消息:

static void *readMessage(void *args)
        {
            int sockfd = *(static_cast<int *>(args));
            pthread_detach(pthread_self());
            while (true)
            {
                char buffer[1024];
                struct sockaddr_in temp;
                socklen_t temp_len = sizeof(temp);
                size_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &temp_len);
                if (n >= 0)
                    buffer[n] = 0;
                cout << buffer << endl;
            }

            return nullptr;
        }

        void run()
        {
            pthread_create(&_reader, nullptr, readMessage, (void *)&_sockfd);

            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(_serverip.c_str());
            server.sin_port = htons(_serverport);

            string message;
            char cmdline[1024];
            while (!_quit)
            {
                fprintf(stderr, "Enter# ");
                fflush(stderr);
                fgets(cmdline, sizeof(cmdline), stdin);
                cmdline[strlen(cmdline)-1] = 0;
                message = cmdline;
                sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
            }
        }

image-20230530213639848

image-20230530213650726

UDP之Windows与Linux

UDP的实现可以在不同的平台上进行交互的,在这里我们以Linux充当服务端,windows充当客户端,进行连通

windows端代码:

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)


#include <iostream>
#include <string>
#include <cstring>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")

using namespace std;
uint16_t serverport = 8080;
string serverip = "8.134.152.121";

int main()
{
	WSAData wsd;
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		cout << "WSAStartup Error = " << WSAGetLastError() << endl;
		return 0;
	}
	else
	{
		cout << "WSAStartup Success" << endl;
	}

	SOCKET csock = socket(AF_INET, SOCK_DGRAM, 0);
	if (csock == SOCKET_ERROR)
	{
		cout << "socket Error = " << WSAGetLastError() << endl;
		return 1;
	}
	else
	{
		cout << "socket success" << endl;
	}
	struct sockaddr_in server;
	memset(&server, 0, sizeof(server));
	server.sin_family = AF_INET;
	server.sin_port = htons(serverport);
	server.sin_addr.s_addr = inet_addr(serverip.c_str());
#define NUM 1024
	char inbuffer[NUM];
	string line;
	while (true)
	{
		cout << "Please Enter#";
		getline(cin, line);

		int n = sendto(csock, line.c_str(), line.size(), 0, (struct sockaddr*)&server, sizeof(server));
		if (n < 0)
		{
			cerr << "sendto error!" << endl;
			break;
		}
		struct sockaddr_in peer;
		int peerlen = sizeof(peer);
		inbuffer[0] = 0;
		n = recvfrom(csock, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&peer, &peerlen);
		if (n > 0)
		{
			inbuffer[n] = 0;
			cout << "server 返回的消息是#" << inbuffer << endl;
		}
		else break;
	}
	closesocket(csock);
	WSACleanup();
	return 0;
}

Linux端代码:

pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

namespace Server
{
    using namespace std;
    static const string defaultIp = "0.0.0.0";
    static const int gnum = 1024;
    enum  {USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,OPEN_ERR};

    typedef function<void (int,string,uint16_t,string)> func_t;

    class udpServer
    {
    public:
        udpServer(const func_t&cb,const uint16_t&port,const string&ip = defaultIp)
        :_callback(cb),_port(port),_ip(ip),_sockfd(-1)
        {}

        void initServer()
        {
            _sockfd = socket(AF_INET,SOCK_DGRAM,0);
            if(_sockfd == -1)
            {
                cerr<<"sdocket error: "<<errno<<" : "<<strerror(errno)<<endl;
                exit(SOCKET_ERR);
            }
            cout<<"socket success: "<<" : "<<_sockfd<<endl;

            struct sockaddr_in local;
            bzero(&local,sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = inet_addr(_ip.c_str());

            int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
            if(n == -1)
            {
                cerr<<"bind errpr: "<<errno<<" : "<<strerror(errno)<<endl;
                exit(BIND_ERR);
            }
        }

        void start()
        {
            char buffer[gnum];
            for(;;)
            {
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                ssize_t s = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
                if(s>0)
                {
                    buffer[s] = 0;
                    string clientip = inet_ntoa(peer.sin_addr);
                    uint16_t clientport = ntohs(peer.sin_port);
                    string message = buffer;

                    cout<<clientip<<"["<<clientport<<"]#"<< message<<endl;

                    _callback(_sockfd,clientip,clientport,message);
                }
            }
        }

        ~udpServer()
        {
            
        }

    private:
        int _sockfd;
        uint16_t _port;
        string _ip;
        func_t _callback;
    };
}
//udpServer.cc
#include <iostream>
#include <memory>
#include "udpServer.hpp"
using namespace std;
using namespace Server;

static void Usage(string proc)
{
    cout<<"\nUsage:\n\t"<<proc<<" locla_port\n\n";
}

void handlerMessage(int sockfd,string clientip,uint16_t clientport,string message)
{
    string response_message = message;
    response_message+=" [server echo]";

    struct sockaddr_in client;
    bzero(&client,sizeof(client));
    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str());
    sendto(sockfd,response_message.c_str(),response_message.size(),0,(struct sockaddr*)&client,sizeof(client));
}

int main(int argc,char*argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);

    std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage,port));
    usvr->initServer();
    usvr->start();
    return 0;
}

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

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

相关文章

Windows系统安全基线

文章目录 前言一、安全基线概述1.操作系统安全基线 二、系统账户安全1.控制密码安全2.密码策略3.账户锁定策略例&#xff1a;密码策略应用例&#xff1a;账户锁定策略应用 三、本地策略1.审核策略2.用户权限分配3.安全选项配置例&#xff1a;审核策略应用例&#xff1a;用户权限…

软件测试理论----软件测试六大测试类型

1、功能测试&#xff1a;关注功能是否正确 常见的关注点 1&#xff09;是否有不正确或者遗漏的功能 2&#xff09;是否满足用户的需求和系统隐藏的需求 3&#xff09;输入能否正确接收&#xff0c;能否输出正确结果 2、可用性测试&#xff1a;关注产品是否好用 常见的关注点…

03灰度变换与空间滤波

文章目录 3灰度变换与空间滤波3.1背景知识3.1.1灰度变换和空间滤波基础 3.2一些基本的灰度变换函数3.2.1图像反转3.2.2对数变换3.2.3幂律&#xff08;伽马&#xff09;变换3.2.4分段线性变换函数 3.3直方图处理3.3.1直方图均衡3.3.2直方图匹配&#xff08;规定化&#xff09;3.…

webpack配置排除打包

webpack配置排除打包 思路 打包时&#xff0c;不要把类似于element-ui第三方的这些包打进来 从网络上&#xff0c;通过url地址直接引入这些包 操作 &#xff08;1&#xff09;先找到 vue.config.js&#xff0c; 添加 externals 项&#xff0c;具体如下&#xff1a; config…

【JavaSE】异常 (异常抛出 异常的捕获 异常声明throws try-catch捕获并处理 finally 自定义异常)

文章目录 异常概念异常结构 异常处理异常抛出异常的捕获异常声明throwstry-catch捕获并处理finally 自定义异常 异常 概念 在程序运行过程中&#xff0c;可能会存在一些奇怪问题&#xff0c;例如&#xff1a;网络不畅&#xff0c;内存报警等等。所以在java中&#xff0c;将程…

NLP:文本预处理总览

1 用n-gram语言模型过滤低质量内容 使用n-gram语言模型对文本进行评估&#xff0c;从而过滤掉低质量的内容。具体来说&#xff0c;可以通过以下步骤进行&#xff1a; 1 将文本分成n-gram序列&#xff0c;其中n是一个整数。 2 使用已经训练好的n-gram语言模型对每个n-gram序列…

面试4年经验的测试员,开口就要25k,却连基础都不会,还不如招应届生!

公司前段缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在10-25k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。看简历很多都是4年工作经验&#xff0c;但面试…

冒泡排序实现(c++)

目录 冒泡排序简介&#xff1a; 冒泡排序原理&#xff1a; 动图演示&#xff1a; 代码实现&#xff1a; 冒泡排序简介&#xff1a; 冒泡排序&#xff0c;最优时间复杂度O(N)&#xff0c;平均时间复杂度O(N^2)&#xff0c;最差空间复杂度O(N)&#xff0c;平均时间复杂度O(1)…

基于ISIC数据集的皮肤病黑色素瘤分类研究与实现

摘要&#xff1a; 本项目利用残差网络结构对皮肤病图像进行分类&#xff0c;提高了皮肤病诊断的准确度。并结合Flask框架实现一个简单的Web应用&#xff0c;用户可以上传图像文件&#xff0c;经过预处理最后将预测结果显示在界面上。通过该系统&#xff0c;医生和研究人员可以快…

STM32单片机(三)第三节:GPIO输入

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

【计算机网络】 1.4——计算机网络的性能指标(重要!含计算)

计算机网络的性能指标 速率 数据量单位 bit Byte KB(2^10B) MB GB TB 数据量比较特别&#xff0c;使用2进制 kB210B而不是103&#xff0c;注意计算题 比特率/数据率单位 bpskb/s(10^3bps) Mb/s Gb/s Tb/s 带宽 *带宽在模拟信号系统中的意义 信号所包含的各种不同频率成…

WebSocket的那些事(4-Spring中的STOMP支持详解)

目录 一、序言二、Spring对STOMP支持的相关注解三、聊天Demo代码示例1、前端页面chat.html2、相关实体(1) 请求消息参数(2) 响应消息内容(3) 自定义认证用户信息 3、自定义用户认证拦截器4、WebSocket消息代理配置5、ChatController控制器 四、测试用例1、指定用户定时消息推送…

【实用篇】SpringCloud02

文章目录 SpringCloud020.学习目标1.Nacos配置管理1.1.统一配置管理1.1.1.在nacos中添加配置文件1.1.2.从微服务拉取配置 1.2.配置热更新1.2.1.方式一1.2.2.方式二 1.3.配置共享1&#xff09;添加一个环境共享配置2&#xff09;在user-service中读取共享配置3&#xff09;运行两…

TCP通信(复习)

目录 TCP通信实现过程 1、socket函数与通信域 socket函数 参 数 bind函数 与 通信结构体 bind函数 参数 通信地址族与同届结构体 通用地址族结构体 IPV4地址族结构体 listen函数与accept函数 listen函数 accept函数 参 数 作 用 要实现进程间的通信必备&#xff1…

Map、Set和哈希表(数据结构系列14)

目录 前言&#xff1a; 1.搜索树 1.1概念 1.2插入 1.3查找 1.4删除 1.5二叉搜索树整体代码展示 2. Map和Set的讲解 2.1 Map的说明 2.1.1Map的方法 2.2 Set 的说明 2.2.1Set的方法 3.哈希表 3.1哈希表的概念 3.2哈希冲突 3.3冲突的避免 3.4哈希冲突的解决 3.4…

企业物资管理系统的设计与实现(ASP.NET,SQL)

论文阐述了企业物资管理系统的设计与实现&#xff0c;并对该系统的需求分析及系统需要实现的设计方法作了介绍。该系统的基本功能包括用户登录&#xff0c;修改密码&#xff0c;物资的基本信息管理&#xff0c;出入库和损坏的管理已经综合查询等功能。 4.1 用户登录模块的实现 …

【滤波】非线性滤波

本文主要翻译自rlabbe/Kalman-and-Bayesian-Filters-in-Python的第9章节09-Nonlinear-Filtering&#xff08;非线性滤波&#xff09;。 %matplotlib inline#format the book import book_format book_format.set_style()介绍 我们开发的卡尔曼滤波器使用线性方程组&#xff0…

【C++】类和对象——拷贝构造函数的概念、拷贝构造函数的特征

文章目录 1.拷贝构造函数1.1拷贝构造函数的概念1.2拷贝构造函数的特征 1.拷贝构造函数 在前面我们已经介绍了构造函数和析构函数的作用和使用方法&#xff0c;而拷贝构造函数则是在对象初始化时调用的一种特殊构造函数。拷贝构造函数可以帮助我们创建一个新的对象&#xff0c;该…

互联网中的web3.0和gpt有何联系?

文章目录 ⭐前言⭐web 3.0&#x1f496; web1.0-web3.0的概念 ⭐chatgpt&#x1f496; gpt的概念 ⭐总结⭐结尾 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享互联网中的web3.0和gpt的关系。 互联网的发展 第一台计算机的出现 世界上第一台通用计算机“ENIAC”于…

C语言(扫雷)

扫雷 开发过程开发思路菜单界面游戏界面的打印雷的随机产生扫雷以及判断胜利条件代码整合 开发过程 准备工作效果展示 准备工作&#xff1a; game.h 一个头文件–>声明函数 test.c 为主文件 game.c 为功能函数实现文件 效果展示 开发思路 菜单界面 游戏界面打印&…