Servlet(一)

一.什么是servlet

Servlet 是一种实现动态页面的技术。 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app。

1.回顾 动态页面 vs 静态页面

静态页面也就是内容始终固定的页面。即使 用户不同/时间不同/输入的参数不同 , 页面内容也不会发生变化。(除非网站的开发人员修改源代码, 否则页面内容始终不变)。对应的, 动态页面指的就是 用户不同/时间不同/输入的参数不同, 页面内容会发生变化。
在这里插入图片描述
举个例子哔哩哔哩由于用户输入的参数或者时间不同页面是会发生变化的,所以它是一个动态页面。
构建动态页面的技术有很多, 每种语言都有一些相关的库/框架来做这件事。
Servlet 就是 Tomcat 这个 HTTP 服务器提供给 Java 的一组 API, 来完成构建动态页面这个任务。

2.servlet 主要做的工作

允许程序猿注册一个类, 在 Tomcat 收到某个特定的 HTTP 请求的时候, 执行这个类中的一些代码。
帮助程序猿解析 HTTP 请求, 把 HTTP 请求从一个字符串解析成HttpRequest 对象。
帮助程序猿构造 HTTP 响应. 程序猿只要给指定的 HttpResponse 对象填写一些属性字段, Servlet就会自动的安装 HTTP 协议的方式构造出一个 HTTP 响应字符串, 并通过 Socket 写回给客户端。

简而言之, Servlet 是一组 Tomcat 提供的 API, 让程序猿自己写的代码能很好的和 Tomcat 配合起来, 从而更简单的实现一个 web app。而不必关注 Socket, HTTP协议格式, 多线程并发等技术细节, 降低了 web app 的开发门槛, 提高了开发效率。

二.第一个 Servlet 程序

1. 创建项目

使用 IDEA 创建一个 Maven 项目。

  1. 菜单 -> 文件 -> 新建项目 -> Maven
    在这里插入图片描述
    一定要选择Maven!!! Maven是java中常用的构建工具,一个程序在编写过程中,往往需要涉及第三方库的依赖,另外还需要针对这个写好的程序进行打包****部署 Maven存在的意义就是能够方便的对依赖进行管理和打包。
    打开后看编译器左边:
    在这里插入图片描述

2. 引入依赖

当前的代码要使用servlet开发,servlet并不是java标准库自带的,而是Tomcat提供的api,所以得先把servlet的依赖获取过来。

  1. 在中央仓库中央仓库地址中搜索 “servlet”, 找到java servlet api
    在这里插入图片描述

点开java servlet api
在这里插入图片描述
选择3.1.0版本。Servlet和Tomcat是对应关系,如果Tomcat版本是8,那么Servlet版本是3.1,如果不匹配可能出现问题。
在这里插入图片描述
复制这段代码到pom文件

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope>
</dependency>

先写标签,然后标签里面把复制的代码写入。
在这里插入图片描述
在这里插入图片描述
点击Maven
在这里插入图片描述
点击该按钮刷新Maven,一般引入的依赖都得手动刷新
在这里插入图片描述
如果控制台出现这种情况说明你的环境没有配对
在这里插入图片描述
解决这个问题首先File->settings->Maven
在这里插入图片描述
点击Maven home path:
在这里插入图片描述
然后选择这个文件
在这里插入图片描述

同理选择usering settings file:
在这里插入图片描述
选择该文件然后点击刷新再看控制台,此时就没有报错了。
在这里插入图片描述

3. 创建目录

Tomcat对于Servlet项目是有一定的额外要求的,Maven是一个通用的工具,可以管理的不仅仅是Servlet项目,还可以管理其他项目,在这个基础上,在按照Servlet项目要求创建出一些特定的目录和文件。
在这里插入图片描述
web.xml文件的代码是固定的,编译器报红是正常的!!!
在这里插入图片描述

<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name></web-app>

4. 编写代码

