WebSocket Day 01:入门案例

news2024/11/7 18:08:24

前言

欢迎来到WebSocket入门案例系列的第一天!在今天的博客中,我们将一起探索WebSocket的基础知识和使用方法。本系列将以一个简单的入门案例为基础,带领您逐步了解WebSocket的原理和用法。

一、什么是 WebSocket ?

WebSocket是一种在Web应用程序中实现实时双向通信的协议。它提供了一种持久连接,允许服务器主动向客户端推送数据,而不需要客户端发起请求。

传统的Web应用程序使用HTTP协议进行通信,这种通信方式是基于请求-响应模式的。客户端发送请求给服务器,服务器响应请求并返回相应的数据。但是这种方式存在一些限制,特别是对于实时性要求较高的应用场景,如聊天室、股票行情等。

WebSocket通过引入新的协议,提供了一种更高效、更低延迟的通信方式。相对于HTTP,WebSocket具有以下特点:

  1. 双向通信:WebSocket允许客户端和服务器之间进行双向通信,服务器可以主动向客户端推送数据,而不仅仅是响应客户端的请求。这使得实时性要求较高的应用程序能够更加高效地进行数据传输。

  2. 持久连接:WebSocket在建立连接后,保持持久的连接状态,而不需要每次通信都重新建立连接。这消除了每次请求建立连接和断开连接的开销,减少了网络流量和延迟。

  3. 低开销:相对于传统的HTTP请求-响应模式,WebSocket协议的数据帧头部较小,有效减少了通信过程中的数据开销。此外,由于使用持久连接,减少了连接建立和断开的开销。

  4. 兼容性:WebSocket协议基于TCP协议,通过HTTP/HTTPS进行握手协商,并在握手成功后升级为WebSocket连接。由于握手过程使用标准的HTTP/HTTPS协议,因此WebSocket可以通过大多数防火墙和代理服务器。

WebSocket在现代Web应用程序中被广泛应用,特别是对于需要实时通信和即时更新的应用场景。它为开发者提供了一种更加便捷、高效的方式来构建实时性强的Web应用程序。无论是聊天应用、实时博客评论、多人协作工具还是股票行情等都可以借助WebSocket实现更好的用户体验和数据交互。

二、为什么要使用 WebSocket 

WebSocket作为一种实现实时双向通信的协议,具有以下优点,这也是为什么要使用WebSocket的原因:

  1. 实时双向通信:传统的Web应用程序都是基于HTTP协议进行请求和响应的,无法实现实时双向通信。而WebSocket协议可以在客户端和服务器之间建立持久连接,实现实时双向通信,从而满足实时性比较强的应用场景。

  2. 低延迟:与HTTP协议相比,WebSocket协议的数据帧头部较小,有效减少了通信过程中的数据开销。由于使用持久连接,减少了连接建立和断开的开销,降低了延迟,提高了应用程序的响应速度和用户体验。

  3. 更好的扩展性:WebSocket协议采用标准的TCP协议,通过HTTP/HTTPS进行握手协商,并在握手成功后升级为WebSocket连接。由于握手过程使用标准的HTTP/HTTPS协议,WebSocket可以通过大多数防火墙和代理服务器。这使得应用程序更容易部署和扩展。

  4. 更低的网络流量:由于WebSocket协议使用二进制帧传输数据,相对于HTTP协议使用文本格式传输数据,WebSocket可以减少网络流量,提高应用程序的效率和性能。

总之,WebSocket协议提供了一种更高效、更低延迟、更实时、更灵活的通信方式,使得实时性较强的应用场景(如聊天室、在线游戏、多人协作工具等)更容易实现。如果您需要构建这些类型的应用程序,那么WebSocket将是您不错的选择。

三、实现一个单的 Hello WebSocket

1、新建项目,结构如下

