在Node终端实现NewBing对话功能

news2024/11/24 7:17:49

目录

前言

准备工作

工作原理

功能设计

实现过程

基础概念

代理

请求

socket

控制台输入模块

配置文件

bingServer请求

bingSocket消息

子线程入口部分

主线程部分

工具函数

效果展示

写在最后


前言

ChatGPT在当下已然成为炙手可热的话题了,随着GPT-4的推出,网上关于其接口的文章也越来越多。但是今天,我们不聊GPT,说说它的老朋友:newbing

之前我发布了几篇关于对接openAI以及chatGPT的文章:Node搭建GPT接口,Node机器人,语音识别及合成,大家对此类文章的兴趣度还是挺高的,于是我决定深入探索一下NewBing的接口及对话方式,如果有兴趣的话就继续往下看吧

准备工作

  • Node环境搭建
  • VPN
  • 可以使用NewBing的账号
  • Edge或chathub谷歌插件(搭建服务可以不需要,只做原理展示)

工作原理

首先我们看看NewBing的实现原理是什么

挂VPN,打开必应,登录bing账号

如果显示使用Edge打开,我们可以下载一个Edge或者使用chathub插件。

这里我以Edge为例,在Edge中我们可以点击立即聊天开始使用

我们打开F12,进入网络菜单进行hack,输入一句对话并发送,开启与newbing的聊天

可以看到,在发送和接收对话时,浏览器发送了一个请求用于新建对话,并建立了websocket连接,最后将对话结果发送到页面

功能设计

知道了程序运行的原理,实现功能就有思路了,我的计划是在node控制台中实现一个与NewBing对话的功能,思路如下:

简述一下上面的流程,使用者通过命令打开newBing控制台,直接输入要发送的对话,等待一段时间后,收到消息反馈,继续下面的对话

这种方式不仅仅可以在控制台中使用,也可以尝试写成服务或websocket的形式,提供接口或消息给客户端调用,这里我就抛砖引玉,将后续的功能留给各位大佬实现

实现过程

基础概念

代理

使用proxy-agent模块可以让请求和socket代理到VPN所在的端口通过代理访问Bing获取消息

import ProxyAgent from "proxy-agent"
const agent = ProxyAgent('http://127.0.0.1:10240')// 访问vpn代理地址

通过agent参数使用代理功能

请求

请求函数使用的是我之前写的一个工具包,配合配套的catchAwait函数食用更佳

import { Request, catchAwait } from "utils-lib-js"
const bingRequest = new Request('https://www.bing.com')// 初始化请求地址
bingRequest.use("error", console.error)// 拦截抛错
const [err, res] = await catchAwait(this.bingRequest.GET("/turing/conversation/create"))// 发起请求

socket

WebSocket的使用可以参照之前的文章

控制台输入模块

使用readline模块可以接收控制台的输入内容

import readline from "readline";
readline.createInterface({
    input: process.stdin,
    output: process.stdout,
}).question('请输入:', ()=>{
    // 输入完成,敲击了回车
})

配置文件

需要注意的是:bing的cookie可以通过在任意浏览器打开NewBing的网站按下F12获取(前提是登录了账号),直接输入document.cookie获取

export const config = {
  cookie: "必应的cookie",
  bingUrl: "https://www.bing.com",
  proxyUrl: "http://127.0.0.1:10240",
  bingSocketUrl: "wss://sydney.bing.com",
};
export const conversationTemplate = {
  arguments: [
    {
      source: "cib",
      optionsSets: [
        "deepleo",
        "nlu_direct_response_filter",
        "disable_emoji_spoken_text",
        "responsible_ai_policy_235",
        "enablemm",
        "dtappid",
        "rai253",
        "dv3sugg",
        "h3imaginative",
      ],
      allowedMessageTypes: ["Chat", "InternalSearchQuery"],
      isStartOfSession: true,
      message: {
        author: "user",
        inputMethod: "Keyboard",
        text: "",
        messageType: "Chat",
      },
      conversationId: "",
      conversationSignature: "",
      participant: {
        id: "",
      },
    },
  ],
  invocationId: "0",
  target: "chat",
  type: 4,
};

bingServer请求

请求就一个接口,暴露接口给外部获取

