豆包编程助手-生产力神器?

本文探讨了豆包编程助手在提高编程生产力方面的应用。使用豆包助手实现MQTT协议的服务端代码。它还提供了代码修复、补全、注释生成等功能,但存在一些限制,如对第三方库的理解不足。豆包助手在简单逻辑代码生成和代码注释方面表现出色,但在复杂场景下需要用户进一步修正。插件的交互设计和语言选择也有待改进。

MQTT概念

主题:MQTT 使用主题来过滤消息,类似于电子邮件中的标签。订阅者可以订阅一个或多个主题,并接收到所有匹配该主题的消息。
消息:MQTT 中的消息是数据传输的基本单元,包含主题、有效载荷(Payload)和一些控制信息。
客户端:任何可以发送或接收 MQTT 消息的设备或应用程序都可以被称为客户端。
服务器:MQTT 服务器,也称为代理,是消息的中转站。它接收来自客户端的消息,过滤这些消息,并将它们传递给订阅了相应主题的客户端。
发布/订阅模型:MQTT 采用发布/订阅模式,允许客户端发布消息到特定的主题,而其他客户端可以订阅这些主题来接收消息。
服务质量:MQTT 定义了三种服务质量级别,分别是:
QoS 0:最多一次,消息可能丢失,但不重复。
QoS 1:至少一次,确保消息至少送达一次,可能会重复。
QoS 2:只有一次,确保消息只送达一次。

MQTT相关文章:

MQTT实践专栏文章

豆包编程助手安装

在线安装官网上有,我就不赘述了,提供离线安装教程。
idea离线安装教程
settings->plugins->installed->设置->Install Plugin from Disk->选择已经下载好的marscode-1.2.0.0.zip 豆包idea插件:
https://pan.quark.cn/s/ecccb0effcb4(30天有效,如果失效可以关注公众号【java程序猿技术】发送安装包获取,路径:软件/ai-code-copilot/字节豆包)

豆包代码生成功能测试

豆包 MarsCode 编程助手可以根据你的自然语言描述生成对应的代码片段。
在输入框中用自然语言描述你的需求。
点击 发送 按钮或敲击回车键。

豆包代码助手实现MQTT服务端

场景一

用户:
使用Netty实现mqtt服务端,实现CONNECT、CONNACK、PUBLISH、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK、PINGREQ、PINGRESP、DISCONNECT
MarsCode:
好的,以下是使用Netty实现MQTT服务端的示例代码,包括了CONNECT、CONNACK、PUBLISH、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK、PINGREQ、PINGRESP、DISCONNECT等报文的处理逻辑。
首先,你需要在项目中引入Netty的依赖:

<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-all</artifactId>
  <version>4.1.76.Final</version>
</dependency>

然后,创建一个Netty服务器来处理MQTT协议的连接和消息:
代码省略。。。。。。
接下来,创建一个MqttBrokerHandler类来处理MQTT协议的各种报文:
代码省略。。。。。。
在这个示例中,我们创建了一个简单的MQTT代理,它能够处理CONNECT、CONNACK、PUBLISH、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK、PINGREQ、PINGRESP、DISCONNECT等报文。你可以根据实际需求扩展这个代理,例如实现消息的持久化、用户认证、访问控制等功能。

场景二

用户:
定义mqtt客户端对象,用来记录用户名、channelId、订阅信息、连接时间、ip、断开连接时间
MarsCode:
在MQTT协议中,客户端对象通常包含一些基本信息,如用户名、客户端ID、订阅信息、连接时间、IP地址以及断开连接时间等。以下是一个简单的Java类定义,用于表示MQTT客户端对象:
代码省略。。。。。。
这个类提供了基本的属性设置和获取方法,你可以根据实际需求扩展它,例如添加更多的属性或方法来处理客户端的行为。在实际应用中,你可能需要一个数据结构来存储这些客户端对象,以便管理连接到MQTT代理的所有客户端。

场景三