在 java 目录中创建一个类 HelloServlet, 代码如下:
在这里插入图片描述
创建一个类 HelloServlet , 继承自 HttpServlet,如果HttpServlet类编译器报红说明Servlet的依赖没有正确的引入,如果颜色正常说明引入成功。
在这里插入图片描述
req代表的是这次HTTP请求的内容,resp代表响应的内容。doGet是重写父类的方法,该方法本身是HttpServlet的。doGet方法不需要手动调用,本质是一个回调函数,把这个方法写好后会交给Tomcat,Tomcat在收到一个合适的请求后就会调用doGet方法。调用doGet的时候,Tomcat就会解析这次的HTTP请求,生成一个HttpservletRequest对象(这个对象的属性啥的都是HTTP协议格式),同时Tomcat也会构造出一个空的HttpServletRequese对象,把这个对象传到doGet里面,doGet要做的事情就是根据请求计算响应。doGet里面的代码就会根据req里面的不同参数的细节,生成一个具体的resp对象(往空对象里面设计属性),tomcat就会根据这个响应对象转换符号http协议的响应报文返回给浏览器。

创建一个类 HelloServlet , 继承自 HttpServlet,在这个类上方加上 @WebServlet(“/hello”) 注解, 表示 Tomcat 收到的请求中, 路径为 /hello的请求才会调用 HelloServlet 这个类的代码.。(这个路径未包含 Context Path),重写 doGet 方法,doGet 的参数有两个, 分别表示收到的 HTTP 请求 和要构造的 HTTP 响应.。这个方法会在 Tomcat 收到 GET 请求时触发HttpServletRequest 表示 HTTP 请求。Tomcat 按照 HTTP 请求的格式把 字符串 格式的请求转成了一个 HttpServletRequest 对象。后续想获取请求中的信息(方法, url, header, body 等) 都是通过这个对象来获取。HttpServletResponse 表示 HTTP 响应。代码中把响应对象构造好(构造响应的状态码, header,body 等)。resp.getWriter() 会获取到一个流对象, 通过这个流对象就可以写入一些数据, 写入的数据会被构造成一个 HTTP 响应的 body 部分, Tomcat 会把整个响应转成字符串, 通过 socket 写回给浏览器。

这个代码虽然只有寥寥几行, 但是包含的信息量是巨大的。

  1. 我们的代码不是通过 main 方法作为入口了。main 方法已经被包含在 Tomcat 里, 我们写的代码会被 Tomcat 在合适的时机调用起来。
    此时我们写的代码并不是一个完整的程序, 而是 Tomcat 这个程序的一小部分逻辑。
  2. 我们随便写个类都能被 Tomcat 调用嘛? 满足啥样条件才能被调用呢?
    主要满足三个条件:
    a) 创建的类需要继承自 HttpServlet
    b) 这个类需要使用 @WebServlet 注解关联上一个 HTTP 的路径
    c) 这个类需要实现 doXXX 方法。
    当这三个条件都满足之后, Tomcat 就可以找到这个类, 并且在合适的时机进行调用。

5. 打包程序

使用 maven 进行打包. 打开 maven 窗口 (一般在 IDEA 右侧就可以看到 Maven 窗口, 如果看不到的话,可以通过 菜单 -> View -> Tool Window -> Maven 打开),然后展开 Lifecycle , 双击 package 即可进行打包.
在打包之前pom文件中加这段代码
在这里插入图片描述

 <packaging>war</packaging><build><finalName>java108</finalName></build>

这段代码的意思是打出来的包叫什么名字,finalname标签中写的是java108,所以包打出来的名字是java108。Tomact需要的是war包,并非jar包,所以往pom文件中写入war标签把jar包改为war包。

war 包和 jar 包的区别
jar 包是普通的 java 程序打包的结果. 里面会包含一些 .class 文件。
war 包是 java web 的程序, 里面除了会包含 .class 文件之外, 还会包含 HTML, CSS, JavaScript, 图
片, 以及其他的 jar 包。打成 war 包格式才能被 Tomcat 识别.

在这里插入图片描述
如果比较顺利的话, 能够看到 SUCCESS 这样的字样.
在这里插入图片描述
打包成功后, 可以看到在 target 目录下, 生成了一个 war 包.
在这里插入图片描述

