46 udp网络程序

查询网络服务的命令

netstat -nlup
n: 显示数字
a:显示所有
u:udp服务
p:显示pid

在这里插入图片描述
Recv-Q收到的数量,本地ip和远端ip,00表示可以收到任何地址

网络聊天

服务端

定义一个server类,成员保存ip地址,端口号,文件描述符,是否运行
在这里插入图片描述
ip和端口号构造时设置默认值,“0.0.0.0”和“8000”
在这里插入图片描述

  • init函数内初始化套接字,返回文件描述符,然后绑定到os。
    在这里插入图片描述
    socket的三个参数,协议族IPv4,套接字类型面向字节流,协议0自动选择,面向字节流默认匹配udp
    返回值描述符,是后续功能传入的第一个参数

  • bind函数建立本地捆绑
    在这里插入图片描述
    第二个参数需要sockaddr类型,这个通用类型需要用sockaddr_in类型强转
    在这里插入图片描述

设置sockaddr_in的结构内容:
初始化为0
设置协议族
ip地址,需要将点分十进制转换为32位整数类型uint32_t,然后转为网络字节序
端口转为网络字节序

ip地址转化方法
在这里插入图片描述
先定义ip结构体,每个成员8位,总共4字节
字符串转整数:
1.先去掉.符号,切割为四个整数
2.然后定义一个uint32变量,强转为ip结构体
3.每个部分对应分割的字符串,将字符串转整数

整数转字符串:
1.强制转换为ip结构
2.将每部分转为字符串加上.拼接为点分

  • run函数内接收和发送消息

在这里插入图片描述在这里插入图片描述

rev第一个参数标识符,第二个接收存放的缓冲区,缓冲区大小,调用方式,0表示阻塞直到有数据或错误,对方的协议结构,协议结构的长度(socklen_t类型)
send参数,标识符,发送内容,内容的大小,调用方式0,不使用特殊行为,对方的协议结构,协议的长度

启动服务端查看有没有服务
在这里插入图片描述

ip和port说明
ip可以固定设置,如果是本机ip,虚拟机是可以的,云服务器禁止绑定公网ip。ip填0的意思是,凡是发给我主机的数据,都根据端口号向上交付,如果有两张网卡,固定设置,只会收到发往一个的,0可以收到任一个网卡的,可以提前设置ip,也可以在绑定时设置

addrin.sin_addr.s_addr = INADDR_ANY;

【0,1023】是系统内定的端口号,有固定的应用层协议使用,http:80 https:443 mysql:3306。。也有几个这种特殊的,所以设置端口号时尽量往大一点设置

#pragma once
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"enum
{SOCK_ERR = 1
};uint16_t defaultport = 8000;
std::string defaultip = "0.0.0.0";Log log;
class server
{ 
public:server(const std::string ip = defaultip, const uint16_t port = defaultport){_ip = ip;_port = port;_sockfd = 0;}void init(){//1.创建套接字int _sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){log.logmessage(fatal, "socket create error:%d", _sockfd);exit(SOCK_ERR);}log.logmessage(info, "socket create success:%d", _sockfd);//绑定sockaddr_in addrin;bzero(&addrin, sizeof(addrin));addrin.sin_family = AF_INET;addrin.sin_addr.s_addr = htonl(inet_addr(_ip.c_str()));addrin.sin_port = htons(_port);addrin.sin_addr.s_addr = INADDR_ANY;int ret = bind(_sockfd, (const sockaddr*)&addrin, sizeof(addrin));if (ret < 0){log.logmessage(fatal, "bind error:%s", strerror(errno));}log.logmessage(info, "bind success:%d", ret);}void run(){_isrunning = true;char buff[1024];while (_isrunning){sockaddr client;socklen_t len = sizeof(client);ssize_t n = recvfrom(_sockfd, buff, sizeof(buff) - 1, 0, &client, &len);if (n < 0){//log.logmessage(warning, "recv error:%s", strerror(errno));}buff[1024] = 0;std::string echo_string = buff;echo_string = "client#" + echo_string;sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, &client, len);}}~server(){if (_sockfd > 0)close(_sockfd);}private:int _sockfd;      // 网路文件描述符uint16_t _port;   // 任意地址bind 0std::string _ip;  // 表明服务器进程的端口号bool _isrunning;
};

客户端

先初始化端口和ip,这个安装软件使用的时候已经默认设好了。这里定为服务器的ip和刚刚启动服务的端口

设置目标协议addr结构,内容为上面初始化
在这里插入图片描述

打开套接字
在这里插入图片描述

客户端也是需要绑定的,只不过不是用户显示绑定,由os随机选择,一个端口号只能被一个进程绑定,对服务端也是如此。客户端的port只需要保证主机上的唯一性,在首次发送数据的时候会自动绑定

和服务端一样,发送和接受消息,只不过客户端先发送再接收
在这里插入图片描述

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <strings.h>using namespace std;
int main()
{uint16_t port = 8000;string ip = "ip";sockaddr_in in;bzero(&in, sizeof(in));in.sin_family = AF_INET;in.sin_addr.s_addr = inet_addr(ip.c_str());in.sin_port = htons(port);int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){cout << "socket error" << endl;}//client 需要绑定,但不显示绑定,由os自由选择string message;char buff[1024];while (true){cout << "please enter: ";getline(cin , message);sendto(sockfd, message.c_str(), message.size(), 0, (const sockaddr*)&in, sizeof(in));sockaddr_in rec;socklen_t len = sizeof(rec);ssize_t s = recvfrom(sockfd, buff, sizeof(buff) - 1, 0, (sockaddr*)&rec, &len);if (s > 0){buff[s] = 0;cout << buff << endl;}}close(sockfd);
}