用户:
在handleConnect方法中实现客户端账号密码校验,校验通过则记录客户端信息MqttClient,如果验证失败则关闭回话,且1分钟内超过三次验证失败则锁账号5分钟
MarsCode:
好的,根据你的需求,你需要在 handleConnect 方法中实现客户端账号密码校验,并且记录客户端信息。如果验证失败,则关闭会话,并且在 1 分钟内超过三次验证失败则锁定账号 5 分钟。以下是修改后的代码:
代码省略。。。。。。
在这个修改后的代码中,我们添加了一个 userPasswords 静态 Map 来存储预设的用户名和密码,并且添加了一个 failedAttempts 静态 Map 来记录每个用户名的失败尝试次数。在 handleConnect 方法中,我们检查用户名和密码是否匹配,如果匹配则记录客户端信息并发送 CONNACK 消息;如果不匹配,则记录失败次数并在超过三次失败后锁定账号。

场景四

用户:
handleSubscribe 方法把客户端订阅的topic记录到MqttClient
MarsCode:
好的,根据你的需求,你需要在 handleSubscribe 方法中记录客户端订阅的主题。以下是修改后的代码:
代码省略。。。。。。
在这个修改后的代码中,我们添加了一个 clients 静态 Map 来存储客户端信息,并且在 handleSubscribe 方法中记录了客户端订阅的主题。当客户端订阅或取消订阅主题时,我们会更新 MqttClient 对象中的订阅信息。

MarsCode最后输出代码汇总:

public class MqttBroker {

    private static final Logger logger = LoggerFactory.getLogger(MqttBroker.class);

    private final int port;

    public MqttBroker(int port) {
        this.port = port;
    }

    public void start() throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() 
{
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(
                                    new MqttDecoder(),
                                    new MqttEncoder(),
                                    new MqttBrokerHandler()
                            );
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture future = bootstrap.bind(port).sync();
            logger.info("MQTT broker started on port {}", port);

            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        int port = 1883// 默认端口
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        }
        new MqttBroker(port).start();
    }

}

public class MqttClient {
    private String username;
    private String clientId;
    private List<String> subscriptions;
    private Date connectTime;
    private String ipAddress;
    private Date disconnectTime;

    // 构造方法
    public MqttClient(String username, String clientId, List<String> subscriptions, String ipAddress) {
        this.username = username;
        this.clientId = clientId;
        this.subscriptions = subscriptions;
        this.connectTime = new Date();
        this.ipAddress = ipAddress;
    }

    // Getters and Setters for the fields

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public List<String> getSubscriptions() {
        return subscriptions;
    }

    public void setSubscriptions(List<String> subscriptions) {
        this.subscriptions = subscriptions;
    }

    public Date getConnectTime() {
        return connectTime;
    }

    public void setConnectTime(Date connectTime) {
        this.connectTime = connectTime;
    }

    public String getIpAddress() {
        return ipAddress;
    }

    public void setIpAddress(String ipAddress) {
        this.ipAddress = ipAddress;
    }

    public Date getDisconnectTime() {
        return disconnectTime;
    }

    public void setDisconnectTime(Date disconnectTime) {
        this.disconnectTime = disconnectTime;
    }
}


public class MqttBrokerHandler extends SimpleChannelInboundHandler<MqttMessage{

    private static final Logger logger = LoggerFactory.getLogger(MqttBrokerHandler.class);

    private static final Map<String, String> userPasswords = new HashMap<>();
    private static final Map<String, Integer> failedAttempts = new HashMap<>();
    private static final long LOCKOUT_DURATION = 5 * 60 * 1000// 5 minutes in milliseconds

    private static final Map<String, MqttClient> clients = new HashMap<>();