2、导入依赖
<!-- websocket 依赖 -->
        <dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
            <version>1.1</version>
            <scope>provided</scope>
        </dependency>

        <!-- 打印日志 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.3.8</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

 下面是每个依赖项的作用:

  1. javax.websocket-api:这是一个Java API,用于支持WebSocket通信协议。WebSocket允许在客户端和服务器之间进行双向通信,实时地发送消息和数据。这个依赖项提供了WebSocket的核心接口和类。

  2. logback-classic:这是一个Java日志框架,用于在应用程序中记录和输出日志信息。logback-classic是logback框架的核心组件,它提供了强大的日志记录功能和灵活的配置选项。

  3. lombok:这是一个Java库,通过注解自动化简化了Java代码的编写。使用lombok可以减少样板代码,例如自动生成getter、setter、构造函数等。它提供了一些注解来简化代码的编写和维护,提高开发效率。

3、 编写 controller 类
@ServerEndpoint("/connect")
@Slf4j
public class WebSocketServer {


    /**
     * 打开连接的方法,当有一个客户端连接服务端的时候
     * 这个方法就会调用一次
     *
     * @OnOpen注解:表示连接
     *
      */
    @OnOpen
    public void onOpen(){
        log.info("客户端已连接" );
    }

    /**
     * 客户端发送消息的方法
     */
    @OnMessage
    public void onMessage(String message,Session session) throws IOException {
        log.info("消息:" + message);
        // 向当前客户端发送一个消息
         session.getBasicRemote().sendText(message);

    }

    /**
     * 当客户端断开连接后调用此方法
     */
    @OnClose
    public void onClose(Session session){
        log.info("客户端已断开连接");
    }

}

 在给出每个注解的用途之前,需要说明这段代码是Java中使用WebSocket的示例代码,使用了javax.websocket包提供的注解。

  1. @ServerEndpoint("/connect"):

    • 作用:将Java类标记为WebSocket服务器端点。
    • 解释:这个注解用于标识一个类是WebSocket的服务器端点,指定了客户端连接的URL路径。在上述示例中,客户端需要通过连接至"/connect"路径来与该服务器端点进行通信。
  2. @OnOpen:

    • 作用:定义打开连接时调用的方法。
    • 解释:当有客户端连接到WebSocket服务器端点时,被该注解标记的方法将会被调用。在示例中,onOpen()方法在客户端连接成功后执行,并打印出连接成功的消息。
  3. @OnMessage:

    • 作用:定义接收客户端消息时调用的方法。
    • 解释:当WebSocket服务器端点接收到客户端发送的消息时,被该注解标记的方法将会被调用。在示例中,onMessage()方法接收两个参数:String类型的message表示接收到的消息内容,Session类型的session表示与该客户端的会话。该方法在接收到消息后,会将消息原样发送回客户端。
  4. @OnClose:

    • 作用:定义客户端断开连接时调用的方法。
    • 解释:当与WebSocket服务器端点建立连接的客户端断开连接时,被该注解标记的方法将会被调用。在示例中,onClose()方法接收一个参数:Session类型的session,表示与断开连接的客户端的会话。该方法在客户端断开连接后执行,并打印出断开连接的消息。

这些注解是Java WebSocket API提供的一部分,通过使用这些注解,可以方便地定义WebSocket服务器端点的行为,包括连接建立、消息接收和断开连接等操作。

这个类是一个示例的WebSocket服务器端点类,用于处理客户端与服务器之间的WebSocket通信。主要功能如下:

  1. 打开连接:当有客户端连接到WebSocket服务器端点时,会调用onOpen()方法,并打印出连接成功的消息。

  2. 接收消息:当WebSocket服务器端点接收到客户端发送的消息时,会调用onMessage()方法,并打印出接收到的消息。同时,该方法会将接收到的消息原样发送回客户端。

  3. 断开连接:当与WebSocket服务器端点建立连接的客户端断开连接时,会调用onClose()方法,并打印出断开连接的消息。

这个类是使用了Java WebSocket API提供的注解来定义WebSocket服务器端点的行为。通过这些注解,可以方便地处理WebSocket的连接、消息接收和断开连接等操作。

4、编写一个 html 页面接收数据
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>websocket 客户端</h1>