import { Request, catchAwait, MessageCenter } from "utils-lib-js"
import { config } from "../config.js"
// 请求对话信息接口的响应信息
export type IBingInfo = {
    clientId: string
    conversationId: string
    conversationSignature: string
    result: {
        message: unknown
        value: string
    }
}
// 切换可选项,防止报错
export type IBingInfoPartial = Partial<IBingInfo>
// 静态配置项结构
export type IConfig = {
    cookie: string
    proxyUrl: string
    bingUrl: string
    bingSocketUrl: string
}
// NewBingServer的构造函数配置
export type IOpts = {
    agent?: any
}
export class NewBingServer extends MessageCenter {
    bingInfo: IBingInfo
    readonly bingRequest: Request
    constructor(private opts: IOpts, private _config: IConfig = config) {
        super()
        const { bingUrl } = this._config
        this.bingRequest = new Request(bingUrl)// 初始化请求地址
        this.initServer()// 初始化request: 拦截器等
    }
    // 抛错事件
    throwErr(err: any) {
        this.emit("new-bing:server:error", err)
    }
    // 重置当前请求
    async reset() {
        this.clearBing()
        const bingInfo = await this.createConversation()
        this.init(bingInfo)
    }
    // 清除当前请求的信息
    clearBing() {
        this.bingInfo = null
    }
    // 赋值当前请求的信息
    init(bingInfo) {
        this.bingInfo = bingInfo
    }
    // 初始化request
    initServer() {
        this.bingRequest.use("error", console.error)
        // .use("response", console.log)
    }
    // 发起请求
    private async createConversation() {
        const { _config, opts, bingInfo } = this
        const { agent } = opts
        if (bingInfo) return bingInfo
        const { cookie } = _config
        const [err, res] = await catchAwait(this.bingRequest.GET("/turing/conversation/create", {}, null, {
            headers: { cookie },
            agent
        }))
        if (err) return this.throwErr(err)
        return res
    }
}

bingSocket消息

socket内容比较多,主要是针对不同的message的type进行区分

import WebSocket, { MessageEvent, Event, ErrorEvent, CloseEvent } from "ws";
import { getType, IObject, jsonToString, MessageCenter, stringToJson } from "utils-lib-js"
import { ClientRequestArgs } from "http"
import { config } from "../config.js"
import { IConfig, IBingInfoPartial } from "../server/index.js"
import { setConversationTemplate, Conversation } from '../helpers/index.js'
const fixStr = ''// 每段对话的标识符,发送接收都有
// websocket配置
export type IWsConfig = {
    address: string | URL
    options: WebSocket.ClientOptions | ClientRequestArgs
    protocols: string | string[]
}
// 发送socket消息的类型
export type IMessageOpts = {
    message: string | IObject<any>
}
// 发送对话的结构
export type IConversationMessage = {
    message: string
    invocationId: string | number
}
export class NewBingSocket extends MessageCenter {
    private ws: WebSocket // ws实例
    private bingInfo: IBingInfoPartial // 请求拿到的conversation信息
    private convTemp: Conversation.IConversationTemplate // 对话发送的消息模板
    private pingInterval: NodeJS.Timeout | string | number // ping计时器
    constructor(public wsConfig: Partial<IWsConfig>, private _config: IConfig = config) {
        super()
        const { bingSocketUrl } = this._config
        const { address } = wsConfig
        wsConfig.address = bingSocketUrl + address
    }
    // 将conversation信息赋值到消息模板中
    mixBingInfo(bingInfo: IBingInfoPartial) {
        const { conversationId, conversationSignature, clientId } = bingInfo
        this.bingInfo = bingInfo
        this.convTemp = setConversationTemplate({
            conversationId, conversationSignature, clientId
        })
        return this
    }
    // 创建ws
    createWs() {
        const { wsConfig, ws } = this
        if (ws) return this
        const { address, options, protocols } = wsConfig
        this.ws = new WebSocket(address, protocols, options)
        return this
    }
    // 重置ws
    clearWs() {
        const { ws } = this
        if (ws) {
            ws.close(4999, 'clearWs')
        }
        this.clearInterval()
        return this
    }
    // 抛错事件
    private throwErr(err: any) {
        this.emit("new-bing:socket:error", err)
    }
    // 开启ws后初始化事件
    initEvent() {
        const { ws, error, close, open, message } = this
        if (!ws) this.throwErr("ws未定义,不能初始化事件")
        ws.onerror = error
        ws.onclose = close
        ws.onopen = open
        ws.onmessage = message
        return this
    }
    // 发消息,兼容Object和string
    sendMessage = (opts: IMessageOpts) => {
        const { bingInfo, convTemp, ws } = this
        const { message } = opts
        if (!bingInfo || !convTemp) this.throwErr("对话信息未获取,或模板信息未配置,请重新获取信息")
        const __type = getType(message)
        let str = ""
        if (__type === "string") {
            str = message as string
        } else if (__type === "object") {
            str = jsonToString(message as IObject<unknown>)
        }
        this.emit("send-message", str)
        ws.send(str + fixStr)
    }
    // 收到消息
    private message = (e: MessageEvent) => {
        this.emit("message", e)
        onMessage.call(this, e)
    }
    // ws连接成功
    private open = (e: Event) => {
        this.emit("open", e)
        const { sendMessage } = this
        sendMessage({ message: { "protocol": "json", "version": 1 } })// 初始化
    }
    // ws关闭
    private close = (e: CloseEvent) => {
        const { ws } = this
        ws.removeAllListeners()
        this.ws = null
        this.emit("close", e)
    }
    // ws出错
    private error = (e: ErrorEvent) => {
        this.emit("error", e)
        console.log("error");
    }
    // 断线检测
    sendPingMsg() {
        const { ws } = this
        if (!ws) this.throwErr("ws未定义,无法发送Ping")
        this.startInterval()
        this.emit("init:finish", {})
    }
    // 开启断线定时器
    private startInterval() {
        this.clearInterval()
        this.pingInterval = setInterval(() => {
            this.sendMessage({ message: { "type": 6 } })
        }, 20 * 1000)
    }
    // 清空断线定时器
    private clearInterval() {
        const { pingInterval } = this
        if (pingInterval) {
            clearInterval(pingInterval)
            this.pingInterval = null
        }
    }
}