    static {
        // 假设这里有一些预设的用户名和密码
        userPasswords.put("user1""password1");
        userPasswords.put("user2""password2");
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MqttMessage msg) throws Exception {
        if (msg instanceof MqttConnectMessage) {
            handleConnect(ctx, (MqttConnectMessage) msg);
        } else if (msg instanceof MqttPublishMessage) {
            handlePublish(ctx, (MqttPublishMessage) msg);
        } else if (msg instanceof MqttSubscribeMessage) {
            handleSubscribe(ctx, (MqttSubscribeMessage) msg);
        } else if (msg instanceof MqttUnsubscribeMessage) {
            handleUnsubscribe(ctx, (MqttUnsubscribeMessage) msg);
        } else if (msg instanceof MqttPingReqMessage) {
            handlePingReq(ctx, (MqttPingReqMessage) msg);
        } else if (msg instanceof MqttDisconnectMessage) {
            handleDisconnect(ctx, (MqttDisconnectMessage) msg);
        } else {
            logger.warn("Unknown MQTT message type: {}", msg.getClass().getName());
        }
    }

    private void handleConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) {
        String username = msg.payload().userName();
        String password = msg.payload().passwordInBytes()!= nullnew String(msg.payload().passwordInBytes()) : null;

        if (userPasswords.containsKey(username) && userPasswords.get(username).equals(password)) {
            // 验证通过,记录客户端信息
            MqttClient client = new MqttClient(username, msg.variableHeader().clientIdentifier(), null, ctx.channel().remoteAddress().toString());
            clients.put(client.getClientId(), client);

            MqttConnAckMessage connAck = MqttMessageBuilders.connAck()
                    .sessionPresent(false)
                    .returnCode(MqttConnectReturnCode.CONNECTION_ACCEPTED)
                    .build();
            ctx.writeAndFlush(connAck);
        } else {
            // 验证失败,记录失败次数
            failedAttempts.put(username, failedAttempts.getOrDefault(username, 0) + 1);
            if (failedAttempts.get(username) >= 3) {
                // 超过三次失败,锁定账号
                failedAttempts.put(username, 0);
                // 这里可以实现锁定账号的逻辑,例如记录锁定时间
                // lockedAccounts.put(username, new Date().getTime() + LOCKOUT_DURATION);
            }

            MqttConnAckMessage connAck = MqttMessageBuilders.connAck()
                    .sessionPresent(false)
                    .returnCode(MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD)
                    .build();
            ctx.writeAndFlush(connAck);
            ctx.close();
        }
    }

    private void handlePublish(ChannelHandlerContext ctx, MqttPublishMessage msg) {
        // 处理发布消息
        // 这里可以根据需要实现消息的路由和分发逻辑
        logger.info("Received PUBLISH message: {}", msg);

        // 获取发布消息的主题
        String topic = msg.variableHeader().topicName();

        // 假设这里有一个方法可以获取所有已连接的客户端
        List<MqttClient> allClients = getAllClients();

        // 遍历所有客户端,检查是否有匹配的订阅
        for (MqttClient client : allClients) {
            List<String> subscriptions = client.getSubscriptions();
            for (String subscription : subscriptions) {
                // 进行简单的字符串匹配,如果需要更复杂的匹配逻辑,可以使用正则表达式或其他方法
                if (subscription.equals(topic) || subscription.matches(topic.replaceAll("/+""/.*"))) {
                    // 找到了匹配的订阅,将消息发送给该客户端
                    ctx.writeAndFlush(msg);
                    break// 停止遍历当前客户端的订阅,因为已经找到了匹配
                }
            }
        }
    }


    private void handleSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage msg) {
        // 处理订阅请求
        MqttSubAckMessage subAck = MqttMessageBuilders.subAck()
                .messageId(msg.variableHeader().messageId())
                .payload(MqttQoS.AT_LEAST_ONCE, MqttQoS.AT_LEAST_ONCE)
                .build();
        ctx.writeAndFlush(subAck);

        // 记录客户端订阅的主题
        String clientId = ctx.channel().id().asShortText();
        MqttClient client = clients.get(clientId);
        if (client!= null) {
            List<String> topics = msg.payload().topics();
            client.setSubscriptions(topics);
        }
    }

    private void handleUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage msg) {
        // 处理取消订阅请求
        MqttUnsubAckMessage unsubAck = MqttMessageBuilders.unsubAck()
                .messageId(msg.variableHeader().messageId())
                .build();
        ctx.writeAndFlush(unsubAck);

        // 移除客户端订阅的主题
        String clientId = ctx.channel().id().asShortText();
        MqttClient client = clients.get(clientId);
        if (client!= null) {
            List<String> topics = msg.payload().topics();
            client.getSubscriptions().removeAll(topics);
        }
    }

    private void handlePingReq(ChannelHandlerContext ctx, MqttPingReqMessage msg) {
        // 处理心跳请求
        MqttPingRespMessage pingResp = MqttMessageBuilders.pingResp()
                .build();
        ctx.writeAndFlush(pingResp);
    }

    private void handleDisconnect(ChannelHandlerContext ctx, MqttDisconnectMessage msg) {
        // 处理断开连接请求
        String clientId = ctx.channel().id().asShortText();
        MqttClient client = clients.remove(clientId);
        if (client!= null) {
            // 可以在这里记录客户端的断开连接时间等信息
            client.setDisconnectTime(new Date());
        }
        ctx.close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.error("Exception caught in MqttBrokerHandler", cause);
        ctx.close();
    }
}