<script>
    // 构建 websocket 的实例,连接后台 server 的请求地址
    // websocket 在第一次请求时使用 http 协议在连接服务器,告诉服务器
    // 接下来要使用 websocket 进行通信,此时服务器将进行协议升级,会在
    // http 的请求头中带有 upgrade:websocket 的头信息
    var webSocket = new WebSocket("ws://localhost:8080/connect");

    // onopen 方法
    webSocket.onopen = function () {
        // 发送一个消息
        webSocket.send("hello websocket");
    }

    // onmessage 方法,接受服务端发送的信息
    webSocket.onmessage = function (event) {
        alert("服务器的消息:" + event.data);
    }

    // onclose 方法
    webSocket.onclose = function () {
        alert("已断开连接");
    }


</script>

</body>
</html>

这段代码是一个简单的WebSocket客户端示例,用于与WebSocket服务器进行通信。它的作用是:

  1. 创建WebSocket实例:通过var webSocket = new WebSocket("ws://localhost:8080/connect");语句创建了一个WebSocket实例,并指定了连接的服务器地址。

  2. 连接建立:当WebSocket连接成功建立时,会自动触发onopen事件处理函数,其中的webSocket.onopen定义了连接建立时的操作。在示例中,onopen函数中发送了一条消息webSocket.send("hello websocket");到服务器。

  3. 接收消息:当WebSocket客户端接收到来自服务器端的消息时,会触发onmessage事件处理函数,其中的webSocket.onmessage定义了接收消息时的操作。在示例中,当接收到消息时,会通过弹窗的方式显示服务器端发送的消息。

  4. 断开连接:当WebSocket连接关闭时,会触发onclose事件处理函数,其中的webSocket.onclose定义了连接关闭时的操作。在示例中,当连接关闭时,会通过弹窗的方式提示连接已断开。

这段代码展示了一个最基本的WebSocket客户端的工作流程,包括连接建立、消息发送和接收、连接断开等操作。您可以将其用于与WebSocket服务器进行实时通信,实现双向数据传输的功能。根据具体需求,您可以根据这个示例进行定制和扩展,添加更多的业务逻辑和交互功能。

 5、运行效果

我们在前端接收到了后台发送来的信息,在后台我们也接受到前端发送的信息,这就是一个简单的多对多的群聊了。

四、实现多人群聊

1、编写 controller 

@ServerEndpoint("/connect")
@Slf4j
public class WebSocketServer {

    /**
     * 用户列表
     */
    private static final List<Session> users = new ArrayList<>();

    /**
     * 打开连接的方法,当有一个客户端连接服务端的时候
     * 这个方法就会调用一次
     *
     * @OnOpen注解:表示连接
     *
     * @param session 表示一个 websocket 客户端的连接会话
     *                每一个客户端连接就会创建一个 session 会话
      */
    @OnOpen
    public void onOpen(Session session){
        log.info("客户端已连接" );
        // 将 session 添加到用户列表中
        users.add(session);
    }

    /**
     * 客户端发送消息的方法
     */
    @OnMessage
    public void onMessage(String message,Session session) throws IOException {
        log.info("消息:" + message);

        // 获取当前时间
        String formattedTime = new SimpleDateFormat("HH:mm:ss").format(new Date());

        // 向当前客户端发送一个消息,包含发送时间
        String messageWithTime = "[" + formattedTime + "] " + message;

        for (Session user : users) {
            // 群发信息
            user.getBasicRemote().sendText(messageWithTime);
        }

    }

    /**
     * 当客户端断开连接后调用此方法
     */
    @OnClose
    public void onClose(Session session){
        log.info("客户端已断开连接");
        // 用户离线
        users.remove(session);
    }

}

 解释一下每一行代码的意思:

