smart Java——Netty实战(下):开发一个仿WeChat聊天工具SmartChat

news2024/10/7 9:17:19

文章目录

  • 〇、代码逻辑
  • 一、搭建Server
    • 1.引入依赖
    • 2.搭建一个简单的Server
  • 二、搭建WebSocket建立连接
    • 1.修改Server,增加一些支持
    • 2.自定义一个WebSocketHandler
  • 三、功能实现——用户注册上线
    • 1.先定义一个工具类Result,用于封装服务端返回消息
    • 2.封装客户端指令
    • 3.完善WebSocketHandler
    • 4.给Server添加一个存放用户映射关系的Map
    • 5.定义ConnectionHandler,用于处理用户上线功能的逻辑
    • 6.最后,运行项目来进行测试
  • 四、功能实现——私聊
    • 1.扩展Command类,增加封装后的消息协议
    • 2.修改WebSocketHandler,增加对聊天功能的处理ChatHandler
    • 3.定义ChatHandler用于处理聊天任务
    • 4.运行项目进行测试
  • 五、功能实现——群聊
    • 1.给Server类添加一个channel组,实现系统默认群聊组
    • 2.给CommandType增加一个加入群聊指令的枚举类型
    • 3.在WebSocketHandler中增加加入群组功能的处理
    • 4.定义JoinHandler,用于处理加入群聊的业务逻辑
    • 5.在ChatHandler中增加发送群聊消息的代码
    • 6.运行项目进行测试

项目源码: github
websocket在线测试工具(在没有前端的情况下也可以与Server连接并进行通信): http://websocket-test.com/

在前两篇博客中我介绍了Java IO通信模型和Netty核心概念,在这篇博客中我会展示如何使用Netty开发一个仿WeChat通讯工具——SmartChat。

我们都知道,Netty是一个异步基于事件驱动的高性能网络通信框架,下面我们使用它来一步步搭建SmartChat。

〇、代码逻辑

在这里插入图片描述

一、搭建Server

首先,使用熟悉的IDE工具创建一个Java项目,我命名为了SmartChat。

1.引入依赖

<!--    netty    -->
<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.85.Final</version>
</dependency>
<!--    lombok工具    -->
<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
</dependency>
<!--    json    -->
<dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.18</version>
</dependency>

2.搭建一个简单的Server

package tracy;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class Server {
    public static void start(){
        //主线程池
        EventLoopGroup bossPool=new NioEventLoopGroup();
        //副线程池
        EventLoopGroup workPool=new NioEventLoopGroup();
        //用于监听端口
        ServerBootstrap bootstrap=new ServerBootstrap();
        bootstrap.group(bossPool,workPool)//放入两个线程池
                .channel(NioServerSocketChannel.class)//指定channel
                .childHandler(new ChannelInitializer<SocketChannel>() {//初始化channel
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {

                    }
                });
        //监听端口
        ChannelFuture future=bootstrap.bind(8080);
    }
}

  • 在项目启动类中调用Server类的start():
package tracy;

/**
 * 启动类
 */
public class Main {
    public static void main(String[] args) {
        System.out.println("SmartChat!!!");
        System.out.println("Hello and welcome!");
        Server.start();
    }
}
  • 运行启动类:
    在这里插入图片描述

二、搭建WebSocket建立连接

websocket用于在服务端和客户端之间建立连接。

1.修改Server,增加一些支持

package tracy;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

public class Server {
    public static void start () throws InterruptedException{
        //主线程池
        EventLoopGroup bossPool=new NioEventLoopGroup();
        //副线程池
        EventLoopGroup workPool=new NioEventLoopGroup();
        //用于监听端口
        ServerBootstrap bootstrap=new ServerBootstrap();
        bootstrap.group(bossPool,workPool)//放入两个线程池
                .channel(NioServerSocketChannel.class)//指定channel
                .childHandler(new ChannelInitializer<SocketChannel>() {//初始化channel
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        //获取pipeline,pipeline的工作是基于责任链模式
                        ChannelPipeline pipeline=socketChannel.pipeline();
                        //添加一些handler
                        //http编码解码器
                        pipeline.addLast(new HttpServerCodec())
                                //对大数据量的支持
                                .addLast(new ChunkedWriteHandler())
                                //对http消息进行聚合
                                .addLast(new HttpObjectAggregator(1024*24))
                                //对websocket进行支持
                                .addLast(new WebSocketServerProtocolHandler("/"))
                                //websocket具体怎么处理,需要自定义
                                .addLast(new WebSocketHandler());
                    }
                });
        //监听端口
        bootstrap.bind(8080);
    }
}