注意
hton转字节序的函数是根据本机字节序情况转换,如果本机是小端调用几次会转几次

打开云主机端口
如果程序没问题,但收发不到消息,可以试着打开云主机的防火墙端口,以腾讯云为例:
登录控制台,查看详情,防火墙添加规则
在这里插入图片描述

服务端自定义数据处理
使用自定义函数类型,run调用实例出的函数,实现数据处理方法的自定义

using func_t = std::function<std::string(const std::string &)>; //定义函数类型

std::string handler(const std::string& str)
{std::string s = "server get message: ";s += str;std::cout << s << std::endl;return s;
}

命令bash

服务端可以收到信息了,也可以将收到的内容当做其他形式。如果将收到的内容当做xshell命令,就会执行命令
run执行的函数替换。popen函数可以创建子进程通信管道,将传入的命令创建子进程执行。将命令执行的结果添加到字符串内,回显发送给客户端

std::string execute(const std::string& cmd)
{//safecheck 不想执行的命令安全检查FILE* fp = popen(cmd.c_str(), "r");if (fp == nullptr){perror("popen");return "error";}string ret;char buff[4096];while (true){char *ok = fgets(buff, sizeof(buff), fp);if (ok == nullptr){break;}ret += buff;}pclose(fp);return ret;
}

在这里插入图片描述

本地环回地址

只会在本地协议走一遍,不会发向网络,通常用来测试

xshell

这就是为什么要用xshell登录,就相当于客户端,将命令发到了远端的服务器,接受到字符串执行命令,然后将结果发送回xshell显示

后台里有一个ssh的tcp服务,端口号22号接收发送的命令
在这里插入图片描述

win客户端

实现让win客户端和linux服务端网络通讯
win需要包含头文件WinSock2.h头文件和ws2_32.lib网络库
上面的头文件要在windows.h这个头文件之前,不然会报错

在这里插入图片描述

首先初始化网络版本信息,addr协议内容
在这里插入图片描述
打开套接字
在这里插入图片描述

收发功能和前面一样
在这里插入图片描述
清理数据
在这里插入图片描述
在这里插入图片描述
两边汉字编码不一样,可能会显示有问题

#include <iostream>
#include <string>
#include <WinSock2.h>
//#include <Windows.h>//关闭安全警告
#pragma warning(disable:4996)
//包含网络库
#pragma comment(lib, "ws2_32.lib")using namespace std;
string ip = "ip";
uint16_t port = 8000;int main()
{//初始化socket数据WSADATA wsd;WSAStartup(MAKEWORD(2, 2), &wsd);sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_addr.S_un.S_addr = inet_addr(ip.c_str());server.sin_port = htons(port);//套接字SOCKET sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == SOCKET_ERROR){cout << "sock error" << endl;return 1;}string message;char buff[1024];while (true){cout << "please enter: ";getline(cin, message);sendto(sockfd, message.c_str(), message.size(), 0, (const sockaddr*) & server, sizeof(server));sockaddr_in temp;int len = sizeof(temp);int s = recvfrom(sockfd, buff, 1023, 0, (sockaddr*)&temp, &len);if (s > 0){buff[s] = 0;cout << buff << endl;}}closesocket(sockfd);WSACleanup();return 0;
}

网络聊天室 (多线程)

聊天室功能需要用户名标识每一个主机,这里用对方的ip和port作为区分,发言的前缀
用一个无序map容器,string作为索引,值存addr结构
在这里插入图片描述

将收到的addr结构转为主机序列加入到显示屏幕的内容