然后打开这个war包的路径
在这里插入图片描述
点击Explorer在文件夹中找到打出来的包
在这里插入图片描述
把 war 包拷贝到 Tomcat 的 webapps 目录下。
在这里插入图片描述
当复制进来后Tomcat就能识别有新的webapp来了,对新的war包进行压缩。
## 7. 验证程序
此时通过浏览器访问 http://127.0.0.1:8080/java108/hello1就能看到结果了
在这里插入图片描述
在这里插入图片描述

8.更方便的部署方式

手动拷贝 war 包到 Tomcat 的过程比较麻烦. 我们还有更方便的办法。此处我们使用 IDEA 中的 Smart Tomcat 插件完成这个工作。

理解 “插件” (plugin)

在这里插入图片描述

天火 + 擎天柱 => 会飞的擎天柱。
天火在牺牲之前把自己变成了擎天柱的 “飞行插件”。在擎天柱需要起飞的时候就变成翅膀装在擎天柱身上。不需要起飞的时候就卸下来放到擎天柱的集装箱里。程序开发的时候也经常如此。像 IDEA 这样的程序虽然功能强大, 但是也无法面面俱到。对于一些特殊场景的功能, 开发者就可以开发一些 “插件”。如果需要这个插件, 就单独安装。插件就是对程序的一些特定场景, 做出一些特定的功能的扩展。

9.安装 Smart Tomcat 插件

  1. 菜单 -> 文件 -> Settings
    在这里插入图片描述
  2. 选择 Plugins, 选择 Marketplace, 搜索 “tomcat”, 点击 “Install”.
    在这里插入图片描述
  3. 安装完毕之后, 会提示 “重启 IDEA”

配置 Smart Tomcat 插件

  1. 点击右上角的 “Add Configuration”
    在这里插入图片描述
  2. 选择左侧的 “Smart Tomcat”
    在这里插入图片描述
  3. 在 Name 这一栏填写一个名字(可以随便写)在 Tomcat Server 这一栏选择 Tomcat 所在的目录. 其他的选项不必做出修改。
  4. 点击 OK 之后, 右上角变成了
    在这里插入图片描述

点击run:
在这里插入图片描述
然后浏览器输入url查看结果。
在这里插入图片描述
使用 Smart Tomcat 部署的时候, 我们发现 Tomcat 的 webapps 内部并没有被拷贝一个 war 包,
也没有看到解压缩的内容。Smart Tomcat 相当于是在 Tomcat 启动的时候直接引用了项目中的 webapp 和 target 目录。

三.几种常见的错误

1.出现 404

在这里插入图片描述
404 表示用户访问的资源不存在。大概率是 URL 的路径写的不正确。
错误实例2: 少写了 Servlet Path通过 /eat1访问服务器。
错误实例3: Servlet Path 写的和 URL 不匹配。
错误实例4: web.xml 写错了
清除 web.xml 中的内容
在这里插入图片描述

在 Tomcat 启动的时候也有相关的错误提示
在这里插入图片描述

2.出现 405

405 表示对应的 HTTP 请求方法没有实现.
错误实例: 没有实现 doGet 方法.

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
}

在这里插入图片描述

在浏览器地址栏直接输入 URL , 会发送一个 HTTP GET 请求。此时就会根据 /eat1/hello1 这个路径找到 HelloServlet 这个类. 并且尝试调用HelloServlet 的 doGet 方法。但是如果没有实现 doGet 方法, 就会出现上述现象。

3.出现 500

往往是 Servlet 代码中抛出异常导致的。
错误实例:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {String s = null;resp.getWriter().write(s.length());}}

重启 Tomcat 服务器。重新访问页面, 可以看到:
在这里插入图片描述
异常信息里已经提示了出现异常的代码是 HelloServlet.java 的第 13 。resp.getWriter().write(s.length());仔细检查这里的代码就可以看到空指针异常。

4.出现 “空白页面”

