C语言项目实战FTP文件传输(windows网络编程基础)

news2025/1/31 2:46:48

文章目录

  • 前言
  • 一、客户端和服务端介绍
  • 二、客户端和服务器之间进行通信的过程
      • 客户端和服务器建立通信的流程
      • 通信过程的示例图
      • 流程说明
  • 三、客户端代码编写
      • 代码解释
  • 四、服务端代码编写
      • 代码解释
  • 总结


前言

本篇文章开始将带大家来学习FTP文件传输助手的项目实现,这个项目的实现会包含网络编程中的内容和学习,希望通过这套文章可以带大家完成这样的一个项目。

一、客户端和服务端介绍

在Windows中进行网络编程,客户端和服务端的实现主要依赖于Windows Socket(Winsock)API。以下是对Windows中网络编程的客户端和服务端的详细介绍,包括关键功能、常用技术和示例代码。

客户端
功能
用户交互

提供界面供用户输入服务器地址、端口和消息。
接收用户输入,并将请求发送到服务端。
网络通信

建立与服务端的连接,发送请求并接收响应。
处理数据传输中的各种情况,如连接超时、数据丢失等。
数据处理

对接收到的数据进行处理和展示。
例如,显示服务端返回的响应信息。
技术架构
Winsock API

使用Windows提供的Winsock API进行网络编程。
包括函数如socket(), connect(), send(), recv(), closesocket()等。
网络协议

支持多种网络协议,最常用的是TCP和UDP。
TCP用于可靠的点对点连接,UDP用于快速的无连接通信。

服务端
功能
连接管理

监听特定端口,接受客户端连接请求。
管理多个客户端连接,处理并发请求。
请求处理

接收客户端请求,进行相应的处理。
例如,返回特定数据或执行某些操作。
数据传输

发送和接收数据,确保通信的正确性和可靠性。
处理数据传输中的错误和异常情况。

二、客户端和服务器之间进行通信的过程

客户端和服务器建立通信的流程

建立客户端和服务器之间的通信通常包括以下几个步骤:

  1. 初始化

    • 客户端和服务器都需要初始化网络库。例如,在Windows系统中,这通常涉及调用 WSAStartup() 来初始化 Winsock 库。
  2. 创建套接字

    • 客户端服务器都需要创建一个网络套接字。这个套接字是进行网络通信的基础。套接字的创建过程涉及指定协议族(如IPv4)、套接字类型(如流式套接字用于TCP)和协议。
  3. 绑定(仅服务器端)

    • 服务器端需要将套接字绑定到本地的IP地址和端口号。这一步骤确定了服务器将监听哪个端口上的连接请求。
  4. 监听(仅服务器端)

    • 服务器端将套接字设置为监听状态,等待客户端的连接请求。这是通过 listen() 函数实现的。
  5. 连接(仅客户端)

    • 客户端使用 connect() 函数尝试连接到服务器端。客户端需要知道服务器的IP地址和端口号。
  6. 接受连接(仅服务器端)

    • 服务器端接受客户端的连接请求。这是通过 accept() 函数实现的,一旦连接建立,服务器和客户端可以开始数据交换。
  7. 数据交换

    • 客户端服务器端可以通过套接字发送和接收数据。这是通过 send()recv() 函数完成的。
  8. 关闭连接

    • 当数据交换完成后,客户端服务器端都可以关闭套接字,释放相关资源。这是通过 closesocket() 函数完成的。

通信过程的示例图

以下是一个简化的通信过程示意图,展示了客户端和服务器之间的基本交互:

客户端 (Client)                   服务器 (Server)
-----------------                   -----------------
    |                                      |
    | (1) 初始化网络库                    |
    |----------------->                   |
    |                                      |
    | (2) 创建套接字                       |
    |----------------->                   |
    |                                      |
    | (3) 连接到服务器                   |
    |----------------->                   |
    |                                      |
    |                       (4) 初始化网络库|
    |                                      |
    |                       (5) 创建套接字 |
    |                                      |
    |                       (6) 绑定套接字 |
    |                                      |
    |                       (7) 监听连接   |
    |                                      |
    |                                      |<---- (8) 监听连接
    |                                      |
    | (9) 接受连接                         |
    |<-----------------                   |
    |                                      |
    | (10) 数据交换                       |
    |<----------------->                   |
    |                                      |
    | (11) 关闭连接                       |
    |----------------->                   |
    |                                      |
    |                                      |
    |                                      |<---- (12) 关闭连接

