当前位置: 首页 > news >正文

02.07 TCP服务器与客户端的搭建

一.思维导图

二.使用动态协议包实现服务器与客户端

1. 协议包的结构定义

首先,是协议包的结构定义。在两段代码中,pack_t结构体都被用来表示协议包:

typedef struct Pack {int size;        // 记录整个协议包的实际大小enum Type type;  // 协议包的类型char buf[2048];  // 存储实际数据int count;       // 记录buf中已使用的字节数
} pack_t;

enum Type定义了协议包的类型,例如TYPE_REGISTTYPE_LOGIN,分别表示注册和登录操作。

2. 服务器端的协议包解析

在服务器端代码中,read_pack函数负责解析从客户端接收到的协议包。该函数的主要步骤如下:

  1. 读取数据大小:从buf中读取前2个字节,表示接下来要读取的数据大小。

  2. 读取数据:根据读取到的数据大小,从buf中读取相应长度的数据。

  3. 处理数据:将读取到的数据打印出来。

    void read_pack(pack_t* pack) {char *buf = pack->buf;int readed_size = 0;while(1) {short data_size = *(short*)(buf + readed_size);if(data_size == 0) {printf("数据解析完毕\n");break;}readed_size += 2;char temp[data_size + 1];memset(temp, 0, data_size + 1);memcpy(temp, buf + readed_size, data_size);readed_size += data_size;printf("temp = %s\n", temp);}
    }

    3. 客户端的协议包构建

    在客户端代码中,append函数负责将数据按照协议格式添加到pack_t结构体中。该函数的主要步骤如下:

  4. 记录数据长度:将数据的长度存储在buf的前2个字节中。

  5. 存储数据:将数据本身存储在buf中。

  6. 更新协议包大小:更新pack_t结构体中的sizecount字段。

    void append(pack_t* pack, const char* data) {char* buf = pack->buf;int len = strlen(data);*(short*)(buf + pack->count) = len;pack->count += 2;memcpy(buf + pack->count, data, len);pack->count += len;pack->size = pack->count + 8;
    }

    4. 客户端与服务器的交互

    在客户端代码中,用户输入账号和密码后,append函数将数据添加到协议包中,然后通过write函数将协议包发送给服务器。

    while(1) {pack_t pack = {0};pack.type = TYPE_LOGIN;char name[20] = "";char pswd[20] = "";printf("请输入账号:");scanf("%19s", name);while(getchar() != 10);printf("请输入密码:");scanf("%19s", pswd);while(getchar() != 10);append(&pack, name);append(&pack, pswd);write(client, &pack, pack.size);
    }

    在服务器端,read函数接收客户端发送的协议包,并调用read_pack函数解析数据。

    while(1) {int pack_size = 0;read(client, &pack_size, 4);pack_t pack = {0};read(client, (char*)&pack + 4, pack_size - 4);pack.size = pack_size;read_pack(&pack);
    }

    5. 总结

    通过这两段代码,我们可以看到如何在C语言中实现一个简单的网络协议包的构建与解析。服务器端负责接收和解析协议包,而客户端则负责构建和发送协议包。这种设计模式在网络编程中非常常见,理解其原理对于开发网络应用程序至关重要。

      6.完整代码

1>服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;enum Type {TYPE_REGIST,TYPE_LOGIN
};typedef struct Pack {int size;enum Type type;char buf[2048];int count;
} pack_t;void read_pack(pack_t *pack) {char *buf = pack->buf;int readed_size = 0;while (1) {short data_size = *(short *)(buf + readed_size);if (data_size == 0) {printf("数据解析完毕\n");break;}readed_size += 2;char temp[data_size + 1];memset(temp, 0, data_size + 1);memcpy(temp, buf + readed_size, data_size);readed_size += data_size;printf("temp = %s\n", temp);}
}int main(int argc, const char *argv[]) {if (argc != 2) {printf("请输入端口号\n");return 1;}int port = atoi(argv[1]);int server = socket(AF_INET, SOCK_STREAM, 0);addr_in_t addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr("0.0.0.0");if (bind(server, (addr_t *)&addr, sizeof(addr)) == -1) {perror("bind");return 1;}listen(server, 10);addr_in_t client_addr = {0};int client_addr_len = sizeof(client_addr);int client = accept(server, (addr_t *)&client_addr, &client_addr_len);if (client != -1) {printf("有客户端连接\n");}while (1) {int pack_size = 0;read(client, &pack_size, 4);pack_t pack = {0};read(client, (char *)&pack + 4, pack_size - 4);pack.size = pack_size;read_pack(&pack);}return 0;
}