2.自定义一个WebSocketHandler

package tracy;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

/**
 * TextWebSocketFrame表示消息体为文本类型
 */
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        //先做一个最简单的处理,把消息内容直接打印出来
        System.out.println("客户端消息:"+msg.text());
    }
}

  • 启动项目后去websocket在线测试网站http://websocket-test.com/进行测试:

在这里插入图片描述

  • 成功:

在这里插入图片描述

三、功能实现——用户注册上线

功能概述: 用户A要想与用户B进行私聊,首先需要保存用户A和B和Server的连接标识,即需要保存映射关系。

  • 优化目录结构:
    首先,我们先来优化一下目录结构,使其更清晰。
    command用来存放客户端的指令,handler用来存放我们的一些处理逻辑,util存放工具类。
    在这里插入图片描述

1.先定义一个工具类Result,用于封装服务端返回消息

package tracy.util;

import com.alibaba.fastjson2.JSON;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
public class Result {
    private String name;//消息类型
    private LocalDateTime time;
    private String message;//消息内容

    public static TextWebSocketFrame fail(String message){
        return new TextWebSocketFrame(JSON.toJSONString(new Result("系统消息",LocalDateTime.now(),message)));
    }

    public static TextWebSocketFrame success(String message){
        return new TextWebSocketFrame(JSON.toJSONString(new Result("系统消息",LocalDateTime.now(),message)));
    }

	public static TextWebSocketFrame success(String name,String message){
        return new TextWebSocketFrame(JSON.toJSONString(new Result(name,LocalDateTime.now(),message)));
    }
}

2.封装客户端指令

  • 定义一个Command类,用于描述客户端的指令:
package tracy.command;

import lombok.Data;

@Data
public class Command {
    //用户昵称
    private String nickname;
    //指令
    private Integer code;
}
  • 定义一个CommandType枚举类型,用于罗列客户端的指令类型:
package tracy.command;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum CommandType{
    //指令:建立连接
    CONNECTION(1),
    //指令:错误指令
    ERROR(0),
    ;

    private final Integer code;

    public static CommandType match(Integer code){
        //遍历枚举类型的所有值,看输入的code是否能与某一个匹配上
        for (CommandType value:CommandType.values()){
            if(value.getCode().equals(code))return value;
        }
        //匹配不上,说明输入的指令不是合法的枚举类
        return ERROR;
    }
}

3.完善WebSocketHandler

package tracy.handler;

import com.alibaba.fastjson2.JSON;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import tracy.command.*;
import tracy.util.Result;

/**
 * TextWebSocketFrame表示消息体为文本类型
 */
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        //将消息解析成一个Command能兼容的对象
        try{
            //将json形式的文本解析成Command类
            Command command= JSON.parseObject(msg.text(),Command.class);
            //每一种指令都定义一个对应的Handler来进行处理
            switch(CommandType.match(command.getCode())){
                case CONNECTION: ConnectionHandler.execute(command,ctx);break;
                default: ctx.channel().writeAndFlush(Result.fail("不支持的CODE"));
            }
        }catch (Exception e){
            e.printStackTrace();
            ctx.channel().writeAndFlush(Result.fail("不支持的CODE"));
        }
    }
}

4.给Server添加一个存放用户映射关系的Map

//用于保存映射关系
public static final Map<String,Channel> USERS=new ConcurrentHashMap<>();

5.定义ConnectionHandler,用于处理用户上线功能的逻辑

package tracy.handler;