流程说明

  1. 客户端服务器端初始化网络库。
  2. 客户端服务器端创建网络套接字。
  3. 客户端连接到服务器端
  4. 服务器端初始化网络库(如果还没初始化)。
  5. 服务器端创建网络套接字。
  6. 服务器端将套接字绑定到本地的IP地址和端口号。
  7. 服务器端设置套接字为监听状态,等待客户端连接。
  8. 服务器端监听来自客户端的连接请求。
  9. 客户端发起连接请求,服务器端接受连接。
  10. 客户端服务器端进行数据交换。
  11. 客户端关闭连接。
  12. 服务器端关闭连接。

这个流程和示意图描述了一个基本的网络通信过程,其中客户端和服务器之间的每个步骤都很关键,以确保数据的正确传输和连接的管理。

三、客户端代码编写

这里会使用到网络调试助手来充当服务器,等待接收到客户端的连接。

在这里插入图片描述

程序代码:

下面是一个简化的Windows网络编程中的客户端代码示例,使用C语言编写。这个客户端连接到服务器,发送一条消息,然后接收服务器的响应。代码中包含详细注释以帮助理解每个步骤。

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib") // 链接 Winsock 库

#define SERVER_ADDRESS "127.0.0.1" // 服务器IP地址
#define SERVER_PORT 8080           // 服务器端口号

void error_exit(const char *message) {
    fprintf(stderr, "Error: %s. Error code: %d\n", message, WSAGetLastError());
    WSACleanup();
    exit(EXIT_FAILURE);
}

int main() {
    WSADATA wsaData;
    SOCKET sock;
    struct sockaddr_in server;
    char message[] = "Hello, Server!";
    char buffer[512];
    int bytesReceived;

    // 1. 初始化 Winsock
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        error_exit("Failed to initialize Winsock");
    }

    // 2. 创建套接字
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock == INVALID_SOCKET) {
        error_exit("Failed to create socket");
    }

    // 3. 配置服务器地址结构
    server.sin_family = AF_INET;
    server.sin_port = htons(SERVER_PORT);
    server.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);

    // 4. 连接到服务器
    if (connect(sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
        error_exit("Failed to connect to server");
    }

    // 5. 发送数据
    if (send(sock, message, sizeof(message), 0) == SOCKET_ERROR) {
        error_exit("Failed to send data");
    }

    // 6. 接收数据
    bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (bytesReceived == SOCKET_ERROR) {
        error_exit("Failed to receive data");
    }

    // 7. 确保接收到的数据是以空字符结束的
    buffer[bytesReceived] = '\0';

    // 8. 打印接收到的消息
    printf("Received from server: %s\n", buffer);

    // 9. 关闭套接字
    closesocket(sock);

    // 10. 清理 Winsock
    WSACleanup();

    return 0;
}