错误实例:
修改代码, 去掉 resp.getWritter().write() 操作。

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {System.out.println("hello");}}

重启服务器,访问服务器, 可以看到一个空白页面。
抓包可以看到, 响应 body 中的内容就是 “空数据”
在这里插入图片描述

5.小结

初学 Servlet, 遇到的这类问题会非常多。我们不光要学习 Servlet 代码的基本写法, 也要学习排查错误的思路。程序猿调试 BUG 如同医生诊病。
一个有经验的程序猿和一个新手程序猿相比, 最大的优势往往不是代码写的多好, 而是调试效率有多高.。同一个问题可能新手花了几天都无法解决的, 但是有经验的程序猿可能几分钟就搞定了。
你还觉得 “程序猿是吃青春饭” 嘛?熟悉 HTTP 协议能够让我们调试问题事半功倍。4xx 的状态码表示路径不存在, 往往需要检查 URL 是否正确, 和代码中设定的 Context Path 以及Servlet Path 是否一致。
5xx 的状态码表示服务器出现错误, 往往需要观察页面提示的内容和 Tomcat 自身的日志, 观察是否
存在报错。
出现连接失败往往意味着 Tomcat 没有正确启动, 也需要观察 Tomcat 的自身日志是否有错误提示。
空白页面这种情况则需要我们使用抓包工具来分析 HTTP 请求响应的具体交互过程。

Servlet 运行原理

在 Servlet 的代码中我们并没有写 main 方法, 那么对应的 doGet 代码是如何被调用的呢? 响应又是如何返回给浏览器的?

Tomcat 的定位

我们自己的实现是在 Tomcat 基础上运行的。
在这里插入图片描述

当浏览器给服务器发送请求的时候, Tomcat 作为 HTTP 服务器, 就可以接收到这个请求。HTTP 协议作为一个应用层协议, 需要底层协议栈来支持工作. 如下图所示:
在这里插入图片描述
更详细的交互过程可以参考下图:
在这里插入图片描述

  1. 接收请求:
    用户在浏览器输入一个 URL, 此时浏览器就会构造一个 HTTP 请求。
    这个 HTTP 请求会经过网络协议栈逐层进行 封装 成二进制的 bit 流, 最终通过物理层的硬件设备转换成光信号/电信号传输出去。
    这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达目标主机(这个过程也需要网络层和数据链路层参与)。
    服务器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成HTTP 请求. 并交给 Tomcat 进程进行处理(根据端口号确定进程)
    Tomcat 通过 Socket 读取到这个请求(一个字符串), 并按照 HTTP 请求的格式来解析这个请求, 根据请求中的 Context Path 确定一个 webapp, 再通过 Servlet Path 确定一个具体的 类.。再根据当前请求的方法 (GET/POST/…), 决定调用这个类的 doGet 或者 doPost 等方法。 此时我们的代码中的doGet / doPost 方法的第一个参数 HttpServletRequest 就包含了这个 HTTP 请求的详细信息。
  1. 根据请求计算响应:

在我们的 doGet / doPost 方法中, 就执行到了我们自己的代码. 我们自己的代码会根据请求中的一
些信息, 来给 HttpServletResponse 对象设置一些属性. 例如状态码, header, body 等。

  1. 返回响应:

我们的 doGet / doPost 执行完毕后, Tomcat 就会自动把 HttpServletResponse 这个我们刚设置好的对象转换成一个符合 HTTP 协议的字符串, 通过 Socket 把这个响应发送出去。此时响应数据在服务器的主机上通过网络协议栈层层 封装, 最终又得到一个二进制的 bit 流, 通过物理层硬件设备转换成光信号/电信号传输出去。
这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达浏览器所在的主机(这个过程也需要网络层和数据链路层参与)。
浏览器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成HTTP 响应, 并交给浏览器处理。
浏览器也通过 Socket 读到这个响应(一个字符串), 按照 HTTP 响应的格式来解析这个响应. 并且把body 中的数据按照一定的格式显示在浏览器的界面上。

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

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

相关文章

从 Microsoft 官网下载 Windows 10