import com.alibaba.fastjson2.JSON;
import io.netty.channel.ChannelHandlerContext;
import tracy.Server;
import tracy.command.Command;
import tracy.util.Result;

public class ConnectionHandler {
    public static void execute(Command command, ChannelHandlerContext ctx){
        //容错处理:避免相同昵称的用户重复上线
        if(Server.USERS.containsKey(command.getNickname())){
            ctx.channel().writeAndFlush(Result.fail("该用户已上线,请更换昵称后重试!"));
            ctx.channel().disconnect();
            return;
        }
        //将用户加入到服务端的映射队列中
        Server.USERS.put(command.getNickname(),ctx.channel());
        //返回一条表示用户上线成功的消息
        ctx.channel().writeAndFlush(Result.success("与服务端连接建立成功!"));
        //再以json字符串的形式返回当前在线的用户列表
        ctx.channel().writeAndFlush(Result.success(JSON.toJSONString(Server.USERS.keySet())));
    }
}

6.最后,运行项目来进行测试

http://websocket-test.com/

wang001上线成功

{
	"nickname": "tracy001",
	"code": 1
}

在这里插入图片描述
新开一个测试窗口,tracy002上线成功

{
	"nickname": "tracy002",
	"code": 1
}

在这里插入图片描述
新开一个测试窗口,tracy001上线失败,原因是昵称重复

{
	"nickname": "tracy001",
	"code": 1
}

在这里插入图片描述
新开一个测试窗口,tracy003上线失败,原因是指令=2不合法

{
	"nickname": "tracy003",
	"code": 2
}

在这里插入图片描述

四、功能实现——私聊

1.扩展Command类,增加封装后的消息协议

  • Command:
package tracy.command;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@AllArgsConstructor
@NoArgsConstructor
public class Command {
    //用户昵称
    private String nickname;
    //指令
    private Integer code;
    //消息
    private Message message;
}
  • CommandType也需要增加相应的枚举值:
//指令:聊天
CHAT(2),
  • Message:
package tracy.command;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message{
    //消息类型
    private Integer type;
    //接收对象
    private String target;
    //内容
    private String content;

    public Message(Integer type,String target){
        this.type=type;
        this.target=target;
    }
    public Message(Integer type){
        this.type=type;
    }
}
  • MessageType:
package tracy.command;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum MessageType {
    //私聊
    PRIVATE(1),
    //群聊
    GROUP(2),
    //错误
    ERROR(0);

    private Integer type;

    public static MessageType match(Integer type){
        //遍历枚举类型的所有值,看输入的type否能与某一个匹配上
        for (MessageType value:MessageType.values()){
            if(value.getType().equals(type))return value;
        }
        //匹配不上,说明输入的type不是合法的枚举类
        return ERROR;
    }
}

2.修改WebSocketHandler,增加对聊天功能的处理ChatHandler

package tracy.handler;

import com.alibaba.fastjson2.JSON;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import tracy.command.*;
import tracy.util.Result;

/**
 * TextWebSocketFrame表示消息体为文本类型
 */
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        try{
            //将json形式的文本解析成Command类
            Command command= JSON.parseObject(msg.text(),Command.class);
            //每一种指令都定义一个对应的Handler来进行处理
            switch(CommandType.match(command.getCode())){
                case CONNECTION: ConnectionHandler.execute(command,ctx);break;
                case CHAT: ChatHandler.execute(command,ctx);break;
                default: ctx.channel().writeAndFlush(Result.fail("不支持的CODE"));
            }
        }catch (Exception e){
            e.printStackTrace();
            ctx.channel().writeAndFlush(Result.fail("发送消息格式错误,请确认后再试"));
        }
    }
}

3.定义ChatHandler用于处理聊天任务

package tracy.handler;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import tracy.Server;
import tracy.command.Command;
import tracy.command.Message;
import tracy.command.MessageType;
import tracy.util.Result;