2>客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;enum Type {TYPE_REGIST,TYPE_LOGIN
};typedef struct Pack {int size;enum Type type;char buf[2048];int count;
} pack_t;void append(pack_t *pack, const char *data) {char *buf = pack->buf;int len = strlen(data);*(short *)(buf + pack->count) = len;memcpy(buf + pack->count + 2, data, len);pack->count += 2;pack->count += len;pack->size = pack->count + 8;
}int main(int argc, const char *argv[]) {if (argc != 2) {printf("请输入端口号\n");return 1;}int port = atoi(argv[1]);int client = socket(AF_INET, SOCK_STREAM, 0);addr_in_t addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr("192.168.60.77");if (connect(client, (addr_t *)&addr, sizeof(addr)) == -1) {perror("connect");return 1;}while (1) {pack_t pack = {0};pack.type = TYPE_LOGIN;char name[20] = "";char pswd[20] = "";printf("请输入账号:");scanf("%19s", name);while (getchar() != '\n');printf("请输入密码:");scanf("%19s", pswd);while (getchar() != '\n');append(&pack, name);append(&pack, pswd);write(client, &pack, pack.size);}return 0;
}

三、基于以上代码,将读取到的一条条代码保存到链表中

1.服务器代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>// 定义网络地址结构体类型
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;// 定义协议包类型枚举
enum Type {TYPE_REGIST,  // 注册类型TYPE_LOGIN    // 登录类型
};// 定义协议包结构体
typedef struct Pack {int size;         // 协议包的总大小enum Type type;   // 协议包的类型char buf[2048];   // 数据缓冲区int count;        // 记录缓冲区中已使用的字节数
} pack_t;// 定义链表节点结构体
typedef struct Node {char* data;       struct Node* next; 
} Node;// 创建新节点
Node* create_node(const char* data) {Node* node = (Node*)malloc(sizeof(Node)); if (data != NULL) {size_t len = strlen(data) + 1;node->data = (char*)malloc(len * sizeof(char));if (node->data != NULL) {strcpy(node->data, data);} else {fprintf(stderr, "内存分配失败!\n");exit(EXIT_FAILURE);}
} else {// 处理输入为 NULL 的情况node->data = NULL;
} node->next = NULL; return node;
}// 将数据添加到链表
void append_to_list(Node** head, const char* data) {Node* new_node = create_node(data);if (*head == NULL) {*head = new_node; } else {Node* current = *head;while (current->next != NULL) {current = current->next;}current->next = new_node; }
}// 释放链表内存
void free_list(Node* head) {Node* current = head;while (current != NULL) {Node* next = current->next; free(current->data); free(current); current = next; }
}// 解析协议包并将数据保存到链表
void read_pack(pack_t* pack, Node** head) {char *buf = pack->buf; int readed_size = 0; // 记录已读取的字节数while(1) {short data_size = *(short*)(buf + readed_size); // 读取数据大小if(data_size == 0) {printf("数据解析完毕\n"); break;}readed_size += 2; char temp[data_size + 1]; memset(temp, 0, data_size + 1); memcpy(temp, buf + readed_size, data_size); readed_size += data_size; // 更新已读取的字节数printf("temp = %s\n", temp); append_to_list(head, temp); // 将数据添加到链表}
}// 主函数
int main(int argc, const char *argv[]) 
{if(argc != 2) {printf("请输入端口号\n"); return 1;}int port = atoi(argv[1]); // 将端口号字符串转换为整数// 创建服务器套接字int server = socket(AF_INET, SOCK_STREAM, 0);addr_in_t addr = {0};addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 绑定套接字到地址if(bind(server, (struct sockaddr*)&addr, sizeof(addr)) == -1) {perror("bind"); // 绑定失败return 1;}// 监听连接listen(server, 10);// 接受客户端连接addr_in_t client_addr = {0};int client_addr_len = sizeof(client_addr);int client = accept(server, (struct sockaddr*)&client_addr, &client_addr_len);if(client != -1) {printf("有客户端连接\n"); }Node* head = NULL; // 初始化链表头while(1) {int pack_size = 0;int ret = read(client, &pack_size, 4); // 读取协议包大小if (ret == -1) {printf("客户端断开连接\n"); break; // 退出循环}pack_t pack = {0};ret = read(client, (char*)&pack + 4, pack_size - 4); // 读取协议包数据if (ret == -1) {printf("客户端断开连接\n"); // 客户端断开连接break; // 退出循环}pack.size = pack_size; // 设置协议包大小read_pack(&pack, &head); // 解析协议包并保存数据到链表// 打印链表中的数据Node* current = head;printf("--------保存的数据如下--------\n");while (current != NULL) {printf("链表数据: %s\n", current->data);current = current->next;}printf("\n");}free_list(head); // 释放链表内存return 0;
}