在这里插入图片描述
在这里插入图片描述

检查如果是新的主机就加入到_online结构,并显示新主机加入
在这里插入图片描述
遍历容器内容,将最新消息发送给所有用户
在这里插入图片描述
sendto可以自动转网络序列,所以容器内容不需要转换

上面可以实现接收到不同用户的消息,但有个问题,客户端的getline没有内容时是一直阻塞住的,不输入内容的时候收不到其他消息。所以用多线程,一个线程收消息,一个线程发消息

两个线程都需要文件描述符和addr结构
定义一个结构体,将初始化的内容赋值给结构体在这里插入图片描述

在这里插入图片描述
这时可以显示其他人的消息,但自己因为发送和接收的顺序无法正常接收。需要将输入和输出的终端分开

在这里插入图片描述

/dev/pts 这个文件里保存了所有打开的终端

在这里插入图片描述

0号终端重定向到6,显示到了1号页面中,所以文件6代表了下面这个终端,5是上面的终端。在接收消息后想显示到上面的终端,下面的用来发送,由于线程的文件描述共享,所以将标准错误重定向到5,在下面打开服务端

在这里插入图片描述

上面的内容可以简化,打开两个端口,提前确定哪个用来显示,另一个启动客户端,将标准错误重定向到显示端
在这里插入图片描述

服务端

