Qt之进程通信-IPC(QLocalServer,QLocalSocket 含源码+注释)

news2024/11/24 9:15:12

文章目录

  • 一、IPC通信示例图
    • 1.1 设置关键字并连接的示例图
    • 1.2 进程间简单的数据通信示例图
    • 1.3 断开连接的示例图
      • 1.3.1 由Server主动断开连接
      • 1.3.2 由Socket主动断开连接
  • 1.4 Server停止监听后的效果
  • 二、个人理解与一些心得
  • 三、一些疑问(求教 家人们😂)
  • 四、源码
    • CMainWindowServer
      • CMainWindowServer.h
      • CMainWindowServer.cpp
      • CMainWindowServer.ui
    • CMainWindowSocket
      • CMainWindowSocket.h
      • CMainWindowSocket.cpp
      • CMainWindowSocket.ui
  • 总结
  • 相关文章

一、IPC通信示例图

1.1 设置关键字并连接的示例图

如下,分别在各个界面的关键字控件中填入key,依次连接。
请添加图片描述

1.2 进程间简单的数据通信示例图

如下,简单演示了server与全部、指定socket通信及接收socket发送的数据。
请添加图片描述

1.3 断开连接的示例图

1.3.1 由Server主动断开连接

如下,演示了单独断开一个及断开全部的操作,其中断开操作是由server发送数据通知socket断开,server这边则等待断开返回。
请添加图片描述

1.3.2 由Socket主动断开连接

如下演示了socket程序主动断开的操作
请添加图片描述

1.4 Server停止监听后的效果

如下,演示了server停止监听后仍可以与已经连接过的socket的通信的效果。
请添加图片描述

二、个人理解与一些心得

  1. 若要使用QLocalServer/QLocalSocket,需要在 pro添加network模块(添加这一行QT += network)。
  2. 在我个人使用中发现,在同一进程中,调用socket的write是不会触发当前进程的readyRead信号链接的信号槽。
  3. 在QLocalServer停止监听后不会影响已经连接好的Socket对象,因为QLocalServer的close仅负责停止监听,并不断开。

三、一些疑问(求教 家人们😂)

  1. 在帮助中又下方的帮助代码,但是在本地测试发现不能先调用disconnectFromServer,后面的waitForDisconnected总是拿不到状态。
	socket->disconnectFromServer();
  	if (socket->waitForDisconnected(1000))
      qDebug("Disconnected!");
  1. 以及在个人理解中的第2点也存在一些疑问

四、源码

CMainWindowServer

CMainWindowServer.h

#ifndef CMAINWINDOWSERVER_H
#define CMAINWINDOWSERVER_H

#include <QMainWindow>
#include <QLocalServer>

namespace Ui {
class CMainWindowServer;
}

class QLocalSocket;
class CMainWindowServer : public QMainWindow
{
    Q_OBJECT

public:
    explicit CMainWindowServer(QWidget *parent = nullptr);
    ~CMainWindowServer();

private:
    /**
     * @brief disconnectSocketByStr 指定socket断开函数(复用)
     * @param socketStr 指定的socket套接字字符串
     */
    void disconnectSocketByStr(const QString &socketStr);

private slots:
    /**
     * @brief on_btnListen_clicked 开始监听按钮
     */
    void on_btnListen_clicked();

    /**
     * @brief on_btnStopListen_clicked 停止监听按钮
     */
    void on_btnStopListen_clicked();

    /**
     * @brief on_newConnection 新连接槽函数
     */
    void on_newConnection();

    /**
     * @brief on_socketReadyRead 数据接收槽函数
     */
    void on_socketReadyRead();

    /**
     * @brief on_btnDisconnectSocket_clicked 断开socket槽函数
     */
    void on_btnDisconnectSocket_clicked();

    /**
     * @brief on_btnSend_clicked 数据发送按钮
     */
    void on_btnSend_clicked();

private:
    Ui::CMainWindowServer *ui;

    QLocalServer            m_localServer;          // 通信服务对象

    QList<QLocalSocket *>   m_listLocalSockets;     //  本地套接字列表
};

#endif // CMAINWINDOWSERVER_H

CMainWindowServer.cpp

#include "CMainWindowServer.h"
#include "ui_CMainWindowServer.h"

#include <QLocalServer>
#include <QMessageBox>
#include <QLocalSocket>
#include <QDebug>
#include <QTimer>