public class ChatHandler {
    //用户聊天逻辑
    public static void execute(Command command, ChannelHandlerContext ctx){
        try{
            Message message=command.getMessage();
            //按不同聊天类型进行处理
            switch(MessageType.match(message.getType())){
                //私聊
                case PRIVATE: {
                    //信息接收对象为空
                    String target=message.getTarget();
                    Channel channel=Server.USERS.get(target);
                    if(target==null||target.isEmpty()){
                        ctx.channel().writeAndFlush(Result.fail("接收者信息为空,请确认后再试"));
                        return;
                    }
                    //信息接收对象不存在
                    else if(channel==null){
                        ctx.channel().writeAndFlush(Result.fail("接收者"+target+"不存在,请确认后再试"));
                        return;
                    }
                    //信息接收对象下线了
                    else if(!channel.isActive()){
                        ctx.channel().writeAndFlush(Result.fail("接收者"+target+"已下线,请确认后再试"));
                        return;
                    }
                    else{
                        channel.writeAndFlush(Result.success("私聊消息("+command.getNickname()+")",message.getContent()));
                    }
                };break;
                //群聊,先空着,下一章实现
                case GROUP: ;break;
                default: ctx.channel().writeAndFlush(Result.fail("不支持的TYPE"));
            }
        }catch (Exception e){
            e.printStackTrace();
            ctx.channel().writeAndFlush(Result.fail("发送消息格式错误,请确认后再试"));
        }

    }
}

4.运行项目进行测试

http://websocket-test.com/

wang001、wang002、wang003上线成功
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

wang001向wang002发送消息,wang002接收到了

{
	"nickname": "wang001",
	"code": 2,
	"message": {
		"type": 1,
		"target": "wang002",
		"content": "你好,我是1号"
	}
}

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

wang001向并不存在的wang004发送消息,失败

在这里插入图片描述

wang003下线,然后wang001向下线的wang003发送消息,失败

在这里插入图片描述

五、功能实现——群聊

功能概述:系统提供一个群里组,但是用户需要有加入群聊这个操作,才能进行后续的收发群聊消息。

1.给Server类添加一个channel组,实现系统默认群聊组

//添加一个channel组,用于实现群聊一对多通信
public static final ChannelGroup CHANNEL_GROUP=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

2.给CommandType增加一个加入群聊指令的枚举类型

//指令:加入群聊
JOIN(3),

3.在WebSocketHandler中增加加入群组功能的处理

            switch(CommandType.match(command.getCode())){
                case CONNECTION: ConnectionHandler.execute(command,ctx);break;
                case CHAT: ChatHandler.execute(command,ctx);break;
                case JOIN: JoinHandler.execute(ctx);break;
                default: ctx.channel().writeAndFlush(Result.fail("不支持的CODE"));
            }

4.定义JoinHandler,用于处理加入群聊的业务逻辑

package tracy.handler;

import io.netty.channel.ChannelHandlerContext;
import tracy.Server;
import tracy.util.Result;

public class JoinHandler {
    public static void execute(ChannelHandlerContext ctx){
        Server.CHANNEL_GROUP.add(ctx.channel());
        ctx.channel().writeAndFlush(Result.success("加入系统默认群聊成功"));
    }
}

5.在ChatHandler中增加发送群聊消息的代码

            switch(MessageType.match(message.getType())){
                //私聊
                case PRIVATE: {
                	……
                    };break;
                //群聊
                case GROUP: {
                    Server.CHANNEL_GROUP.writeAndFlush(Result.success("群聊消息("+command.getNickname()+")",message.getContent()));
                };break;
                default: ctx.channel().writeAndFlush(Result.fail("不支持的TYPE"));
            }

6.运行项目进行测试

http://websocket-test.com/
自行完成这一工作。

  • 加入群聊json:
{
	"nickname": "wang001",
	"code": 3
}
  • 发送群聊消息json:
{
	"nickname": "wang001",
	"code": 2,
	"message": {
		"type": 2,
		"content": "你好,我是1号"
	}
}

成功!
在这里插入图片描述


到这里,我们就完成了mini版微信聊天工具SmartChat的开发工作了,在此基础上,可以增加前端的开发,以及更多功能的实现,实际上开发步骤都是类似的,只是针对不同的功能具体的业务逻辑不同罢了,感兴趣的同学可以尝试着扩展一下SmartChat的功能。
源码请看文章的最顶部。感谢阅读。

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

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