#pragma once
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include "log.hpp"enum
{SOCK_ERR = 1
};uint16_t defaultport = 8000;
std::string defaultip = "0.0.0.0";
using func_t = std::function<std::string(const std::string &)>; //定义函数类型
//typedef std::function<std::string(const std::string &)> func_tLog log;
class server
{ 
public:server(const std::string ip = defaultip, const uint16_t port = defaultport){_ip = ip;_port = port;_sockfd = 0;}void init(){//1.创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){log.logmessage(fatal, "socket create error:%d", _sockfd);exit(SOCK_ERR);}log.logmessage(info, "socket create success:%d", _sockfd);//绑定struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = inet_addr(_ip.c_str());local.sin_port = htons(_port);//local.sin_addr.s_addr = INADDR_ANY;int ret = bind(_sockfd, (const sockaddr*)&local, sizeof(local));if (ret < 0){log.logmessage(fatal, "bind error:%s", strerror(errno));}log.logmessage(info, "bind success:%d", ret);}void checkuser(const struct sockaddr_in& sock, const string ip, const uint16_t port){auto it = _online.find(ip);if (it == _online.end()){_online.insert({ip, sock});cout << "[" << ip << ":" << port << "] add new user" << endl;}}void broadcast(const string& message, string ip, uint16_t port){for (const auto user : _online){socklen_t len = sizeof(user.second);sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)(&user.second), len);}}void run(func_t func){_isrunning = true;char buff[1024];while (_isrunning){sockaddr_in client;socklen_t len = sizeof(client);ssize_t n = recvfrom(_sockfd, buff, sizeof(buff) - 1, 0, (sockaddr*)&client, &len);if (n < 0){//log.logmessage(warning, "recv error:%s", strerror(errno));continue;}buff[n] = 0;//获取端口号和ipstring ip = inet_ntoa(client.sin_addr);// 网络字节序转主机uint16_t port = ntohs(client.sin_port);checkuser(client, ip, port);std::string echo_string = "[" + ip + ":" + to_string(port) + "]: " + buff;broadcast(echo_string, ip, port);// std::cout << echo_string << endl;//  echo_string = func(echo_string);//sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (const sockaddr *)&client, len);}}~server(){if (_sockfd > 0)close(_sockfd);}private:int _sockfd;      // 网路文件描述符uint16_t _port;   // 任意地址bind 0std::string _ip;  // 表明服务器进程的端口号bool _isrunning;//用ip检索unordered_map<string, struct sockaddr_in> _online;  //注册主机
};

客户端

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <fcntl.h>using namespace std;string path = "/dev/pts/5";
struct thread_data
{int _sockfd;struct sockaddr_in _server;string _ip;
};void *send_message(void * temp)
{thread_data *td = (struct thread_data*)temp;string message;cout << "welcome " << td->_ip << endl;while (true){cout << "please enter: ";getline(cin, message);sendto(td->_sockfd, message.c_str(), message.size(), 0, (const sockaddr *)&td->_server, sizeof(td->_server));}}void* recv_message(void* temp)
{// int fd = open(path.c_str(), O_WRONLY);// if (fd < 0)// {//     perror("open");// }// dup2(fd, 2);thread_data *td = (struct thread_data *)temp;char buff[1024];sockaddr_in rec;socklen_t len = sizeof(rec);while (true){ssize_t s = recvfrom(td->_sockfd, buff, sizeof(buff) - 1, 0, (sockaddr *)&rec, &len);if (s > 0){buff[s] = 0;cerr << buff << endl;}}//close(fd);
}int main()
{thread_data td;uint16_t port = 8000;string ip = "ip";sockaddr_in in;bzero(&in, sizeof(in));td._server.sin_family = AF_INET;td._server.sin_addr.s_addr = inet_addr(ip.c_str());td._ip = ip;td._server.sin_port = htons(port);td._sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (td._sockfd < 0){cout << "socket error" << endl;}//client 需要绑定,但不显示绑定,由os自由选择pthread_t tid_send;pthread_t tid_recv;pthread_create(&tid_send, nullptr, send_message, &td);pthread_create(&tid_recv, nullptr, recv_message, &td);  pthread_join(tid_send, nullptr);pthread_join(tid_recv, nullptr);close(td._sockfd);
}

地址转换函数

介绍基于ipv4的socket网络编程,struct in_addr sin_addr表示32位的ip地址,但是我们通常用点分十进制字符串表示ip地址,一下函数可以在字符串表示和in_addr表示之间转换

其中inet_pton和inet_ntop不仅可以转换ipv4的in_addr,还可以转换ipv6的in6_addr,因此接口是void* addrptr

字符串转in_addr函数:
在这里插入图片描述在这里插入图片描述
in_addr转字符串函数:
在这里插入图片描述在这里插入图片描述

关于inet_ntoa

这个函数返回一个char*,自己内部申请了一块内存保存ip的结果,那么需不需要手动释放呢?

在这里插入图片描述
man手册说返回结果放在了静态存储区,不需要手动释放

当多次调用时,两个不同ip都会转换为相同的结果,以最后一个为准
因为inet_ntoa把结果放到内部的静态缓存区,第二次调用的时候会覆盖上一次结果。APUE中,明确提出inet_ntoa不是线程安全的函数,但是centos7上测试,没有出现问题,可能是内部实现加了互斥锁,在多线程环境下,推荐使用inet_ntop,这个函数由用户提供缓冲区结果,可以规避线程安全的问题

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM 类加载机制

JVM 类加载机制分为五个部分&#xff1a;加载&#xff0c;验证&#xff0c;准备&#xff0c;解析&#xff0c;初始化&#xff0c;下面我们就分别来看一下这五个过程。 加载 加载是类加载过程中的一个阶段&#xff0c;这个阶段会在内存中生成一个代表这个类的 java.lang.class 对…

基于yolov5+streamlit目标检测演示系统设计

YOLOv5与Streamlit&#xff1a;智能目标检测可视化展示介绍 随着人工智能技术的飞速发展&#xff0c;目标检测技术已成为推动智能化社会进步的关键技术之一。在众多目标检测算法中&#xff0c;YOLOv5以其卓越的性能和实时性&#xff0c;成为了业界的佼佼者。与此同时&#xff…

UDP多播

1 、多播的概念 多播&#xff0c;也被称为组播&#xff0c;是一种网络通信模式&#xff0c;其中数据的传输和接收仅在同一组内进行。多播具有以下特点&#xff1a; 多播地址标识一组接口&#xff1a;多播使用特定的多播地址&#xff0c;该地址标识一组接收数据的接口。发送到多…

实现红黑树

目录 红黑树的概念 红黑树的节点结构定义 红黑树的插入 红黑树的验证 实现红黑树完整代码 红黑树的概念 红黑树 &#xff0c;是一种 二叉搜索树 &#xff0c;但 在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是 Red 或 Black 。 通过对 任何一条从根到叶子的…

Leetcode39.组合总和

文章目录 题目描述解题思路重复子集剪枝 代码 题目 参考题解 题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返…

【JavaSE】/*初识Java*/

目录 一、了解 Java 语言 二、Java 语言的重要性 2.1 使用程度 2.2 工作领域 三、Java 语言的特性 四、Java 的基础语法 五、可能遇到的错误 六、第一个 java 程序代码解析 七、Java 注释 八、Java 标识符 九、Java 关键字 一、了解 Java 语言 Java 是由 Sun Micr…

图论(洛谷刷题)

目录 前言&#xff1a; 题单&#xff1a; P3386 【模板】二分图最大匹配 P1525 [NOIP2010 提高组] 关押罪犯 P3385 【模板】负环 P3371 【模板】单源最短路径&#xff08;弱化版&#xff09; SPFA写法 Dij写法&#xff1a; P3385 【模板】负环 P5960 【模板】差分约束…

【iOS开发】—— 初识锁

【iOS开发】—— 初识锁 线程安全锁的种类自旋锁定义原理自旋锁缺点OSSpinLock&#xff08;自旋锁&#xff09; 互斥锁os_unfair_lockpthread_mutexNSLockNSRecusiveLockSemaphore信号量synchronized 总结两种之间的区别和联系&#xff1a; 线程安全 当一个线程访问数据的时候…

双向冒泡法,可以只求最大最小值

int BiBubbleSort(int Arr[],int n,int maxnum){int left0,rightn-1;int i;bool notDone true;int temp;if(n<2)return -1;while(left<right&&notDone){ notDone false; //设置未发生交换标志 for(ileft;i<right;i){if(Arr[i]>Arr[i1]){//swap(Arr[…

Python-VBA函数之旅-staticmethod函数

目录 一、staticmethod函数的常见应用场景 二、staticmethod函数使用注意事项 三、如何用好staticmethod函数&#xff1f; 1、staticmethod函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a; https://blog…

HackMyVM-VivifyTech

目录 信息收集 arp nmap nikto whatweb WEB web信息收集 wpscan feroxbuster hydra 提权 系统信息收集 横向渗透 git提权 get root 信息收集 arp ┌──(root㉿0x00)-[~/HackMyVM] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 08:00:27:9d:6d:7b, …

【密评】 | 商用密码应用安全性评估从业人员考核题库(9/58)

Hill密码是重要古典密码之一&#xff0c;其加密的核心思想的是&#xff08;&#xff09;。 A.线性变换 B.非线性变换 C.循环移位 D.移位 著名的Kerckhoff原则是指&#xff08;&#xff09;。 A.系统的保密性不但依赖于对加密体制或算法的保密&#xff0c;而且依赖于密钥 B.系统…

即插即用篇 | YOLOv8 引入 Strip Pooling | 重新思考场景解析的空间池化

本改进已集成到 YOLOv8-Magic 框架。 空间池化已被证明在捕获像素级预测任务的长距离上下文信息方面非常有效,如场景解析。在本文中,我们超越了通常具有N N规则形状的常规空间池化,重新思考空间池化的构成,引入了一种新的池化策略,称为条带池化,它考虑了一个长而窄的核,…

C++ 中的 lambda 表达式

1.概念 lambda表达式实际上是一个匿名类的成员函数&#xff0c;该类由编译器为lambda创建&#xff0c;该函数被隐式地定义为内联。因此&#xff0c;调用lambda表达式相当于直接调用匿名类的operator()函数&#xff0c;这个函数可以被编译器内联优化&#xff08;建议&#xff0…

C++的数据结构(二)

一、链表的基本概念 链表&#xff08;Linked List&#xff09;是一种物理存储单元上非连续的、非顺序的线性数据结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列节点&#xff08;链表中每一个元素称为节点&#xff09;组成&#xff0c;节点…

py黑帽子学习笔记_网络编程工具

tcp客户端 socket.AF_INET表示使用标准IPV4地址和主机名 SOCK_STREAM表示这是一个TCP客户端 udp客户端 udp无需连接&#xff0c;因此不需要client.connect这种代码 socket.SOCK_DGRAM是udp的 tcp服务端 server.listen(5)表示设置最大连接数为5 发现kill server后端口仍占用…

牛客NC404 最接近的K个元素【中等 二分查找+双指针 Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/b4d7edc45759453e9bc8ab71f0888e0f 知识点 二分查找&#xff1b;找到第一个大于等于x的数的位置idx;然后从idx开始往两边扩展Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、…

Shell编程规范与变量

Shell 什么是Shell&#xff1f; 就是与内核沟通的界面、应用程序等等。比如你要播放音乐&#xff0c;你的计算机通过你在Shell输入的打开音乐的命令&#xff0c;Shell在告诉操作系统的内核用户希望打开音乐&#xff0c;内核在通过cpu调度、内存管理、磁盘输入输出等工作&#…

JAVA课程设计

一&#xff1a;Java连接mysql数据库 1.1点击进入mysql jar包下载官网 MySQL :: MySQL Community Downloads 将下载好的压缩包进行解压 解压之后下图就是连接数据库所用到的jar包&#xff1a; 将jar包复制到IDEA所用的项目下&#xff0c;放置jar包的目录为lib&#xff0c;需要…

NSS刷题

[SWPUCTF 2021 新生赛]jicao 类型&#xff1a;PHP、代码审计、RCE 主要知识点&#xff1a;json_decode()函数 json_decode()&#xff1a;对JSON字符串解码&#xff0c;转换为php变量 用法&#xff1a; <?php $json {"ctf":"web","question"…