CMainWindowServer::CMainWindowServer(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::CMainWindowServer)
{
    ui->setupUi(this);
    // 关联套接字连接槽函数
    connect(&m_localServer, &QLocalServer::newConnection, this, &CMainWindowServer::on_newConnection);
}

CMainWindowServer::~CMainWindowServer()
{
    delete ui;
}

void CMainWindowServer::disconnectSocketByStr(const QString &socketStr)
{
    // 强转当前指针字符串或者socket指针对象
    QLocalSocket *socket = (QLocalSocket *)socketStr.toUInt();
    // 判断是否存在于socket容器中
    if(m_listLocalSockets.contains(socket)) {
        // 发送关闭提示给socket
        socket->write(u8"服务器断开!");
        // 等待3000毫秒接收断开链接的信息
        if(!socket->waitForDisconnected(3000)) {
            QMessageBox::information(this, u8"提示", "断开超时");
        }
        else {
            // 移除当前位置的控件
            QMessageBox::information(this, u8"提示", "断开成功");
            // 移除当前指定的
            ui->comboBoxSockets->removeItem(ui->comboBoxSockets->findText(socketStr));
            m_listLocalSockets.removeOne(socket);
        }
    }
    else {
        QMessageBox::information(this, u8"提示", socketStr + u8"地址无记录");
    }
}

void CMainWindowServer::on_btnListen_clicked()
{
    QString listenKey = ui->lineEditListenKey->text();
    // 获取是否监听成功
    bool flag =  m_localServer.listen(listenKey);
    if(!flag) {
        QMessageBox::information(this, u8"提示", m_localServer.errorString());
    }
    else {
        QMessageBox::information(this, u8"提示", u8"监听成功");
        // 监听后‘开始监听’按钮禁用,‘停止监听’按钮启用
        ui->btnListen->setEnabled(false);
        ui->btnStopListen->setEnabled(true);
    }
}

void CMainWindowServer::on_btnStopListen_clicked()
{
    m_localServer.close();
    if(!m_localServer.isListening()) {
        QMessageBox::information(this, u8"提示", u8"停止监听成功");
        // 停止监听后‘开始监听’按钮启用,‘停止监听’按钮禁用
        ui->btnListen->setEnabled(true);
        ui->btnStopListen->setEnabled(false);
    }
    else {
        QMessageBox::information(this, u8"提示", u8"停止监听失败");
    }
}

void CMainWindowServer::on_newConnection()
{
    // 判断是否存在新的socket连接
    if(m_localServer.hasPendingConnections()) {
        // 获取套接字对象
        QLocalSocket *socketTmp = m_localServer.nextPendingConnection();
        // 套接字对象添加到套接字容器中
        m_listLocalSockets.append(socketTmp);
        // 套接字地址转为数值
        QString socketStr = QString::number((uint64_t)socketTmp);
        // 套接字文本添加到下拉列表中并在界面做出连接提示
        ui->comboBoxSockets->addItem(socketStr);
        ui->textEdit->append(socketStr + "加入连接!");
        // 关联新数据的信号槽
        connect(socketTmp, &QLocalSocket::readyRead, this, &CMainWindowServer::on_socketReadyRead);
    }
}

void CMainWindowServer::on_socketReadyRead()
{
    // 获取发送信号的对象
    QLocalSocket *curSocket = dynamic_cast<QLocalSocket *>(sender());
    // 将数据直接读取并添加到多行文本框中
    ui->textEdit->append(QString::number((uint64_t)curSocket) + ":" + curSocket->readAll());
}

void CMainWindowServer::on_btnDisconnectSocket_clicked()
{
    // 获取将要断开的文本并弹出断开提示
    QString socketStr = ui->comboBoxSockets->currentText();
    QMessageBox::StandardButton flag = QMessageBox::information(this, u8"提示", u8"是否断开" + socketStr + "?");
    if(QMessageBox::Ok != flag) {
        return;
    }

    // 根据断开文本做不不同断开操作
    if(0 == socketStr.compare(u8"全部")) {
        foreach(QLocalSocket *socket, m_listLocalSockets) {
            disconnectSocketByStr(QString::number((uint64_t)socket));
        }
    }
    else {
        disconnectSocketByStr(socketStr);
    }
}