1)、定义了一个静态的会话列表users,用于存储所有连接到WebSocket服务端的客户端Session对象。
private static final List<Session> users = new ArrayList<>();
2)、使用@OnOpen注解声明一个方法,该方法用于处理WebSocket客户端连接到服务器后的操作。当有一个新客户端连接到服务器时,该方法会被自动调用。在该方法中,首先记录下客户端连接成功的日志信息,然后将客户端的Session对象保存到静态会话列表中。
@OnOpen
public void onOpen(Session session){
    log.info("客户端已连接" );
    // 将 session 添加到用户列表中
    users.add(session);
}
3)、使用@OnMessage注解声明一个方法,该方法用于处理WebSocket客户端发送消息的操作。当客户端向服务器发送消息时,该方法会被自动调用,并传入消息内容和对应的Session对象。在该方法中,首先记录下客户端发送的消息内容,然后获取当前时间,并将时间和消息内容拼接为一个新的字符串。最后,遍历所有连接到服务器的客户端(即静态会话列表users),并将消息广播给所有客户端。
@OnMessage
public void onMessage(String message,Session session) throws IOException {
    log.info("消息:" + message);
    // 向当前客户端发送一个消息
//         session.getBasicRemote().sendText(message);

    // 获取当前时间
    String formattedTime = new SimpleDateFormat("HH:mm:ss").format(new Date());

    // 向当前客户端发送一个消息,包含发送时间
    String messageWithTime = "[" + formattedTime + "] " + message;

    for (Session user : users) {
        // 群发信息
        user.getBasicRemote().sendText(messageWithTime);
    }

}
4)使用@OnClose注解声明一个方法,该方法用于处理WebSocket客户端断开连接的操作。当有一个客户端与服务器断开连接时,该方法会被自动调用,并传入断开连接的Session对象。在该方法中,首先记录下客户端断开连接的日志信息,然后将与断开连接的客户端对应的Session对象从静态会话列表中移除。
@OnClose
public void onClose(Session session){
    log.info("客户端已断开连接");
    // 用户离线
    users.remove(session);
}
2、编写一个页面发送信息
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/JQuery文件.txt.js"></script>
</head>
<body>
<h1>聊天室</h1>
<div id="msg"></div>
<input id="message" type="text"/>
<input type="button" value="发送"/>
<script>
    //创建websocket对象
    let ws = new WebSocket("ws://localhost:8080/connect");
    //接收服务端的消息
    ws.onmessage = function(event){
        let message = event.data;
        //将消息放入聊天框
        $('#msg').append(message + '<br>');
    }

    $(function(){
        $(':button').on('click',function(){
            //获取文本消息
            let msg = $('#message').val();
            //发送消息
            ws.send(msg);
            //清空发送框
            $('#message').val('');
        });
    })
</script>

</body>
</html>

这段代码是一个简单的HTML页面,它实现了一个基本的聊天室功能。具体功能如下:

  1. 在页面上显示一个标题为"聊天室"的大标题。
  2. 有一个用于显示聊天消息的<div>元素,它的id属性为"msg"。
  3. 有一个<input>元素,用于输入要发送的消息,它的id属性为"message"。
  4. 有一个<input>按钮,用于发送消息,按钮上显示"发送"。
  5. 使用JavaScript和jQuery库来处理页面上的交互逻辑。

在脚本部分,代码实现了以下功能:

  1. 创建了一个WebSocket对象,通过指定WebSocket服务器的URL来建立与服务器的连接。在这个例子中,服务器的URL是"ws://localhost:8080/connect"。
  2. 设置了一个事件处理程序(ws.onmessage),当接收到来自服务器的消息时,会触发该事件,并将消息显示在聊天框中。
  3. 使用jQuery库,在页面加载完成后,绑定了按钮的点击事件处理程序。当按钮被点击时,获取输入框中的文本消息,并通过WebSocket发送给服务器。然后清空输入框,以便输入下一条消息。

通过将这段代码部署到一个能够处理WebSocket协议的服务器上,你可以创建一个简单的聊天室页面,用户可以在其中实时地发送和接收消息。当用户在输入框中输入消息并点击发送按钮时,消息将通过WebSocket发送给服务器,在服务器上处理后,会将消息广播给所有连接的客户端,以便显示在聊天框中。这样就实现了简单的聊天室功能。 

3、运行效果

 五、使用局域网 IP 地址实现多人群聊

1、使用命令查看 IP 地址

win + r 打开命令提示符,输入 cmd 回车进入。在控制面板输出 : ipconfig 回车即可查询到自己本机的 IP.

2、在 client.html 页面更改 url

只需要把 localhost 换成自己本机的 IP 地址即可。

3、运行效果

改完 url 地址后,我们在浏览器就不能使用 localhost:8080 去访问了。