2.客户端代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>typedef struct sockaddr_in addr_in_t;  
typedef struct sockaddr addr_t;        
typedef struct sockaddr_un addr_un_t;  // 定义消息类型枚举
enum Type{TYPE_REGIST,    // 注册类型TYPE_LOGIN      // 登录类型
};// 自定义数据包结构体
typedef struct Pack
{int size;           enum Type type;     char buf[2048];    int count;         
}pack_t;void append(pack_t* pack, const char* data)
{char *buf = pack->buf;int len = strlen(data);*(short*)(buf + pack->count) = len;  pack->count += 2;                   memcpy(buf + pack->count, data, len); pack->count += len;                   // 更新数据包总大小(头部8字节 + 数据长度)pack->size = pack->count + 8;         
}int main(int argc, const char *argv[])
{if(argc != 2){printf("Usage: %s <port>\n", argv[0]);return 1;}int port = atoi(argv[1]);  // 将端口参数转为整数// 创建TCP套接字int client = socket(AF_INET, SOCK_STREAM, 0);if(client == -1){perror("socket creation failed");return 1;}// 配置服务器地址信息addr_in_t addr = {0};addr.sin_family = AF_INET;                   addr.sin_port = htons(port);                addr.sin_addr.s_addr = inet_addr("192.168.126.233");// 连接服务器if(connect(client, (addr_t*)&addr, sizeof(addr)) == -1){perror("connect failed");return 1;}while(1){pack_t pack = {0};      pack.type = TYPE_LOGIN; char name[20] = "";char pswd[20] = "";// 获取用户名输入printf("请输入账号:");scanf("%19s", name);while(getchar() != '\n'); // 清空输入缓冲区// 获取密码输入printf("请输入密码:");scanf("%19s", pswd);while(getchar() != '\n'); // 清空输入缓冲区// 将用户名和密码打包到数据包append(&pack, name);append(&pack, pswd);// 发送整个数据包(发送大小为计算后的总大小)write(client, &pack, pack.size);}return 0;
}

相关文章:

02.07 TCP服务器与客户端的搭建

一.思维导图 二.使用动态协议包实现服务器与客户端 1. 协议包的结构定义 首先&#xff0c;是协议包的结构定义。在两段代码中&#xff0c;pack_t结构体都被用来表示协议包&#xff1a; typedef struct Pack {int size; // 记录整个协议包的实际大小enum Type type; …...

Jenkins数据备份到windows FTP服务器

文章目录 背景1. 安装配置 FileZilla Server&#xff08;Windows&#xff09;1.1 下载并安装 FileZilla Server1.2 配置 FTP 用户和共享目录 2. 安装并配置 FTP 客户端&#xff08;CentOS&#xff09;2.1 在 CentOS 安装 lftp 3. 编写 Jenkins 备份脚本3.1 赋予执行权限3.2 测试…...

【R语言】卡方检验