方法一&#xff1a; 打开 Microsoft 官网&#xff1a; 打开开发人员工具&#xff08;按 F12 或右键点击“检查”&#xff09;。 点击“电脑模拟手机”按钮&#xff0c;即下图&#xff1a; 点击后重新加载此网页&#xff0c;即可看到下载选项。

Palo Alto Networks Expedition 未授权SQL注入漏洞复现(CVE-2024-9465)

0x01 产品介绍&#xff1a; Palo Alto Networks Expedition 是一款强大的工具&#xff0c;帮助用户有效地迁移和优化网络安全策略&#xff0c;提升安全管理的效率和效果。它的自动化功能、策略分析和可视化报告使其在网络安全领域中成为一个重要的解决方案。 0x02 漏洞描述&am…

windows下安装、配置neo4j并服务化启动

第一步&#xff1a;下载Neo4j压缩包 官网下载地址&#xff1a;https://neo4j.com/download-center/ &#xff08;官网下载真的非常慢&#xff0c;而且会自己中断&#xff0c;建议从以下链接下载&#xff09; 百度网盘下载地址&#xff1a;链接&#xff1a;https://pan.baid…

周易解读:八卦02,八卦所代表的基本事物

八 卦02 上一节&#xff0c;我是讲完了八卦的卦象的画法的问题。这一节&#xff0c;我来尝试着去讲解八卦所代表的自然事物。 八卦是谁发明的呢&#xff1f;根据《周易说卦传》的说法&#xff0c;八卦是伏羲发明的。伏羲氏仰观天文&#xff0c;俯察地理&#xff0c;从中提取…

项目模块二:日志宏

一、代码展示 二、补充知识 1、LOG(level, format, ...) format 是用于宏识别格式化&#xff0c;类似于 printf("%s", str); 里面的 "%s" ... 不定参&#xff0c;传入宏的参数除了 level, format, 还有不确定个数的参数。 2、红色 \ 由于宏只能写在一…

链上相遇,节点之间的悸动与牵连

公主请阅 1. 返回倒数第 k 个节点1.1 题目说明1.2 题目分析1.3 解法一代码以及解释1.3 解法二代码以及解释 2.相交链表2.1 题目说明示例 1示例 2示例 3 2.2 题目分析2.3 代码部分2.4 代码分析 1. 返回倒数第 k 个节点 题目传送门 1.1 题目说明 题目名称&#xff1a; 面试题 02…

15分钟学 Go 第 10 天:函数参数和返回值

第10天&#xff1a;函数参数和返回值 目标&#xff1a;理解函数如何传递参数 在Go语言中&#xff0c;函数是程序的基本构建块。了解如何传递参数和返回值是编写高效、可复用代码的重要步骤。本文将详细讲解函数参数的类型、传递方式以及如何处理返回值&#xff0c;辅以代码示…

DP—子数组,子串系列 第一弹 -最大子数组和 -环形子数组的最大和 力扣

你好&#xff0c;欢迎阅读我的文章~ 个人主页&#xff1a;Mike 所属专栏&#xff1a;动态规划 ​ 53. 最大子数组和 最大子数组和 ​ 分析: 使用动态规划解决 状态表示: 1.以某个位置为结尾 2.以某个位置为起点 这里使用以某个位置为结尾&#xff0c;结合题目要求&#…

MySQL8.0主从同步报ERROR 13121错误解决方法

由于平台虚拟机宿主机迁移&#xff0c;导致一套MySQL主从库从节点故障&#xff0c;从节点服务终止&#xff0c;在服务启动后&#xff0c;恢复从节点同步服务&#xff0c;发现了如下报错&#xff1a; mysql> show slave status\G; *************************** 1. row *****…

GDAL+C#实现矢量多边形转栅格

1. 开发环境测试 参考C#配置GDAL环境&#xff0c;确保GDAL能使用&#xff0c;步骤简述如下&#xff1a; 创建.NET Framework 4.7.2的控制台应用 注意&#xff1a; 项目路径中不要有中文&#xff0c;否则可能报错&#xff1a;can not find proj.db 在NuGet中安装GDAL 3.9.1和G…