// 接收到消息
export function onMessage(e: MessageEvent) {
    const dataSource = e.data.toString().split(fixStr)[0]
    const data = stringToJson(dataSource)
    const { type } = data ?? {}
    switch (type) {
        case 1://对话中
            this.emit("message:ing", data.arguments?.[0]?.messages?.[0]?.text)
            break;
        case 2://对话完成
            this.emit("message:finish", data.item?.messages?.[1]?.text)
            break;
        case 6://断线检测
            // console.log(data);
            break;
        case 7://Connection closed with an error
            console.log(data);
            break;
        default:// 初始化响应
            this.sendPingMsg()
            break;
    }
}
// 发送聊天消息
export function sendConversationMessage(params?: IConversationMessage) {
    const { message, invocationId } = params
    const arg = this.convTemp.arguments[0]
    arg.message.text = message
    arg.isStartOfSession = invocationId === 0// 是否是新对话
    this.convTemp.invocationId = invocationId.toString()// 第几段对话
    this.sendMessage({ message: this.convTemp })
}

子线程入口部分

然后通过startBingConversation作为入口函数,对上面的两个模块进行调用

import { NewBingServer, IBingInfoPartial } from "./server/index.js"
import { NewBingSocket, sendConversationMessage } from "./socket/index.js"
import { config } from "./config.js"
import ProxyAgent from "proxy-agent"
import { parentPort } from "worker_threads";

const { proxyUrl } = config// 代理地址
const agent = ProxyAgent(proxyUrl)// 访问vpn代理地址
// 初始化bing请求
const bingServer = new NewBingServer({
    agent
})
// 初始化bing的websocket消息
const bingSocket = new NewBingSocket({
    address: "/sydney/ChatHub",
    options: {
        agent
    }
})
let invocationId = -1// 同一段对话的id
let bingInfo: IBingInfoPartial// bing的conversation信息,BingServer请求的结果
const startBingConversation = async () => {
    initEvent()
    await initBingServer()
    initBingSocket()
}

const initEvent = () => {
    bingServer.on("new-bing:server:error", (...args) => { throw new Error(...args) })// 请求抛错
    bingSocket.on("new-bing:socket:error", (...args) => { throw new Error(...args) })// 消息抛错
    // 接收主线程的消息
    parentPort.on("message", (res) => {
        const { type } = res
        if (type === "sendMessage") {
            // 发送消息
            sendConversationMessage.call(bingSocket, { message: res.message, invocationId: ++invocationId })
        }
    })
}
const initBingServer = async () => {
    await bingServer.reset()// 重置请求
    bingInfo = bingServer.bingInfo
}
const initBingSocket = () => {
    bingSocket.mixBingInfo(bingInfo).createWs().initEvent().on("init:finish", () => {// socket初始化完成
        parentPort.postMessage({
            type: "init:finish"
        })
    }).on("message:finish", (data = "") => {
        // 一段对话完成
        parentPort.postMessage({
            type: "message:finish",
            data
        })
    }).on("message:ing", (data = "") => {
        // 对话时,触发主线程loading操作
        parentPort.postMessage({
            type: "message:ing",
            data
        })
    })
}