void CMainWindowServer::on_btnSend_clicked()
{
    // 获取将要接收数据的识别文本
    QString socketStr = ui->comboBoxSockets->currentText();
    // 获取将要发送的数据
    QString data = ui->textEditSendData->toPlainText();

    // 根据识别文本做出不同的操作
    if(0 == socketStr.compare(u8"全部")) {
        foreach(QLocalSocket *socket, m_listLocalSockets) {
            socket->write(data.toUtf8());
        }
    }
    else {
        // 直接将当前文本强转为套接字对象(因为该文本为指针地址强转而来)
        QLocalSocket *socket = (QLocalSocket *)socketStr.toUInt();
        if(m_listLocalSockets.contains(socket)) {
            socket->write(data.toUtf8());
        }
        else {
            QMessageBox::information(this, u8"提示", socketStr + "地址找不到");
        }
    }

}

CMainWindowServer.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>CMainWindowServer</class>
 <widget class="QMainWindow" name="CMainWindowServer">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>340</width>
    <height>420</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>CMainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,3,1,0">
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <widget class="QLineEdit" name="lineEditListenKey"/>
      </item>
      <item>
       <widget class="QPushButton" name="btnListen">
        <property name="text">
         <string>监听</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnStopListen">
        <property name="enabled">
         <bool>false</bool>
        </property>
        <property name="text">
         <string>停止监听</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_3">
      <item>
       <widget class="QComboBox" name="comboBoxSockets">
        <item>
         <property name="text">
          <string>全部</string>
         </property>
        </item>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnDisconnectSocket">
        <property name="text">
         <string>断开当前链接选项</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item>
     <widget class="QTextEdit" name="textEdit"/>
    </item>
    <item>
     <widget class="QTextEdit" name="textEditSendData"/>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_2">
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="btnSend">
        <property name="text">
         <string>发送</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>340</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

CMainWindowSocket

CMainWindowSocket.h

#ifndef CMAINWINDOWSOCKET_H
#define CMAINWINDOWSOCKET_H

#include <QMainWindow>
#include <QLocalSocket>

namespace Ui {
class CMainWindowSocket;
}

class CMainWindowSocket : public QMainWindow
{
    Q_OBJECT

public:
    explicit CMainWindowSocket(QWidget *parent = nullptr);
    ~CMainWindowSocket();

private slots:
    /**
     * @brief on_btnConnect_clicked 连接按钮信号槽
     */
    void on_btnConnect_clicked();

    /**
     * @brief on_btnSend_clicked 发送按钮信号槽
     */
    void on_btnSend_clicked();

    /**
     * @brief on_btnDisConnected_clicked 断开连接信号槽
     */
    void on_btnDisConnected_clicked();

    /**
     * @brief on_socketReadyRead 数据接收信号槽
     */
    void on_socketReadyRead();

private:
    Ui::CMainWindowSocket *ui;

    QLocalSocket    m_localSocket;  // 套接字对象
};

#endif // CMAINWINDOWSOCKET_H

CMainWindowSocket.cpp

#include "CMainWindowSocket.h"
#include "ui_CMainWindowSocket.h"

#include <QMessageBox>
#include <QTimer>

CMainWindowSocket::CMainWindowSocket(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::CMainWindowSocket)
{
    ui->setupUi(this);
    // 关联数据接收信号槽
    connect(&m_localSocket, &QLocalSocket::readyRead, this, &CMainWindowSocket::on_socketReadyRead);
}

CMainWindowSocket::~CMainWindowSocket()
{
    delete ui;
}

void CMainWindowSocket::on_btnConnect_clicked()
{
    // 根据key连接服务
    m_localSocket.connectToServer(ui->lineEditConnectKey->text());
    // 等待一秒是否连接成功
    if(m_localSocket.waitForConnected(1000)) {
        QString tip = u8"连接成功";
        // 连接成功后打开读写通道
        if(!m_localSocket.open(QIODevice::ReadWrite)) {
            tip.append(QString(u8",但Socket读写打开失败(%1)").arg(m_localSocket.errorString()));
        }
        QMessageBox::information(this, u8"提示", tip);
        // 连接后‘连接’按钮禁用,‘断开连接’按钮启用
        ui->btnConnect->setEnabled(false);
        ui->btnDisConnected->setEnabled(true);

    }
    else {
        QMessageBox::information(this, u8"提示", u8"连接失败");
    }
}

void CMainWindowSocket::on_btnSend_clicked()
{
    // 写入数据
    m_localSocket.write(ui->textEditSendData->toPlainText().toUtf8());
    // 等待写入信号,若未写入成功弹出提示
    if(!m_localSocket.waitForBytesWritten(100)) {
        QMessageBox::information(this, u8"提示", m_localSocket.errorString());
    }
}