使用 IP 地址后的访问路径为:http://本机IP地址:8080/chat.html 

4、什么是局域网?

局域网通常用于连接位于同一地理位置的多台计算机和设备。

注意:我们使用的是局域网的 IP 地址去和同学聊天的,如果你们使用的是同一个网络就能够正常的访问这个路径去聊天,如果不在同一网络就不能。比如:教室、学校、家里的WIFI,都可以。

 六、总结

        本次案例,我们简单的讲解了实现聊天的功能。但是,这只是一个开头,我们要实现真正的聊天还有很多其他的功能要做,比如:登录,登录成功后获取用户的名称。我们这次聊天是不知道用户名的,只有时间和内容,那么在下一章节我将要讲解如何实现登录后实现聊天。

        

七、gitee 案例

地址:ch01 · qiuqiu/WebSocket-study - 码云 - 开源中国 (gitee.com)

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

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

相关文章

【数据结构】希尔排序

文章目录 前言一、希尔排序的演示图例二、希尔排序&#xff1a;插入排序的优化版本☆三、核心算法思路四、算法思路步骤&#xff08;一&#xff09;预排序 gap>1&#xff08;二&#xff09;gap1 插入排序 完成排序收尾 五、码源详解&#xff08;1&#xff09;ShellSort1 ——…

OceanBase:01-单机部署(开发环境)

目录 一、体系架构 二、配置要求 三、解压安装包 四、执行安装 五、配置环境变量 六、快速部署 七、访问数据库 OceanBase 数据库&#xff08;OceanBase Database&#xff09;是一款蚂蚁集团完全自研的企业级原生分布式数据库&#xff0c;在普通硬件上实现金融级高可用&…

msvcp140.dll重新安装的解决方法,快速修复dll丢失问题

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp140.dll丢失”。这个错误通常会导致某些应用程序无法正常运行&#xff0c;给用户带来很大的困扰。那么&#xff0c;如何解决msvcp140.dll丢失的问题呢&#xff1f;本文将详细介绍解决…

基于transformer的解码decode目标检测框架(修改DETR源码)

提示:transformer结构的目标检测解码器,包含loss计算,附有源码 文章目录 前言一、main函数代码解读1、整体结构认识2、main函数代码解读3、源码链接二、decode模块代码解读1、decoded的TransformerDec模块代码解读2、decoded的TransformerDecoder模块代码解读3、decoded的De…

软件测试面试最经典的5个问题

软件测试面试灵魂五问&#xff01; 请做一下自我介绍&#xff1f;你为什么从上家公司离职&#xff1f;为什么转行做测试? 你对测试行业的认识&#xff1f;你的期望薪资是多少&#xff1f;最后&#xff0c;你要问我什么&#xff1f; 一、请做一下自我介绍 简历上有的可以一两…

VLAN与配置

VLAN与配置 什么是VLAN 以最简单的形式为例。如下图&#xff0c;此时有4台主机处于同一局域网中&#xff0c;很明显这4台主机是能够直接通讯。但此时我需要让处于同一局域网中的PC3和PC4能通讯&#xff0c;PC5和PC6能通讯&#xff0c;并且PC3和PC4不能与PC5和PC6通讯。 为了实…

【工具】【IDE】Qt Creator社区版

Qt Creator社区版下载地址&#xff1a;https://download.qt.io/archive/qt/ 参考&#xff1a;https://cloud.tencent.com/developer/article/2084698?areaSource102001.8&traceIduMchNghqp8gWPdFHvSOGg MAC安装并配置Qt&#xff08;超级简单版&#xff09; 1.安装brew&…

单链表的详解实现

单链表 结构 单链表结构中有两个数据&#xff0c;一个是存储数据的&#xff0c;还有一个指针指向下一个节点。 该图就是一个简单单链表的结构图。 接口实现 SLNode* CreateNode(SLNDataType x);//申请节点 void SLTprint(SLNode* head);//打印链表 void SLTPushBack(SLNode*…

【Echarts】玫瑰饼图数据交互

在学习echarts玫瑰饼图的过程中&#xff0c;了解到三种数据交互的方法&#xff0c;如果对您也有帮助&#xff0c;不胜欣喜。 一、官网教程 https://echarts.apache.org/examples/zh/editor.html?cpie-roseType-simple &#xff08;该教程数据在代码中&#xff09; import *…