startBingConversation()

主线程部分

主线程可以参照之前的打包工具,注册成系统命令,使用bing启动,通过readline进行对话交互

#!/usr/bin/env node
import { Worker } from "worker_threads";
import readline from "readline";
import { defer, logLoop, logOneLine } from "utils-lib-js";
const NewBing = new Worker("./src/index.js");
// 工厂模式
const readlineFactory = () => {
  return readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
};
let rl, loading;
// 解决node低版本无readline/promises模块,将异步函数换成promise
const readlinePromise = (...args) => {
  const { promise, resolve } = defer();
  rl.question(...args, resolve);
  return promise;
};
// 启动命令输入
const start = () => {
  readlinePromise("请输入:").then((res) => {
    console.log(`你:${res}`);
    NewBing.postMessage({ type: "sendMessage", message: res });
    loading = logLoop(); // 加载中动画
  });
};
// 关闭命令输入
const clear = () => {
  rl.close();
  rl = null;
};
// 重置
const reset = () => {
  if (rl) {
    clear();
  }
  rl = readlineFactory();
};
// 初始化当前命令窗口
const initBing = () => {
  reset();
  NewBing.on("message", (res) => {
    switch (res.type) {
      case "message:finish": // 收到消息,重置输入框,换行
        loading.isStop = true;
        logOneLine(`Bing:${res.data}`, true, true);
      case "init:finish": // 初始化完成
        start();
        break;
      case "message:ing": // 对话中
        // loading = logLoop(loadList);
        break;
    }
  });
};
initBing();

工具函数


import { conversationTemplate } from "../config.js"
import { readFileSync, writeFileSync } from "fs"
let conTemp: Conversation.IConversationTemplate = conversationTemplate
export namespace Conversation {
    // 对话模型类型
    // Creative:创造力的,Precise:精确的,Balanced:平衡的
    type ConversationStyle = 'Creative' | 'Precise' | 'Balanced'
    // 对话方式
    type ConversationType = 'SearchQuery' | 'Chat' // bing搜索,聊天
    // 模型映射
    export enum ConversationStr {
        Creative = 'h3imaginative',
        Precise = 'h3precise',
        Balanced = 'galileo'
    }
    // 发起对话时传入的参数
    export type IConversationOpts = {
        convStyle: ConversationStyle
        messageType: ConversationType
        conversationId: string
        conversationSignature: string
        clientId: string
    }
    type IMessage = {
        author: string,
        text: string,
        messageType: ConversationType,
    }
    type IArguments = {
        source: string
        optionsSets: string[]
        allowedMessageTypes: string[]
        isStartOfSession: boolean
        message: IMessage
        conversationId: string
        conversationSignature: string
        participant: {
            id: string
        }
    }
    // 发起对话的模板
    export type IConversationTemplate = {
        arguments: IArguments[]
        invocationId: string
        target: string
        type: number
    }
}
// 默认使用平衡类型
const { Balanced } = Conversation.ConversationStr
// 数据文件缓存(暂时没用上,调试的时候用的)
export function ctrlTemp(path?: string): any
export function ctrlTemp(path?: string, file?: any): void
export function ctrlTemp(path: string = "./temp", file?: string) {
    try {
        if (file) {
            return writeFileSync(path, file, "utf8")
        }
        return readFileSync(path, "utf8")
    } catch (error) { }
}

// 配置socket鉴权及消息模板
export function setConversationTemplate(params: Partial<Conversation.IConversationOpts> = {}): Conversation.IConversationTemplate {
    const { convStyle = Balanced, messageType = "Chat", conversationId,
        conversationSignature, clientId } = params
    if (!conversationId || !conversationSignature || !clientId) return null
    const args = conTemp.arguments[0]
    conTemp.arguments[0] = {
        ...args,
        conversationId,
        conversationSignature,
        participant: { id: clientId }
    }
    args.optionsSets.push(convStyle)// 这里传入对话风格
    args.message.messageType = messageType// 这里传入对话类型
    return conTemp
}