代码解释

  1. 初始化 Winsock

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        error_exit("Failed to initialize Winsock");
    }
    
    • 使用 WSAStartup() 初始化 Winsock 库,以便使用网络功能。
  2. 创建套接字

    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock == INVALID_SOCKET) {
        error_exit("Failed to create socket");
    }
    
    • 使用 socket() 创建一个TCP套接字,AF_INET 表示IPv4,SOCK_STREAM 表示流式套接字(TCP),IPPROTO_TCP 表示使用TCP协议。
  3. 配置服务器地址结构

    server.sin_family = AF_INET;
    server.sin_port = htons(SERVER_PORT);
    server.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);
    
    • 设置服务器的地址、端口号和IP地址。
  4. 连接到服务器

    if (connect(sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
        error_exit("Failed to connect to server");
    }
    
    • 使用 connect() 函数连接到服务器。如果连接失败,打印错误信息并退出程序。
  5. 发送数据

    if (send(sock, message, sizeof(message), 0) == SOCKET_ERROR) {
        error_exit("Failed to send data");
    }
    
    • 使用 send() 函数将数据发送到服务器。如果发送失败,打印错误信息并退出程序。
  6. 接收数据

    bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (bytesReceived == SOCKET_ERROR) {
        error_exit("Failed to receive data");
    }
    
    • 使用 recv() 函数接收从服务器返回的数据。如果接收失败,打印错误信息并退出程序。
  7. 确保接收到的数据是以空字符结束的

    buffer[bytesReceived] = '\0';
    
    • 在接收到的数据末尾添加空字符,以确保它是一个有效的字符串。
  8. 打印接收到的消息

    printf("Received from server: %s\n", buffer);
    
    • 打印从服务器接收到的消息。
  9. 关闭套接字

    closesocket(sock);
    
    • 使用 closesocket() 关闭套接字,释放资源。
  10. 清理 Winsock

    WSACleanup();
    
    • 使用 WSACleanup() 清理 Winsock 库,完成网络编程的清理工作。

这个示例展示了如何在Windows平台上创建一个简单的客户端应用程序,与服务器进行通信。代码中的每个步骤都包括详细的注释,帮助理解网络编程中的基本操作。

调试结果:
在这里插入图片描述

四、服务端代码编写

下面是一个简单的Windows服务端代码示例,使用C语言编写。这个服务端程序会监听特定端口,接收来自客户端的消息,并发送一个响应消息。代码中包含详细注释以帮助理解每个步骤。

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib") // 链接 Winsock 库

#define SERVER_PORT 8080 // 服务器端口号
#define BUFFER_SIZE 512 // 缓冲区大小

void error_exit(const char *message) {
    fprintf(stderr, "Error: %s. Error code: %d\n", message, WSAGetLastError());
    WSACleanup();
    exit(EXIT_FAILURE);
}

int main() {
    WSADATA wsaData;
    SOCKET serverSocket, clientSocket;
    struct sockaddr_in server, client;
    int clientSize = sizeof(client);
    char buffer[BUFFER_SIZE];
    int bytesReceived;

    // 1. 初始化 Winsock
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        error_exit("Failed to initialize Winsock");
    }

    // 2. 创建套接字
    serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverSocket == INVALID_SOCKET) {
        error_exit("Failed to create socket");
    }

    // 3. 配置服务器地址结构
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY; // 监听所有网络接口
    server.sin_port = htons(SERVER_PORT);

    // 4. 绑定套接字到端口
    if (bind(serverSocket, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
        error_exit("Failed to bind socket");
    }

    // 5. 监听传入连接
    if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
        error_exit("Failed to listen on socket");
    }

    printf("Waiting for a connection...\n");

    // 6. 接受客户端连接
    clientSocket = accept(serverSocket, (struct sockaddr*)&client, &clientSize);
    if (clientSocket == INVALID_SOCKET) {
        error_exit("Failed to accept client connection");
    }

    printf("Client connected.\n");

    // 7. 接收数据
    bytesReceived = recv(clientSocket, buffer, BUFFER_SIZE - 1, 0);
    if (bytesReceived == SOCKET_ERROR) {
        error_exit("Failed to receive data");
    }

    // 8. 确保接收到的数据是以空字符结束的
    buffer[bytesReceived] = '\0';

    // 9. 打印接收到的消息
    printf("Received from client: %s\n", buffer);

    // 10. 发送响应数据
    const char *response = "Hello, Client!";
    if (send(clientSocket, response, strlen(response), 0) == SOCKET_ERROR) {
        error_exit("Failed to send data");
    }

    // 11. 关闭客户端套接字
    closesocket(clientSocket);

    // 12. 关闭服务器套接字
    closesocket(serverSocket);

    // 13. 清理 Winsock
    WSACleanup();

    return 0;
}

代码解释

  1. 初始化 Winsock

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        error_exit("Failed to initialize Winsock");
    }
    
    • 使用 WSAStartup() 初始化 Winsock 库,以便使用网络功能。
  2. 创建套接字

    serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverSocket == INVALID_SOCKET) {
        error_exit("Failed to create socket");
    }
    
    • 使用 socket() 创建一个TCP套接字,AF_INET 表示IPv4,SOCK_STREAM 表示流式套接字(TCP),IPPROTO_TCP 表示使用TCP协议。
  3. 配置服务器地址结构

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY; // 监听所有网络接口
    server.sin_port = htons(SERVER_PORT);
    
    • 设置服务器的地址、端口号,并指定 INADDR_ANY 以监听所有网络接口。
  4. 绑定套接字到端口

    if (bind(serverSocket, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
        error_exit("Failed to bind socket");
    }
    
    • 使用 bind() 将套接字绑定到指定的IP地址和端口。如果绑定失败,打印错误信息并退出程序。
  5. 监听传入连接

    if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
        error_exit("Failed to listen on socket");
    }
    
    • 使用 listen() 函数开始监听传入的连接请求。SOMAXCONN 表示允许的最大连接请求队列长度。
  6. 接受客户端连接

    clientSocket = accept(serverSocket, (struct sockaddr*)&client, &clientSize);
    if (clientSocket == INVALID_SOCKET) {
        error_exit("Failed to accept client connection");
    }
    
    • 使用 accept() 函数接受来自客户端的连接请求。clientSocket 是与客户端通信的套接字。
  7. 接收数据

    bytesReceived = recv(clientSocket, buffer, BUFFER_SIZE - 1, 0);
    if (bytesReceived == SOCKET_ERROR) {
        error_exit("Failed to receive data");
    }
    
    • 使用 recv() 函数接收来自客户端的数据。如果接收失败,打印错误信息并退出程序。
  8. 确保接收到的数据是以空字符结束的

    buffer[bytesReceived] = '\0';
    
    • 在接收到的数据末尾添加空字符,以确保它是一个有效的字符串。
  9. 打印接收到的消息

    printf("Received from client: %s\n", buffer);
    
    • 打印从客户端接收到的消息。
  10. 发送响应数据

    const char *response = "Hello, Client!";
    if (send(clientSocket, response, strlen(response), 0) == SOCKET_ERROR) {
        error_exit("Failed to send data");
    }
    
    • 使用 send() 函数向客户端发送响应数据。如果发送失败,打印错误信息并退出程序.
  11. 关闭客户端套接字

    closesocket(clientSocket);
    
    • 使用 closesocket() 关闭与客户端的连接。
  12. 关闭服务器套接字

    closesocket(serverSocket);
    
    • 使用 closesocket() 关闭服务器套接字,释放资源。
  13. 清理 Winsock

    WSACleanup();
    
    • 使用 WSACleanup() 清理 Winsock 库,完成网络编程的清理工作。