相关文章

easyX库图像处理相关函数(注释版)

0.图像处理相关函数与类型概览 您好&#xff0c;这里是limou3434&#xff0c;本次我将给您带来的是easyX的图像处理相关接口。 如果您感兴趣也可以看看我的其他内容。 函数或数据类型描述IMAGE保存图像的对象。loadimage读取图片文件。putimage在当前绘图设备上绘制指定图像…

高压线路距离保护程序逻辑原理(三)

阻抗元件的主要程序功能是阻抗计算&#xff0c;即第二章的解微分方程算法。配合低通数字滤波器计算出故障点至保护安装处的感受电抗X和电阻R值&#xff0c;再同整定值比较以确定是否在区内。阻抗元件也可以采用其他算法计算阻抗&#xff0c;但应指出上述哪种计算感受电抗和电阻…

如何去伪存真高效挖掘用户真实需求?2大模型

用户需求描述模糊&#xff0c;不清晰&#xff0c;往往对项目造成不可估量的风险&#xff1a;需求理解错误、新增需求、变更需求等问题&#xff0c;从而影响项目按时交付。 那么如何更准确地描述用户需求&#xff0c;就显得尤为重要。而可行性较强的模型主要有两种&#xff1a;5…

Django纪录操作之增删改查

一、单表 1、 添加记录 准备表 from django.db import modelsclass Book(models.Model):title models.CharField(max_length20)price models.DecimalField(max_digits65,decimal_places5)publish models.CharField(max_length30)pub_date models.DateTimeField(auto_now…

阿里云盘如何实现 大文件 秒上传?

文章目录 Intro极速上传的原因隐私保护 Intro 今天把几个软件上传到阿里云盘进行分享&#xff0c;文件大小将近1GB&#xff0c;按理说上传需要个2~3分钟吧。 之前上传一个压缩包看到上传速度大概是4~5MB/s。 但是我刚到别的软件看了一圈&#xff0c;回来发现文件居然已经上传…

网络攻防基础(复习)

文章目录 第 1 章 软件与系统安全概述第 2 章 扫描与防御技术2.1 扫描技术概述2.2 常见的扫描技术2.3 扫描工具2.4 扫描的防御 第 3 章 网络监听及防御技术3.1 网络监听概述3.2 监听技术3.3 监听的防御 第 4 章 口令破解与防御技术4.1 口令的历史与现状4.2 口令破解方式4.3 典型…

基于遗传算法(GA)的多旅行商问题(MTSP)

matlab2016b可运行&#xff0c;输入城市位置&#xff0c;可以动态显示规划过程 % MTSPF_GA Fixed Multiple Traveling Salesmen Problem (M-TSP) Genetic Algorithm (GA) % Finds a (near) optimal solution to a variation of the M-TSP by setting % up a GA to search …

前后端分离,通用分页js处理模板

截图&#xff1a; 步骤&#xff1a; 第一步&#xff1a;创建一个index.html引入 <script src"./jquery-3.7.0.js"></script><link rel"stylesheet" href"https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstra…

想要wav转换mp3软件?看我给你分享将wav转换成mp3的软件

你是否曾经遇到过这样的情况&#xff1a;下载了一首你喜爱的音乐&#xff0c;却发现它的格式是wma&#xff0c;无法在你的设备或播放器上正常播放&#xff1f;别担心&#xff0c;今天我将向你介绍几款wma转换器&#xff0c;让你能够轻松转换音频格式&#xff0c;不再困惑于wma转…

Samba协议实现视频上传、远程播放

一、效果演示 扫码打开上传页面&#xff0c;上传进度可以全局筛选订单查看&#xff0c;上传过程中查看视频是本地视频&#xff08;速度快&#xff09;&#xff0c;上传完成后再次打开是smb服务器视频&#xff08;打开慢&#xff09; 本文涉及demo参考下载 二、集成smbj 1、集…

写毕业论文之前一定要看的经验总结!!!

本文为笔者关于毕业论文的经验总结 如果本文有错误的地方 或者关于论文有什么更好的经验分享欢迎私信或者评论指出 打开word的格式标记 打开标尺 摘要 标题 目录 分页 页码 图片 表格 参考文献 打开word的格式标记 word格式标记并不是真的存在&#xff0c;只是帮你你修改word时…

嵌入式工作的前景:为什么嵌入式系统需求仍在增长

嵌入式系统的需求在过去几十年中一直稳步增长&#xff0c;并且预计在未来仍然会继续增加。嵌入式系统是指嵌入到其他设备或系统中的计算机系统&#xff0c;它们具有特定的功能和任务。这些系统广泛应用于汽车、消费电子产品、医疗设备、智能家居、工业自动化等领域。 随着科技…

Quiz 10: Tuples | Python for Everybody 配套练习_解题记录

文章目录 课程简介Quiz 10: Tuples 单选题&#xff08;1-11&#xff09;编程题Exercise 10.2 课程简介 Python for Everybody 零基础程序设计&#xff08;Python 入门&#xff09; This course aims to teach everyone the basics of programming computers using Python. 本课…

【软件测试】Java+selenium环境搭建

目录 1.下载Chrome浏览器&#xff0c;查看浏览器的版本 2.根据浏览器版本下载驱动 根据电脑版本下载驱动&#xff1a; 3.去maven仓库寻找selenium驱动 4.在idea中创建一个项目 1.在pom.xml中添加依赖 2.点击右侧刷新按钮 3.在Java下创建一个类Main 4.将以下代码写入 5.…

B061-ES6 NodeJS npm Vue

目录 ECMAScript6什么是ECMAScriptECMAScript历史语法申明变量解构表达式箭头函数模块化 npm引出nodejs安装VUEvue简介配置Terminalvue入门vue属性-elvue属性-datavue属性-methods vue架构认识vue表达式vue-表达式-基础vue-表达式-操作对象&数组 vue-指令v-text & v-ht…

软件工程师,入门下深度学习吧

概述 ChatGPT,英文全称为Chat Generative Pre-trained Transformer,是OpenAI研发的聊天机器人程序。ChatGPT是人工智能技术驱动的自然语言处理工具,它能够通过理解和学习人类的语言来进行对话,还能根据聊天的上下文进行互动,真正像人类一样来聊天交流。除此之外,还能进行…

kettle架构图

2、架构说明 1&#xff09;最底层的是kettle的核心引擎层&#xff0c;相关的jar在lib目录下。 2&#xff09;中间是开发层&#xff0c;在开发阶段我们接触最多的就是通过spoon进行开发&#xff0c;通过Spoon.bat或者spoon.sh即可启动客户端&#xff0c;开发文件调试之前要先保…

使用vtk创建立方体,设置顶点为不同颜色

引言 改示例为官网上的例子。创建了一个顶点是不同颜色的立方体。 示例 开发环境 使用QtCreator4.11.2,Qt5.14.2。使用的vtk9.2的库及其头文件。创建空项目。 示例代码 其pro文件中的内容&#xff1a; QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgets…

【半监督图像分类 ICLR 2023】FreeMatch

【半监督图像分类 ICLR 2023】FreeMatch 论文题目&#xff1a;FREEMATCH: SELF-ADAPTIVE THRESHOLDING FOR SEMI-SUPERVISED LEARNING 中文题目&#xff1a;Freematch&#xff1a;用于半监督学习的自适应阈值 论文链接&#xff1a;https://arxiv.org/abs/2205.07246 论文代码&a…

数字经济下的架构新图景—2023架构·可持续未来峰会(北京主会场)成功举办!

2023 年 6 月 29日&#xff0c;由The Open Group主办的2023架构可持续未来峰会&#xff08;北京主会场&#xff09;在机械工业出版社圆满落幕。 本次大会以“可持续未来”为主题&#xff0c;采取13&#xff0c;即北京主会场上海/成都/深圳三个城市峰会场模式&#xff0c;聚焦架…