一、定义 卡方检验是用来检验样本观测次数与理论或总体次数之间差异性的推断性统计方法&#xff0c;其原理是比较观测值与理论值之间的差异。两者之间的差异越小&#xff0c;检验的结果越不容易达到显著水平&#xff1b;反之&#xff0c;检验结果越可能达到显著水平。 二、用…...

ASP.NET Core托管服务

目录 托管服务的异常问题 托管服务中使用DI 托管服务案例&#xff1a;数据的定时导出 场景&#xff0c;代码运行在后台。比如服务器启动的时候在后台预先加载数据到缓存&#xff0c;每天凌晨3点把数据导出到备份数据库&#xff0c;每隔5秒钟在两张表之间同步一次数据。托管服…...

HarmonyOS 5.0应用开发——全局自定义弹出框openCustomDialog

【高心星出品】 文章目录 全局自定义弹出框openCustomDialog案例开发步骤完整代码 全局自定义弹出框openCustomDialog CustomDialog是自定义弹出框&#xff0c;可用于广告、中奖、警告、软件更新等与用户交互响应操作。开发者可以通过CustomDialogController类显示自定义弹出框…...

如何在C++ QT 程序中集成cef3开源浏览器组件去显示网页?

文章目录 1. **准备工作**1.1 下载CEF31.2 配置Qt项目2. **集成CEF3到Qt窗口**2.1 创建Qt窗口容器2.2 初始化CEF33. **处理CEF3消息循环**4. **处理多进程架构**5. **完整代码示例**`main.cpp`6. **常见问题**6.1 黑屏问题6.2 窗口嵌入失败6.3 多进程调试7.**Github源码参考**8…...

深入讲解MyBatis

1. MyBatis 的背景和优势 背景&#xff1a;在 Java 开发中&#xff0c;传统的 JDBC 操作数据库代码繁琐&#xff0c;需要手动管理数据库连接、编写 SQL 语句、处理结果集等&#xff0c;开发效率低且容易出错。MyBatis 应运而生&#xff0c;它通过将 SQL 语句与 Java 代码分离&a…...

使用matlab 对传递函数分析bode图和阶跃函数

如果已知一个系统的传递函数&#xff0c;想看一下bode图&#xff0c;可以通过simulink 建模&#xff0c;但是simulink运行起来相对比较慢&#xff0c;我一般都是直接通过matlab 的m语言写脚本实现。可以快速的获得结果 如 我们有一个一阶低通传递函数 syswn/(swn) 在matlab中…...

2025牛客寒假算法基础集训营5(补题)

C 小L的位运算 显然&#xff0c;如果两次反置的价格小于等于交换的价格&#xff0c;那么直接全部反置就好了。 反之&#xff0c;由于交换一定低于两次反置&#xff0c;我们尽可能用交换来消去不正确的位置。不正确的位置类型只有00&#xff0c;01&#xff0c;10&#xff0c;11&…...

FaceFusion如何设置公开链接和端口

有时候我们想在局域网内的其他设备上使用 FaceFusion&#xff0c;这时候需要设置公开链接和端口。 当你运行 FaceFusion 的时候&#xff0c;会发现有这样的一段提示&#xff1a; To create a public link, set shareTrue in launch().但是这个提示是错的&#xff0c;如果你查…...

神经网络常见激活函数 6-RReLU函数

文章目录 RReLU函数导函数函数和导函数图像优缺点pytorch中的RReLU函数tensorflow 中的RReLU函数 RReLU 随机修正线性单元&#xff1a;Randomized Leaky ReLU 函数导函数 RReLU函数 R R e L U { x x ≥ 0 a x x < 0 \rm RReLU \left\{ \begin{array}{} x \quad x \ge 0…...

计算机网络面经

文章目录 基础HTTPHTTP报文结构 (注意)RPC和http的区别TCPTCP报文结构(注意)IP基础 HTTP HTTP报文结构 (注意) 请求行:请求方法get/post,url,http版本 请求头:用户标识,请求体长度,类型,cookie 请求体:内容 状态行:状态码,状态消息、(http版本) 响应头:内…...

Qt:常用控件