运行结果:

在这里插入图片描述

总结

本篇文章就讲解到这里,在学习网络编程的时候大家需要先将基础知识掌握牢固再进行编程训练。

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

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

相关文章

【51单片机仿真】基于51单片机设计的温湿度采集检测系统仿真源码文档视频——文末资料下载

演示 目录 1.系统功能 2.背景介绍 3.硬件电路设计 4.软件设计 4.1 主程序设计 4.2 温湿度采集模块程序设计 4.3 LCD显示屏程序设计 5.系统测试 6.结束语 源码、仿真、文档视频等资料下载链接 1.系统功能 该系统通过与AT89C51单片机、LCD1602显示屏和DHT11温湿度传感器…

makefile(1)

文章目录 概述简单Makefilemakefile三要素makefile&#xff1a;工作原理编译程序 小结 概述 makefile这个编译在c中很常见&#xff0c;看下操作系统&#xff0c;都是基于makefile编译的。来简单看下&#xff0c; 这个就不看了&#xff0c;每一个模块下都有一个对应的makefile…

前端:javaScript基础知识

一、输出语句 ①window.alert()&#xff1a;弹出框 ②ducument.write()&#xff1a;写入HTML输入 ③console.log()&#xff1a;在浏览器控制台输出 二、声明变量 ①使用关键字var来声明变量 ②javaScript是一门弱类型语言&#xff0c;变量可以存放不同类型的值 ③变量需要遵…

【Python】字符串练习题及代码示例

1、使用while循环实现对字符串中每个字符进行输出。 代码示例&#xff1a; 2、请将代码实现如下进制的转换。 &#xff08;1&#xff09;v1675,请将v1转换为二进制。 代码&#xff1a; 注意&#xff1a;将十进制数转换为二进制数的方法是&#xff1a;bin(a),a是整型&#x…

使用 addRouteMiddleware 动态添加中间

title: 使用 addRouteMiddleware 动态添加中间 date: 2024/8/4 updated: 2024/8/4 author: cmdragon excerpt: 摘要&#xff1a;文章介绍了Nuxt3中addRouteMiddleware的使用方法&#xff0c;该功能允许开发者动态添加路由中间件&#xff0c;以实现诸如权限检查、动态重定向及…

html+css 实现hover双层按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文…

AI人工智能开发环境配置

AI人工智能 为什么使用Python来开发AI 人工智能被认为是未来的趋势技术。 已经有了许多应用程序。 因此&#xff0c;许多公司和研究人员都对此感兴趣。 但是这里出现的主要问题是&#xff0c;在哪种编程语言中可以开发这些 AI 应用程序&#xff1f; 有各种编程语言&#xff0c…

Stable Diffusion绘画 | 图生图-基础使用介绍—提示词反推

按默认设置直接出图 拖入图片值图生图框中&#xff0c;保持默认设置&#xff0c;直接生成图片&#xff0c;出图效果如下&#xff1a; 因为重绘幅度0.7&#xff0c;所出图片与原图有差异&#xff0c;但整体的框架构图与颜色与原图类似。 输入关键词后出图 在正向提示词中输入…