springboot-2.7.3+ES-7.10.0

跟着官网走&#xff0c;能干99。一年几次变&#xff0c;次次不一样。刚部署好ES-6.8&#xff0c;又买阿里云Es-7.10.0根本忙不完。 做为JDK1.8最后一个版本。今天就拿新技术部署一套。致辞&#xff1a;大家以后就用这套好了。别轻易触发springboot3.0了 学习无止境&#xff1…

【使用Python编写游戏辅助工具】第三篇:鼠标连击器的实现

前言 这里是【使用Python编写游戏辅助工具】的第三篇&#xff1a;鼠标连击器的实现。本文主要介绍使用Python来实现鼠标连击功能。 鼠标连击是指在很短的时间内多次点击鼠标按钮&#xff0c;通常是鼠标左键。当触发鼠标连击时&#xff0c;鼠标按钮会迅速按下和释放多次&#xf…

言情小说怎么推广?如何推广网络小说?

网络小说是一种文学形式&#xff0c;它的受众群体相当广泛&#xff0c;其实也面临着很强的竞争&#xff0c;因此&#xff0c;网络推广是小说宣传的一项重要工作&#xff0c;这里小马识途营销顾问就分享一下小说推广的渠道和方法。 1、软文推广 在推广小说的过程中&#xff0c;…

面试10000次依然会问的【synchronized】,你还不会?

引言 synchronized 关键字是实现线程同步的核心工具&#xff0c;它能够确保在任一时刻&#xff0c;只有一个线程能够访问被同步的方法或代码块。 这不仅保证了操作的原子性&#xff0c;即这些操作要么完全执行&#xff0c;要么完全不执行&#xff1b;同时也确保了操作的可见性…

高效操作,轻松打造企业百度百科,展现实力形象

百度百科已经成为企业提升形象的重要渠道&#xff0c;拥有自己的百科词条意味着企业在互联网上拥有更高的知名度和可信度。接下来&#xff0c;将为大家介绍企业百度百科的创建过程和一些技巧&#xff0c;帮助企业更好地在百度百科上展现自身实力。 首先&#xff0c;创建企业百度…

基于Tensorflow卷积神经网络玉米病害识别系统(UI界面)

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 Tensorflow是一个流行的机器学习框架&#xff0c;可用于训练和部署各种人工智能模型。玉米病害识别系统基于Tensorf…

明明用的不是自己机器视觉软件,甚至是盗版,机器视觉公司为什么还要申请那么多专利?

我首先看下专利是什么&#xff1f; 专利分为发明、实用新型、外观设计三种类型。 发明是指对产品、方法或者其改进所提出的新的技术方案。 实用新型是指对产品的形状构造或者其结合所提出的适于实用的新的技术方案。一般对日用品、机械、电器等产品的简单改进比较适用于申请…

Mysql数据目录结构以及文件类型解析

目录 1. 数据目录 2. Data目录 3. 数据库目录 1&#xff09;db.opt 2&#xff09;.frm 3&#xff09;.MYD和.MYI 4&#xff09;.ibd 5&#xff09;.ibd和.ibdata 在 MySQL 中&#xff0c;物理文件存放在数据目录中。数据目录与安装目录不同&#xff0c;安装目录用来存储…

NLP之Bert介绍和简单示例

文章目录 1. Bert 介绍2. 代码示例 1. Bert 介绍 2. 代码示例

Express框架开发接口之轮播图API

1.获取所有轮播图、 // 处理轮播图 const handleDB require(../handleDB/index) // 获取所有轮播图 exports.allCarousel (req, res) > {(async function () {let results await handleDB(res, "book_carousel", "find", "查询数据出错&#xf…

Python 生成Android不同尺寸的图标

源代码 # -*- coding: utf-8 -*- import sys import os import shutil from PIL import Imagedef generateAndroidIcons():imageSource icon.pngicon Image.open(imageSource)sizes [(android/drawable,512),(android/drawable-hdpi,72),(android/drawable-ldpi,36),(andro…