效果展示

我们使用npm link绑定全局命令

然后使用bing运行命令,并输入对话

写在最后

以上就是文章全部内容了,文章主要讲述了在node中实现一个与newbing对话的案例,希望能对你有帮助,对文章有任何问题欢迎评论或私信。

感谢你看到了这里,如果觉得文章不错的话,还望三连支持一下,非常感谢!

源码:Node-NewBing: 基于node+NewBing提供的AI模型做的案例

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

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

相关文章

MAX14866 16通道高电压模拟开关(不需要高电压供电)

总体介绍 MAX14866 是一个16通道高电压模拟开关&#xff0c;主要用在超声应用的高压多路传输中。 每一个通道的状态可以由一个高速的SPI接口控制&#xff0c;最高时钟为30MHz 详细介绍 MAX14866 是一个单刀单掷开关&#xff0c;以下是等效电路图 MAX14866由一个带有16位串…

什么是Lambda表达式?

什么是Lambda表达式 可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式&#xff1a;它没有名称&#xff0c;但它有参数列表、函数主体、返回类型&#xff0c;可能还有一个可以抛出的异常列表。 匿名&#xff1a;它不像普通的方法那样有一个明确的名称&#xff1…

Ae:材质选项

在 Ae 中&#xff0c;一个图层开启 3D 之后&#xff0c;会多出几何选项 Geometry Options和材质选项 Material Options两个属性组。材质用于 3D 对象的表面&#xff0c;而材质选项就是这些表面的属性&#xff0c;支配着对象与光线交互的方式。展开材质选项的快捷键&#xff1a;…

数据结构入门-9-线段树字典树并查集

文章目录一、线段数Segment Tree1.1 线段树的优势1.1.2 数组实现线段树1.2 线段树结构1.2.1 创建线段树1.2.2 线段树中的区间查询1.2.3 线段树的更新二、字典树 Trie1.2 字典树结构1.2.1 创建Trie1.2.2 Trie查询三、并查集3.1 并查集的实现3.1.1 QuickFind3.1.1 QuickUnion初始…

事件触发模式 LT ET ?EPOLLIN EPOLLOUT 各种情况总结。【面试复盘】【学习笔记】

麻了&#xff0c;对 epoll 的触发机制理解不深刻…面试又被拷打了… 下面总结一下各种情况&#xff0c;并不涉及底层原理&#xff0c;底层原理看这里。 文章结构可以看左下角目录、 有什么理解的不对的&#xff0c;请大佬们指点。 先说结论&#xff0c;下面再验证&#xff…

WRF-UCM 高精度城市化气象动力模拟、WRF+WRF-UCM 模拟气象场

查看原文>>>&#xff08;WRF-UCM&#xff09;高精度城市化气象动力模拟技术与案例应用 目录 模型基础理论 模型平台从零安装讲解 城市模块在线耦合&#xff08;WRFWRF-UCM&#xff09;模拟案例讲解 WRFWRF-UCM如何模拟气象场 实际应用及案例分析 其他大气相关推…

PostgreSQL插件—数据恢复工具pg_recovery使用详解

说明 pg_recovery 是一款基于PostgreSQL的数据恢复工具。针对表做了 update/delete/rollback/dropcolumn 后的数据恢复。 版本支持 pg_revovery当前支持 PostgreSQL 12/13/14 。 安装 下载插件 墨天轮下载地址&#xff1a;https://www.modb.pro/download/434516github下载地…

吃鸡录屏怎么录到自己的声音 吃鸡录屏怎么隐藏按键

很多人在玩吃鸡游戏时喜欢将自己的游戏过程录制下来&#xff0c;特别是很多游戏主播会录制视频&#xff0c;录制后将视频分享到社交平台。但是在录制时经常会遇到很多问题&#xff0c;如声音、画面清晰度和完整性等。接下来就来分享一下吃鸡录屏怎么录到自己的声音&#xff0c;…

pytorch单机多卡训练

多卡训练的方式 以下内容来自知乎文章&#xff1a;当代研究生应当掌握的并行训练方法&#xff08;单机多卡&#xff09; pytorch上使用多卡训练&#xff0c;可以使用的方式包括&#xff1a; nn.DataParalleltorch.nn.parallel.DistributedDataParallel使用Apex加速。Apex 是 N…