PMP–知识卡片--Scrum框架

定义 Scrum框架包含由产品负责人、开发团队、敏捷专家构成的Scrum团队&#xff0c;以及活动工件。框架中的每一个组件都服务于一个特定的目标&#xff0c;且是Scrum成功和运用的基本要素。 Scrum的规则将角色、活动和工件绑定在一起&#xff0c;管理它们之间的关系和交互。 …

JavaEE 第2节 线程安全知识铺垫1

目录 一、通过jconsole.exe查看线程状态的方法 二、Thread类的几种常见属性 三、线程状态 一、通过jconsole.exe查看线程状态的方法 通过jconsole查看线程状态非常实用的方式 只要你安装了jdk&#xff0c;大致按照这个目录就可以找到这个可执行程序&#xff1a; 然后双击这…

YOLOv10改进 | 注意力篇 | YOLOv10引入24年最新的上下文锚注意力机制(CAA),并构建C2f_CAA(适用于小目标检测)

1. CAA介绍 1.1 摘要:遥感图像中的目标检测面临着越来越多的挑战,包括目标尺度的变化和背景的多样性。先前的方法试图通过扩大骨干的空间感受野来解决这些挑战,无论是通过大核卷积还是扩张卷积。然而,前者通常会引入相当大的背景噪声,而后者则会产生过于稀疏的特征表示。…

[CTF]-PWN:ORW题型综合解析

经典ORW&#xff1a; 例题&#xff08;极客大挑战 2019 Not Bad&#xff09;&#xff1a; 这里使用mmap函数创造了一个内存映射区域 从地址0x123000开始&#xff0c;大小位0x1000 权限为可写可执行&#xff08;可读0x1&#xff0c;可写0x2&#xff0c;可执行0x3&#xff09;…

Halcon 二维匹配

匹配&#xff1a;在训练图像中呈现一个所谓的模板。系统从这个模板中派生出一个模型&#xff0c;通过使用该模型来定位于搜索图像中的相似模板对象。此方法能够处理打光、杂乱、位置和旋转变换的图像。 匹配优点&#xff1a;鲁棒性和灵活性。匹配不需要对目标进行任何形式的分割…

50 选择结构

常见的选择结构有单分支选择结构、双分支选择结构、多分支选择结构及嵌套的分支结构&#xff0c;也可以构造跳转表来实现类似的逻辑。循环结构和异常处理结构中也可以实现带有 else 子句&#xff0c;可以看作特殊形式的选择结构。 所有的 Python 合法表达式都可以作为条件表达…

MySQL数据库入门:掌握数据管理的关键步骤!

前言 竹竹零将带您深入探索MySQL数据库的基础知识和实用技能&#xff0c;一同走进Java逻辑控制语句的 学习吧&#xff01;&#xff01;&#xff01; 本节目标 数据库的操作&#xff1a;创建数据库、删除数据库常用数据类型表的操作&#xff1a;创建表、删除表 1. 数据库的操作…

跨域以及解决方案

禹神&#xff1a;一小时彻底搞懂跨域&解决方案_哔哩哔哩_bilibili该直播回放对跨域的多种解决方案进行了梳理&#xff0c;内容包括&#xff1a;分析同源策略、分析预检请求、跨域的多种解决方案&#xff08;CORS、JSONP、代理&#xff09;、同时也从后端人员角度梳理了各种…

计算机毕业设计选题推荐-学生作业管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

一次性下发100w的优惠券/短信/二维码,兼顾线程池参数可配置

1、场景需求分析 针对6.18,11.11这种场景,平台一次性发布500w张优惠券,或者对于锁单用户统一发下100w张确认信息,同时我们平时有抢购茅台的场景,京东一次性发布10w个验证码,主要是针对高并发多线程大数据批处理任务的场景,一般用于二维码、优惠券、邮件、短信等场景。 …

MyBatis的适用场合与优势

MyBatis的适用场合与优势 1、适用场合2、优势 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、适用场合 SQL灵活控制&#xff1a; 适合需要精确控制SQL语句生成和执行的项目。 高性能需求&#xff1a; 对于性能要求高的系统&#xff0c;…

02:【stm32】工程模板的创建

工程模板的创建 1、创建工程文件2、创建STM32的启动文件3、Keil软件里面添加启动文件4、创建我们编写程序的文件夹5、创建存放标准库函数的文件夹6、Keil软件的一些设置 1、创建工程文件 2、创建STM32的启动文件 ①创建Start文件夹用来保存STM32的启动文件 ②下载STM32的固件库…