代码是否可以直接运行?

尴尬的是代码很多地方都出错的,下面逐一列出代码出错的地方,并尝试使用豆包的代码修复功能,看是否可以修复。

豆包代码修复测试

当代码中存在错误时,窗口中的代码文件名称会高亮,名称右侧会提示错误数量。
选中有问题代码片段后,在对话框中输入自然语言描述(例如:修复选中的代码片段)或 /fix 指令,然后点击右侧的 发送 按钮或敲击键盘上的回车键。豆包 MarsCode 编程助手将为选中的代码片段进行智能修复。

MqttBrocker代码出错地方

原因分析:

从idea给出的提示MqttEncoder的无参构造方法是私有的(private)。
进入源码也可以看出MqttEncoder的无参构造方法是私有的,但是提供一个名为 INSTANCE 的静态常量,它是 MqttEncoder 类的一个实例(单例模式)。

使用豆包代码修复功能:

选中报错代码,并输入指令: /fix

但是我们知道的MqttEncoder是Netty源码提供的,是无法改成public的,显然豆包AI并没有给出正确的修复方法。那么我尝试引导豆包看能不能按照我们意图修正代码。
选中报错代码,并输入指令:/fix MqttEncoder的构造方法属于private的,是否可以更换其他方式实例化它呢

二次引导后,豆包输出的修复方案还是和第一次一模一样,显然它没有真正理解MqttEncoder源码,这里我选择自己手动修正。

MqttBrokerHandler报错代码

原因分析:

很严然MqttPingReqMessage、MqttDisconnectMessage这两个类是不存的。很有可能是豆包编程助手生造成来的。

使用豆包代码修复功能:

豆包只是给出修复错误的方向,但没有给出具体实现方式。目前豆包编程助手面对这种复杂场景并没有那么从容。这里还是选择自己修改。

豆包代码补全功能测试

豆包 MarsCode 编程助手可以帮助你自动补全后续代码。
场景一:在对应代码处,敲击回车键换行。豆包 MarsCode 编程助手会阅读并理解当前代码,然后自动补全后续代码。

场景二:在对应位置添加注释,然后敲击回车键换行。豆包 MarsCode 编程助手会阅读并理解代码注释,然后直接生成代码。

场景二通过注释补全代码这个功能对很多场景下具备很高的效率的。

豆包代码编辑功能测试

豆包 MarsCode 编程助手可以编辑指定代码,包括重构、优化、修改部分逻辑等。你可以通过自然语言描述代码编辑需求。

  1. 选中代码片段,然后在对话框中输入自然语言描述,例如 “优化选中的代码片段”。
  2. 点击 发送 按钮或敲击回车键。

优化handlePublish方法

场景一

优化handlePublish方法

场景二

代码中的subscription.matches(topic.replaceAll("/+", "/.*"))正则表达式是无法满足MQTT的topic的。
MQTT的topic的通配符规则:MQTT 主题通配符包含单层通配符 + 及多层通配符 #,主要用于客户端一次订阅多个主题。【注意:通配符只能用于订阅,不能用于发布】