void CMainWindowSocket::on_btnDisConnected_clicked()
{
    if(QLocalSocket::ConnectedState == m_localSocket.state()) {
        m_localSocket.write(QString(u8"%1已断开!").arg((uint64_t)this).toUtf8());
        m_localSocket.disconnectFromServer();
        // 断开连接后‘连接’按钮启用,‘断开连接’按钮禁用
        ui->btnConnect->setEnabled(true);
        ui->btnDisConnected->setEnabled(false);
    }
    else {
        QMessageBox::information(this, u8"提示", u8"断开失败,当前并非连接状态!" );
    }
}

void CMainWindowSocket::on_socketReadyRead()
{
    // 读取索引数据
    QString data = m_localSocket.readAll();
    // 识别数据文本,当复合条件是断开连接
    if(0 == data.compare(u8"服务器断开!")) {
        on_btnDisConnected_clicked();
    }
    ui->textEdit->append(data);
}

CMainWindowSocket.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>CMainWindowSocket</class>
 <widget class="QMainWindow" name="CMainWindowSocket">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>300</width>
    <height>420</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>CMainWindowSocket</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QVBoxLayout" name="verticalLayout" stretch="0,3,1,0">
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <widget class="QLineEdit" name="lineEditConnectKey"/>
      </item>
      <item>
       <widget class="QPushButton" name="btnConnect">
        <property name="text">
         <string>连接</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnDisConnected">
        <property name="enabled">
         <bool>false</bool>
        </property>
        <property name="text">
         <string>断开连接</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item>
     <widget class="QTextEdit" name="textEdit"/>
    </item>
    <item>
     <widget class="QTextEdit" name="textEditSendData"/>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_2">
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="btnSend">
        <property name="text">
         <string>发送</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>300</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

总结

在使用QLocalServer和QLocalSocket的过程中,发现QLocalSocket不是数据通道的持有对象,而是数据通道本身(如共享内存是通过data获取共享内存的地址,而QLocalSocket是直接调用write写入,当然和他的继承有关系),而相对来说QLocalServer更像使用者。不过IPC相对于共享内存来说可能有及时性的特点,因为数据一来IPC就直接读取,而共享内存则是需要定时读取数据。

相关文章

Qt之进程通信-共享内存(含源码+注释)

友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)

注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除

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

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

相关文章

VueRouter的基本使用

路由的基本使用 文章目录 路由的基本使用01-VueRouterVueRouter的使用 &#xff08; 5 2&#xff09;综合代码 拓展&#xff1a;组件存放问题 什么是路由呢&#xff1f; 在生活中的路由&#xff1a;设备和IP的映射关系 在Vue中&#xff1a;路径 和 组件 的 映射 关系。 01-Vu…

与面试官互动:建立积极的技术讨论氛围

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

OpenCV简介

OpenCV简介 OpenCV&#xff08;开源计算机视觉库&#xff1a;http://opencv.org&#xff09;是一个开源库&#xff0c;包含数百种计算机视觉算法。OpenCV 具有模块化结构&#xff0c;主要包括下列模块&#xff1a; 核心功能&#xff08;core&#xff09; - 定义基本数据结构的…

3、监测数据采集物联网应用开发步骤(3)

监测数据采集物联网应用开发步骤(2) 系统整体结构搭建 新建项目 输入项目名称&#xff1a;MonitorData 所谓兵马未动粮草先行&#xff0c;按下图创建好对应的模块备用&#xff1a; com.plugins 业务插件模块 com.zxy.adminlog 日志或文本文…

【从零开始学习JAVA | 第四十六篇】处理请求参数

前言&#xff1a; 在我们之前的学习中&#xff0c;我们已经基本学习完了JAVA的基础内容&#xff0c;从今天开始我们就逐渐进入到JAVA的时间&#xff0c;在这一大篇章&#xff0c;我们将对前后端有一个基本的认识&#xff0c;并要学习如何成为一名合格的后端工程师。今天我们介绍…

Apache Celeborn 让 Spark 和 Flink 更快更稳更弹性

摘要&#xff1a;本文整理自阿里云/数据湖 Spark 引擎负责人周克勇&#xff08;一锤&#xff09;在 Streaming Lakehouse Meetup 的分享。内容主要分为五个部分&#xff1a; Apache Celeborn 的背景Apache Celeborn——快Apache Celeborn——稳Apache Celeborn——弹Evaluation…