无人机之自主飞行关键技术篇

无人机自主飞行指的是无人机利用先进的算法和传感器&#xff0c;实现自我导航、路径规划、环境感知和自动避障等能力。这种飞行模式大大提升了无人机的智能化水平和操作的自动化程度。 一、传感器技术 传感器是无人机实现自主飞行和数据采集的关键组件&#xff0c;主要包括&a…

C语言复习第3章 函数

目录 一、函数介绍1.1 函数是什么1.2 C语言中函数的分类1.3 函数原型1.4 高内聚 低耦合1.5 C语言main函数的位置 二、函数的参数2.1 实参和形参2.2 函数的参数(实参)可以是表达式2.3 传值与传址(swap函数)2.4 明确形参是实参的临时拷贝2.5 void(如果不写函数返回值 默认是int)2…

python 爬虫 入门 三、登录以及代理。

目录 一、登录 &#xff08;一&#xff09;、登录4399 1.直接使用Cookie 2.使用账号密码进行登录 可选观看内容&#xff0c;使用python对密码进行加密&#xff08;无结果代码&#xff0c;只有过程分析&#xff09; 二、代理 免费代理 后续&#xff1a;协程&#xff0c;…

企业级调度器 LVS

集群和分布式基础知识 系统性能的扩展方式 当一个系统&#xff0c;或一个服务的请求量达到一定的数量级的时候&#xff0c;运行该服务的服务器的性能和资源上限&#xff0c; 很容易成为其性能瓶颈。除了性能问题之外&#xff0c;如果只部署在单台服务器上&#xff0c;在此服务…

gitee建立/取消关联仓库

目录 一、常用指令总结 二、建立关联具体操作 三、取消关联具体操作 一、常用指令总结 首先要选中要关联的文件&#xff0c;右击&#xff0c;选择Git Bash Here。 git remote -v //查看自己的文件有几个关联的仓库git init //初始化文件夹为git可远程建立链接的文件夹…

uniapp uni.uploadFile errMsg: “uploadFile:fail

uniapp 上传后一直显示加载中 1.检查前后端上传有无问题 2.检查失败信息 await uni.uploadFile({url,filePath,name,formData,header,timeout: 30000000, // 自定义上传超时时间fail: async function(err) {$util.hideAll()// 失败// err 返回 {errMsg: "uploadFile:fai…

SpringCloud学习:Openfeign组件实现服务调用和负载均衡

OpenFeign&#xff1a;服务调用与负载均衡&#xff08;服务端接口&#xff09; 是什么&#xff1a;通过OpenFeign可以实现服务调用和负载均衡 OpenFeign是一个声明性web服务客户端&#xff0c; 怎么用&#xff1a;服务提供者提取公共接口用FrignClient标注&#xff0c;服务调…

kernel32.dll下载地址:如何安全地恢复系统文件

关于从网络上寻找kernel32.dll的下载地址&#xff0c;这通常不是一个安全的做法&#xff0c;而且可能涉及到多种风险。kernel32.dll是Windows操作系统的核心组件之一&#xff0c;负责内存管理、进程和线程管理以及其他关键系统功能。因为kernel32.dll是系统的基础文件&#xff…

信息安全工程师(57)网络安全漏洞扫描技术与应用

一、网络安全漏洞扫描技术概述 网络安全漏洞扫描技术是一种可以自动检测计算机系统和网络设备中存在的漏洞和弱点的技术。它通过使用特定的方法和工具&#xff0c;模拟攻击者的攻击方式&#xff0c;从而检测存在的漏洞和弱点。这种技术可以帮助组织及时发现并修补漏洞&#xff…

【数据结构与算法】链表(上)

记录自己所学&#xff0c;无详细讲解 无头单链表实现 1.项目目录文件 2.头文件 Slist.h #include <stdio.h> #include <assert.h> #include <stdlib.h> struct Slist {int data;struct Slist* next; }; typedef struct Slist Slist; //初始化 void SlistI…