目录 控件概述 控件体系的发展 按钮类控件 QPushButton QRadioButton QCheckBox QToolButton 显示类控件 QLabel QLCDNumber QProgressBar QCalendarWidget 输入类控件 QLineEdit QTextEdit QComboBox QSpinBox QDateEdit & QTimeEdit QDial QSlider …...

算法设计-找第二大数(C++)

一、问题描述 用于在给定的整数数组中找到 第二大值。 二、详细代码 #include<iostream> #include<limits.h> using namespace std; //初始化最大值为a[0]&#xff0c;次大值为a[1]&#xff0c;遍历一次&#xff0c;每次比较并更新最大值和次大值&#xff0c;最…...

【C++高并发服务器WebServer】-14:Select详解及实现

本文目录 一、BIO模型二、非阻塞NIO忙轮询三、IO多路复用四、Select()多路复用实现 明确一下IO多路复用的概念&#xff1a;IO多路复用能够使得程序同时监听多个文件描述符&#xff08;文件描述符fd对应的是内核读写缓冲区&#xff09;&#xff0c;能够提升程序的性能。 Linux下…...

redis项目

短信登录 这一块我们会使用redis共享session来实现 商户查询缓存 通过本章节&#xff0c;我们会理解缓存击穿&#xff0c;缓存穿透&#xff0c;缓存雪崩等问题&#xff0c;让小伙伴的对于这些概念的理解不仅仅是停留在概念上&#xff0c;更是能在代码中看到对应的内容 优惠…...

Spring统一修改RequestBody

我们编写RestController时&#xff0c;有可能多个接口使用了相同的RequestBody&#xff0c;在一些场景下需求修改传入的RequestBody的值&#xff0c;如果是每个controller中都去修改&#xff0c;代码会比较繁琐&#xff0c;最好的方式是在一个地方统一修改&#xff0c;比如将he…...

NCV4275CDT50RKG 车规级LDO线性电压调节器芯片——专为新能源汽车设计的高可靠性电源解决方案

产品概述: NCV4275CDT50RKG 是一款符合 AEC-Q100 车规认证的高性能LDO&#xff08;低压差线性稳压器&#xff09;&#xff0c;专为新能源汽车的严苛工作环境设计。该芯片支持 输出调节为 5.0 V 或 3.3 V&#xff0c;最大输出电流达 450mA&#xff0c;具备超低静态电流&#xf…...

前端开发架构师Prompt指令的最佳实践

前端开发架构师Prompt 提示词可作为系统提示词使用&#xff0c;可基于用户的需求输出对应的编码方案。 本次提示词偏向前端开发的使用&#xff0c;如有需要可适当修改关键词和示例。 推荐使用 Cursor 中作为自定义指令使用Cline 插件中作为自定义指令使用在力所能及的范围内使…...

【AI实践】Windsurf AI编程voice对话应用

Android Studio新建一个安卓 hello world 应用&#xff0c;使用gitee插件&#xff0c;推送到个人gitee仓库。 本文要写一个基于GLM4-voice的一个语音对话应用&#xff0c;参考 bigmodel.cn平台和开发文档&#xff1a;智谱AI开放平台 第一轮 打开cursor&#xff0c;model切换到…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

前端高频面试题2:浏览器/计算机网络

本专栏相关链接 前端高频面试题1&#xff1a;HTML/CSS 前端高频面试题2&#xff1a;浏览器/计算机网络 前端高频面试题3&#xff1a;JavaScript 1.什么是强缓存、协商缓存&#xff1f; 强缓存&#xff1a; 当浏览器请求资源时&#xff0c;首先检查本地缓存是否命中。如果命…...

鸿蒙HarmonyOS 5军旗小游戏实现指南

1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;采用DevEco Studio实现&#xff0c;包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...

多元隐函数 偏导公式

我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式&#xff0c;给定一个隐函数关系&#xff1a; F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 &#x1f9e0; 目标&#xff1a; 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z​、 …...

JS红宝书笔记 - 3.3 变量

要定义变量&#xff0c;可以使用var操作符&#xff0c;后跟变量名 ES实现变量初始化&#xff0c;因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符&#xff0c;可以创建一个全局变量 如果需要定义…...