嵌入式学习笔记汇总

本文整理STM32、STM8和uCOS-III的所有文章链接。 STM32学习笔记目录 源码&#xff1a;mySTM32-learn STM32学习笔记&#xff08;1&#xff09;——LED和蜂鸣器 STM32学习笔记&#xff08;2&#xff09;——按键输入实验 STM32学习笔记&#xff08;3&#xff09;——时钟系统 …

.NET System.Management 获取windows系统和硬件信息

ManagementObject用于创建WMI类的实例与WINDOWS系统进行交互&#xff0c;通过使用WMI我们可以获取服务器硬件信息、收集服务器性能数据、操作Windows服务&#xff0c;甚至可以远程关机或是重启服务器。 WMI 的全称 Windows Management Instrumentation&#xff0c;即 Windows …

音视频八股文(1)--音视频基础

1.1.音视频录制原理 1.2.音视频播放原理 1.3.图像表示RGB-YUV 1.3.1 图像基础概念 ◼ 像素&#xff1a;像素是一个图片的基本单位&#xff0c;pix是英语单词picture的简写&#xff0c;加上英 语单词“元素element”&#xff0c;就得到了“pixel”&#xff0c;简称px&#xff…

使用Mingw64在CLion中搭建Linux开发环境

1.前言&#xff1a; 博主本来一直是在Visual Studio 2017中使用C语言编写程序&#xff0c;但有个问题是Visual Studio 2017默认使用自带的Windows SDK和编译器&#xff0c;我想使用POSIX文件操作就不行&#xff08;因为Windows中没有Linux SDK&#xff09;&#xff0c;虽然Wind…

【Kafka-架构及基本原理】Kafka生产者、消费者、Broker原理解析 Kafka原理流程图

【Kafka-架构及基本原理】Kafka生产者、消费者、Broker原理解析 & Kafka原理流程图1&#xff09;Kafka原理1.1.生产者流程细节1.2.Broker 的存储流程细节1.3.消费者流程细节2&#xff09;Kafka读写流程图1&#xff09;Kafka原理 1.1.生产者流程细节 1、生产者发送消息到 …

计算机毕业设计源码整合大全_kaic

以下为具体单个列表&#xff08;单个下载在我主页搜索即可&#xff09;&#xff1a; 1&#xff1a;计算机专业-ASP(499套&#xff09; ASP学生公寓管理系统的设计与实现(源代码论文).rar 1&#xff1a;计算机专业-ASP(499套&#xff09; ASP学科建设设计(源代码论文).ra…

Clickhouse 引擎之MergeTree详解

分区详解 数据存储底层分布 # 数据在这个位置 rootfjj001:~# cd /var/lib/clickhouse/data rootfjj001:/var/lib/clickhouse/data# ls # 数据库 default system rootfjj001:/var/lib/clickhouse/data# cd default/ rootfjj001:/var/lib/clickhouse/data/default# ls #表 enu…

ASEMI代理AD8400ARZ10-REEL原装ADI车规级AD8226ARZ-R7

编辑&#xff1a;ll ASEMI代理AD8400ARZ10-REEL原装ADI车规级AD8226ARZ-R7 型号&#xff1a;AD8400ARZ10-REEL 品牌&#xff1a;ADI/亚德诺 封装&#xff1a;SOIC-8 批号&#xff1a;2023 引脚数量&#xff1a;8 安装类型&#xff1a;表面贴装型 AD8400ARZ10-REEL汽车芯…

Zabbix监控系统——附详细步骤和图解

文章目录一、Zabbix概述1、使用zabbix的原因2、zabbix的概念和构成3、zabbix 监控原理&#xff1a;4、zabbix的程序组件二、安装 zabbix 5.01、部署 zabbix 服务端的操作步骤2、实例操作&#xff1a;部署 zabbix 服务端3、部署 zabbix 客户端4、实例操作&#xff1a;部署 zabbi…

【Linux】揭开套接字编程的神秘面纱(下)

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;前言&…

(二十三)槽函数的书写规则导致槽函数触发2次的问题

在创建QT的信号和槽时&#xff0c;经常无意间保留着QT书写槽函数的习惯&#xff0c;或者在QT设计界面直接右键【转到槽】去创建槽函数&#xff0c;但是后期需要用到disconnect时&#xff0c;又重新写了一遍connect函数&#xff0c;那么你会发现实际槽函数执行了2遍。 首先来看…