河湖长制综合管理信息平台建设项目总体设计方案[507页Word]

导读&#xff1a;原文《河湖长制综合管理信息平台建设项目总体设计方案[507页Word]》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 部分内容&#xff1a; 1.1.1.3…

【conda install】网络慢导致报错CondaHTTPError: HTTP 000 CONNECTION FAILED for url

⭐⭐问题&#xff1a; 部署安装环境经常会出现由于网络慢问题&#xff0c;导致conda安装不了库&#xff0c;报错如下&#xff1a; Solving environment: failedCondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/…

什么是应用程序监控

应用程序监控是一项基本功能&#xff0c;可以对两者进行实时分析关键业务应用程序的前端和后端性能。应用程序监控起着至关重要的作用 通过提供对应用程序的宝贵见解来确保应用程序的不间断运行可用性、性能和最终用户体验。主动监控应用程序有助于快速识别和解决任何潜在问题&…

最新ChatGPT程序源码+AI系统+详细图文搭建教程/支持GPT4/AI绘画/H5端/完整Prompt知识库

一、AI系统 如何搭建部署人工智能源码、AI创作系统、ChatGPT系统呢&#xff1f;小编这里写一个详细图文教程吧&#xff01;SparkAi使用Nestjs和Vue3框架技术&#xff0c;持续集成AI能力到AIGC系统&#xff01; 1.1 程序核心功能 程序已支持ChatGPT3.5/GPT-4提问、AI绘画、Mi…

spring打入filter内存马+冰蝎成功

环境&#xff1a; springboot版本2.4.5 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.5</version><relativePath/> <!-- lookup parent from r…

设计模式—观察者模式(Observer)

目录 思维导图 一、什么是观察者模式&#xff1f; 二、有什么优点吗&#xff1f; 三、有什么缺点吗&#xff1f; 四、什么时候使用观察者模式&#xff1f; 五、代码展示 ①、双向耦合的代码 ②、解耦实践一 ③、解耦实践二 ④、观察者模式 六、这个模式涉及到了哪些…

12.工作数字钟

效果 源码 <!doctype html> <html><head><meta charset="utf-8"><title>Digital Clock</title><link rel="stylesheet" href="style.css"></head><body><div id="time">…

开源项目的测试和质量保证

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

《Flink学习笔记》——第一章 概念及背景

​ 什么是批处理和流处理&#xff0c;然后由传统数据处理架构为背景引出什么是有状态的流处理&#xff0c;为什么需要流处理&#xff0c;而什么又是有状态的流处理。进而再讲解流处理的发展和演变。而Flink作为新一代的流处理器&#xff0c;它有什么优势&#xff1f;它的相关背…

短视频seo搜索矩阵系统源码----独立应用搭建

前言&#xff1a;抖音账号矩阵系统/抖音seo霸屏系统/抖音矩阵seo系统源码/独立部署,技术团队如何围绕抖音矩阵关键词霸屏来做开发&#xff1f;来做到抖音seo优化达到账号排名效果&#xff0c;关键词起到至关重要的作用&#xff0c;依托于抖音平台的正规权限。 当普通对象属性更…

启动项目时Service有却找不到而报错

解决方法&#xff0c;重启一下Idea&#xff0c;看是否可以&#xff0c;如果不行的话 点击File->setting中 勾上&#xff0c;重启一下。接可以了

PMAC与Modbus主站进行Modbus Tcp通讯

PMAC与Modbus主站进行Modbus Tcp通讯 创建modbus通讯参数 在项目的PMAC Script Language\Global Includes下创建一个名为00_Modbus_Para.pmh的pmh文件。 Modbus[0].Config.ServerPort 0 Modbus[0].Config.ConnectTimeOut 6000 Modbus[0].Config.SendRecvTimeOut 0 Modbu…

基于django框架的学生选课系统jsp学校教务信息java源代码Mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于django框架的学生选课系统 系统有2权限&#xff…

好用的电容笔有哪些推荐?适合开学季电容笔推荐

至于是用苹果原装的电容笔还是平替式电容笔&#xff0c;那就要看你自己的使用需求的了&#xff0c;例如常用于绘画上的话&#xff0c;就用苹果笔&#xff1b;如果你每天花在书写上的时间比花在画图上的时间还多&#xff0c;那么你可以考虑买一款便宜一点的平替电容笔。小编整理…