场景三

我们希望通过正则表达实现topic匹配。

豆包代码解释功能测试

选中代码片段后,你可以向豆包 MarsCode 编程助手发送代码解释指令。编程助手将解释选中的代码片段。发送指令方式如下:

  1. 在侧边对话视图的输入框中输入自然语言描述,例如:解释选中的代码片段;
  2. 在侧边对话视图的输入框中输入 /explain 指令;
  3. 选中代码片段后右键,然后在快捷菜单中选择 MarsCode > Explain Code;
  4. 点击代码顶部(Visual Studio Code)或左侧 Logo 中(JetBrains)的 Explain 按钮。

场景一

用于解析业务代码,使我们快速了解业务,并解决问题。
解析一下 handleConnect(ChannelHandlerContext ctx, MqttConnectMessage msg)

场景二

解析复杂的源码,使我们对源码有个粗略了解,根据自己实质情况选择需要深入理解代码阅读。
er:SpringBoot的run方法解读

豆包代码注释生成功能测试

选中代码片段后,向豆包 MarsCode 编程助手发送代码注释生成指令。编程将为选中的代码片段生成代码注释。
发送指令的方式如下:

  1. 在侧边对话视图的输入框中输入自然语言描述,例如:为所选代码片段生成注释;
  2. 在侧边对话视图的输入框中输入 /doc 指令;
  3. 点击代码顶部(Visual Studio Code)或左侧 Logo 中(JetBrains)的 Doc 按钮。

锐评

使用交互锐评

  1. 图标交互还不错,但是只有三个指令选择
  1. 选择代码右键只出现三个指令,其它指令无法选择。
  1. 复制、插入、新建文件这几个图标交互我比较喜欢使用的
  1. 首次安装插件时候,语言的选择是auto时候默认是英文的,但是豆包是需要登录,应该具备根据用户地区选择语言,不然这个auto就失去意义。

代码生成

优点
简单逻辑代码生成是不错的,特别是不需要依赖其它库的情况。
场景类型的代码,根据你的自然语言描述生成对应的代码片段。但是需要你根据实质情况补充完整代码。
编写一些SQL语句,是非常好用的。
缺点
它会根据三方库的上下文,生造出一些不存的类。
无法真正理解三方库类的,给出一些类的不正确用法。

代码注释生成功

这个功能个人觉得是可以无脑用的

代码解释

优点
在前期粗略阅读源码时候,可以提供不错帮助。
英文不好的程序员,是一个两用的功能,翻译及代码逻辑介绍。
缺点
代码解释还是提留在代码本身解释,不具备结合框架整体及上下文去深入了解源码。特别是开源并且应用很广的框架(er:java中spring),豆包应该结合spring框架本身给出答案。

代码补全

场景二:在对应位置添加注释,然后敲击回车键换行。豆包 MarsCode 编程助手会阅读并理解代码注释,然后直接生成代码。这个场景下代码补全是非常好用的。
场景一:在对应代码处,敲击回车键换行。豆包 MarsCode 编程助手会阅读并理解当前代码,然后自动补全后续代码。这个场景我个人觉得比不上idea本身代码提示功能。

代码编辑

个人觉得这个功能还不错,在我们原来的代码上给出另外一种代码编写方式,我们可以选择需要或者不需要。
但是比如想把代码写得更优雅还是需要提升个人修为(设计模式、oop、DDD等知识),目前AI提供的能力有限。

单元测试生成

这次没有使用到,暂时不做评论。

其它

源码代码:
https://gitee.com/xmhzzz/emqx-practice/tree/master/mars-code-mqtt-brocker
豆包idea插件:
https://pan.quark.cn/s/ecccb0effcb4(30天有效,如果失效可以关注公众号【java程序猿技术】发送安装包获取,路径:软件/ai-code-copilot/字节豆包)
豆包官方地址:
https://www.marscode.cn/home
关注公众【java程序猿技术】获取更多有资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后端马农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值