SpringBoot3 + Flowable7 工作流引擎使用笔记

目录

  • Flowable 简介
  • 流程设计器
    • 安装
    • 使用
  • SpringBoot 3 整合
  • 表结构
  • 流程部署
  • 启动流程
  • 流程审批
  • 流程挂起和激活
  • 任务分配
    • 固定分配
    • 表达式分配
      • 值表达式
      • 方法表达式
    • 监听器分配
  • 流程变量
    • 运行时变量
    • 历史变量
  • 身份服务
    • 候选人
    • 拾取任务
    • 归还任务
    • 指派给别人
    • 候选人组
      • 创建用户
      • 创建用户组
      • 用户关联用户组
      • 流程图设置用户组
      • 查询组待办任务
  • 网关
    • 排他网关
    • 并行网关
    • 包含网关

Flowable 简介

Flowable 由 Activiti 项目分支演变而来,用于构建和管理各种业务流程。其核心是一个通用的流程引擎,支持 BPMN 2.0(Business Process Model and Notation)标准,这是一种业务流程建模和执行的国际标准。Flowable 完全支持 BPMN 2.0,使得非技术人员也能参与流程的设计。

BPMN 是一种用于业务流程建模的标准化图形表示法。它定义了一套符号和规则,用于描述业务流程的各个方面,如任务、事件、网关等。BPMN 的目标是提供一种统一的、易于理解的图形化语言来表示业务流程。

BPMN 2.0是 BPMN 规范的2.0版本,是当前比较稳定且广泛使用的版本。

Flowable 7.x 是目前 Flowable 的最新版本,该版本基于 JDK 17,如果使用 Spring Boot 集成的话,需要 Spring Boot 的版本最少为 SpringBoot 3.x。

官方文档地址

流程设计器

安装

BPMN 定义了如何用符号来描述业务流程,这种符号组合在一起就是个模型,但是程序不能直接识别这些符号,因此 BPMN 规定使用 XML 格式来编码业务流程模型,这种编码形式称为 BPMN XML。

我们一般使用流程设计器画图业务流程模型,再导出为 BPMN 规范的 XML 文件,然后再使用。

Flowable 官方有提供一个名为 Flowable-UI 的东西,这是一个 Web 应用,可以直接在这上面设计业务流程,但是 Flowable-UI 从 Flowable 7 以后就没提供了,但是经过我的测试,用旧版本的UI 也可以实现对应功能,以下提供一个我目前使用版本的 Docker 命令:

docker run -p 8080:8080 flowable/flowable-ui:6.8.0

使用以下命令启动后,访问地址端口,可以看到以下界面:
flowable-ui
使用默认的用户名密码登录:

user: admin
password: test

flowable-ui登陆后的页面

使用

打开建模应用程序,可以看到以下界面:
在这里插入图片描述
点击创建流程,可以创建一个业务流程,其中模型 key 用于唯一标识一个业务流程模型,这个后面会有用的,模型名称可以重复:
在这里插入图片描述
创建完成后,进入流程的编辑页面:
在这里插入图片描述
编辑完成后,保存,查看模型,会进到这个界面,这里可以将模型导出成为XML:
在这里插入图片描述

SpringBoot 3 整合

使用 SpringBoot 整合,首先需要引入以下依赖:

<!-- https://mvnrepository.com/artifact/org.flowable/flowable-spring-boot-starter -->
<dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>7.0.1</version>
</dependency>

由于 Flowable 运行需要数据库的支持,所以需要配置一个数据源:

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/flowable_test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=trueusername: rootpassword: 123456type: com.zaxxer.hikari.HikariDataSource
flowable:# 关闭定时任务JOBasync-executor-activate: false# 在引擎启动时,如果数据库架构与 Flowable 引擎期望的架构不一致,Flowable 会自动更新数据库架构。这包括创建缺失的表和列,以及修改现有的表和列以匹配最新版本database-schema-update: true

配置数据源后,启动项目,Flowable会自动识别数据库,并帮你新建对应的表,大约有70个。

表结构

主要表前缀及其用途

  • ACT_RE_*:RE 代表 repository(存储)。这些表包含静态信息,如流程定义和流程的资源(图片、规则等)。RepositoryService 接口操作的表。
  • ACT_RU_*:RU 代表 runtime。这些表存储运行时信息,如流程实例、用户任务、变量、作业等。Flowable 只在流程实例运行中保存运行时数据,并在流程实例结束时删除记录,以保证运行时表小且快。RuntimeService 接口操作的表。
  • ACT_HI_*:HI 代表 history。这些表存储历史数据,如已完成的流程实例、变量、任务等。HistoryService 接口操作的表。
  • ACT_ID_*:ID 表示 identity(组织机构)。这些表包含标识的信息,如用户、用户组等。IdentityService 接口操作的表。
  • ACT_GE_*:通用数据表,用于存储各种情况下都可能需要的数据。

核心表

  • act_ge_bytearray:二进制数据表,用于存储流程定义、流程模板、流程图的图片等
  • act_re_deployment:记录部署操作的表,一次部署操作对应一条记录
  • act_re_procde:流程定义表,一次部署可以部署多个流程,一个流程对应一条记录
  • act_ru_task:存储运行中流程的任务节点信息,常用于查询人员或部门的待办任务。
  • act_ru_execution:运行时流程执行实例表,记录运行中流程运行的各个分支信息。
  • act_hi_procinst:历史流程实例表,存储流程实例历史数据(包含正在运行的流程实例)。

流程部署

  1. 导出流程模型 XML
  2. 通过 ProcessEngine 获取 RepositoryService,或直接注入 RepositoryService
    private ProcessEngine processEngine;
    private RepositoryService repositoryService;@Autowired
    public void setRepositoryService(RepositoryService repositoryService) {this.repositoryService = repositoryService;
    }@Autowired
    public void setProcessEngine(ProcessEngine processEngine) {this.processEngine = processEngine;
    }@Test
    public void test() {System.out.println(repositoryService != null && repositoryService == processEngine.getRepositoryService());
    }
    
  3. 部署流程图,一次部署可以部署多个
    @Test
    public void testDeploy() {Deployment deploy = repositoryService//创建一个部署.createDeployment()//部署的流程图.addClasspathResource("flowable/Example01.bpmn20.xml")//这次部署的名字.name("test_deploye")//执行部署.deploy();
    }
    

启动流程

  1. 部署一个流程,获取到部署的流程ID,或者获取到流程定义 Key,就是一开始画图时候填写的那个 Key,ID 和 Key 可以从 ACT_RE_PROCDEF 表忠获取。
  2. 通过 ProcessEngine 获取 RepositoryService,或直接注入 RepositoryService
    private RuntimeService runtimeService;@Autowired
    public void setRuntimeService(RuntimeService runtimeService) {this.runtimeService = runtimeService;
    }
    
  3. 通过流程 ID 或 Key 启动一个流程实例
    @Test
    public void testStart() {ProcessInstance processInstanceById = runtimeService.startProcessInstanceById("Example01:1:085efe64-57f2-11ef-aa6e-4c034f4db418");ProcessInstance processInstanceByKey = runtimeService.startProcessInstanceByKey("Example01");
    }
    

流程审批

  1. 任务类的操作都是使用 TaskService 完成的,可以使用 ProcessEngine 获取,或直接注入
    private TaskService taskService;@Autowired
    public void setTaskService(TaskService taskService) {this.taskService = taskService;
    }
    
  2. 查询某个用户的代办任务,获取到 task id
    @Test
    public void testQuery() {List<Task> taskList = taskService.createTaskQuery()//查询所有zhangsan用户代办的任务.taskAssignee("zhangsan").list();
    }
    
  3. 使用 complete 完成审批
    @Test
    public void testComplete() {taskService.complete("e4c98b66-5856-11ef-85d0-4c034f4db418");
    }
    

流程挂起和激活

Flowable 可以针对流程定义和流程实例进行流程挂起和激活。

如果将流程定义挂起,再尝试启动一个新的流程实例,会报 xxx is suspended 异常,但是不会影响已经创建的实例继续:

@Test
public void testSuspend() {//挂起流程定义repositoryService.suspendProcessDefinitionById("Example01:1:085efe64-57f2-11ef-aa6e-4c034f4db418");
}@Test
public void testActivate() {//激活流程定义repositoryService.activateProcessDefinitionById("Example01:1:085efe64-57f2-11ef-aa6e-4c034f4db418");
}

如果对某一个流程实例进行挂起,则该实例无法再进行后续操作:

@Test
public void testSuspendTask() {//挂起流程实例runtimeService.suspendProcessInstanceById("997106c1-586f-11ef-8c2e-4c034f4db418");
}@Test
public void testActivateTask() {//激活流程实例runtimeService.activateProcessInstanceById("997106c1-586f-11ef-8c2e-4c034f4db418");
}

任务分配

Flowable 的任务分配是流程管理中的一个重要环节,它决定了流程中的任务由谁来执行。

Flowable支持多种任务分配方式,主要包括固定分配、表达式分配和监听器分配。

固定分配

固定分配是最直接的任务分配方式,即在绘制流程图时,直接在流程文件中通过 Assignee 属性来指定任务的执行者。这种方式简单明了,但缺乏灵活性,一旦流程设计完成,任务的执行者就固定下来了。
在这里插入图片描述

表达式分配

表达式分配是 Flowable 中更为灵活的任务分配方式,它允许在流程执行时动态地确定任务的执行者。Flowable 支持两种 UEL(Unified Expression Language)表达式:值表达式(Value Expression)和方法表达式(Method Expression)。

值表达式

解析为一个值,通常用于直接指定任务的执行者。例如,${assignee} 这样的表达式会在流程执行时被解析为流程变量 assignee 的值,该值即为任务的执行者。
在这里插入图片描述

设置好表达式后,可以在启动流程实例时或调用 complete 时使用 map 传递变量,传递过的变量可以在后续的步骤中通用:

@Test
public void testStart() {Map<String, Object> map = new HashMap<>();map.put("assignee", "zhangsan");ProcessInstance processInstanceById = runtimeService.startProcessInstanceById("Example_02:1:736ec65b-588c-11ef-8923-4c034f4db418", map);
}@Test
public void testComplete() {Map<String, Object> map = new HashMap<>();map.put("assignee1", "lisi");taskService.complete("c228521f-588d-11ef-b80c-4c034f4db418", map);
}

方法表达式

调用一个方法,可以带或不带参数。这种方法允许在流程执行时通过调用某个方法来动态确定任务的执行者。例如,${userService.findAssigneeByTaskId(taskId)} 这样的表达式会调用userService 的 findAssigneeByTaskId 方法来获取任务的执行者。

这里的 userService 需要是容器中的一个 bean。

监听器分配

监听器分配是一种更为高级的任务分配方式,它通过在流程中设置监听器来动态地改变任务的执行者。监听器可以在任务创建、分配、完成等关键节点触发,并执行相应的逻辑来改变任务的执行者。例如,可以设置一个任务创建监听器,在任务创建时根据某些条件(如发起人的部门、任务的优先级等)来动态指定任务的执行者。

  1. 编写一个监听器类实现 TaskListener 接口
    public class MyTaskListener implements TaskListener {@Overridepublic void notify(DelegateTask delegateTask) {}
    }
    
  2. 将类的全限定名设置到流程图
    在这里插入图片描述
    在这里插入图片描述
  3. 重写 notify 方法实现指派
    public class MyTaskListener implements TaskListener {@Overridepublic void notify(DelegateTask delegateTask) {switch (delegateTask.getEventName()) {case EVENTNAME_CREATE -> {//节点创建,指派处理人delegateTask.setAssignee("zhangsan");}}}
    }
    

流程变量

流程变量是 Flowable 管理工作流时根据管理需要而设置的变量。它们用于在流程的不同阶段之间传递信息,如任务分配、条件判断等。流程变量可以是任何类型的数据,如字符串、整数、浮点数、布尔值等。

流程变量的作用域可以是一个流程实例(ProcessInstance)、一个任务(Task)或者是一个执行实例(execution)。流程变量的默认作用域是流程实例,此时可以称为global变量。Global变量在整个流程实例中都是可见的,但变量名不允许重复,后设置的值会覆盖之前设置的值。任务和执行实例的变量作用域相对较小,仅针对一个任务或一个执行实例范围,称为local变量。Local变量在不同的任务或执行实例中互不影响,即使变量名相同也不会互相冲突。

Flowable 将流程变量分为两种类型:运行时变量和历史变量。

  • 运行时变量:流程实例运行时的变量,存入 act_ru_variable 表中。在流程实例运行结束时,这些变量在表中会被删除。因此,查询一个已经完结的流程实例的变量时,需要在历史变量表中查找。
  • 历史变量:存入 act_hi_varinst 表中。在流程启动时,流程变量会同时存入历史变量表中;在流程结束时,历史表中的变量仍然存在。这可以视为“永久代”的流程变量。

运行时变量

在流程实例启动时,可以通过 startProcessInstanceByKey 或 startProcessInstanceById 等方法的可选参数来设置流程变量。这些变量将作为全局变量(Global Variables),在整个流程实例中有效:

@Test
public void testStart() {Map<String, Object> map = new HashMap<>();map.put("v1", "123456");map.put("v2", "asfafs");map.put("v3", "fasaf");ProcessInstance processInstanceById = runtimeService.startProcessInstanceById("Example_02:1:736ec65b-588c-11ef-8923-4c034f4db418", map);
}

在流程执行过程中,可以通过 setVariable、setVariableLocal、setVariables、setVariablesLocal 等方法为特定的执行实例(Execution)或任务(Task)设置变量。这些变量可以是局部变量(Local Variables),仅在当前执行实例或任务中有效:

runtimeService.setVariable(executionId, "variableName", variableValue);  
// 或者为当前执行实例设置局部变量  
runtimeService.setVariableLocal(executionId, "variableName", variableValue);taskService.setVariable(taskId, "variableName", variableValue);
// 或者为当前task设置局部变量  
taskService.setVariableLocal(taskId, "variableName", variableValue);

可以在流程审批的时候设置变量:

@Test
public void testStart() {Map<String, Object> map = new HashMap<>();map.put("v1", "123456");map.put("v2", "asfafs");map.put("v3", "fasaf");taskService.complete(taskId, map);
}

获取流程变量:

Object variableValue = runtimeService.getVariable(executionId, variableName);
Object taskVariableValue = taskService.getVariable(taskId, variableName);

历史变量

Flowable 将历史变量存储在特定的数据库表中,通常这个表名为 ACT_HI_VARINST。这个表包含了流程实例运行过程中的所有变量实例的信息,包括变量的名称、类型、值以及它们所属的流程实例和任务实例等。

身份服务

候选人

在 Flowable 中,候选人是指可能被分配给某个任务的用户或用户组。当流程执行到某个任务节点时,该节点的候选人可以领取并执行任务。这种方式避免了在流程定义时固定设置任务负责人,使得在需要变更任务负责人时无需修改流程定义,提高了系统的可扩展性和灵活性。

可以在设计器直接分配候选人:
在这里插入图片描述

拾取任务

候选人不是审批人,需要进行拾取操作才能进行审批。多个候选人只有一个能变成审批人。

  1. 查看候选人任务
    List<Task> zhangsan = taskService.createTaskQuery().taskCandidateUser("zhangsan").list();
    
  2. 拾取任务
    taskService.claim(taskId,"zhangsan");
    

归还任务

@Test
public void testReturn() {//审批人 -> 候选人taskService.unclaim(taskId);
}

指派给别人

@Test
public void testSetAssignee() {//审批人指派给另一个审批人taskService.setAssignee(taskId, userId);
}

候选人组

创建用户

private IdentityService identityService;@Autowired
public void setIdentityService(IdentityService identityService) {this.identityService = identityService;
}@Test
public void testCreateUser() {User user = identityService.newUser("zhangsan");user.setEmail("xxx@163.com");identityService.saveUser(user);
}

创建用户组

@Test
public void testCreateGroup() {Group group = identityService.newGroup("group");identityService.saveGroup(group);
}

用户关联用户组

@Test
public void testMembership() {Group group = identityService.createGroupQuery().groupId("group").singleResult();List<User> userList = identityService.createUserQuery().list();for (User user : userList) {identityService.createMembership(user.getId(), group.getId());}
}

流程图设置用户组

在这里插入图片描述

查询组待办任务

@Test
public void testQueryGroupTask() {List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("group").list();
}

网关

网关是Flowable 工作流引擎中的一个重要组成部分,用于控制流程的执行路径和决策。Flowable支持多种类型的网关,其中最常见的包括排他网关(Exclusive Gateway)、并行网关(Parallel Gateway)等。

排他网关

排他网关,也被称为异或网关(XOR Gateway),是 Flowable 流程模型中的一个重要组件。它的主要功能是根据设定的条件对流程的执行路径进行选择和判断。

当流程执行到排他网关时,会按照所有出口顺序流定义的顺序对它们进行计算,并选择第一个条件计算为 true 的顺序流继续执行。如果没有可选的顺序流(即所有条件都不满足),则会抛出异常。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

并行网关

并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起。其功能是基于进入和外出顺序流的。

当流程执行到并行网关的分支点时,会为每个外出顺序流创建一个并发分支,所有分支并行执行。当所有分支都执行完毕后,流程会汇聚到并行网关的汇聚点,然后继续执行。

与其他网关不同的是,并行网关会忽略条件。
在这里插入图片描述

包含网关

包含网关可以看作是排他网关(Exclusive Gateway)和并行网关(Parallel Gateway)的结合体。它允许根据条件选择性地执行多个路径,而不仅仅是单一路径或并行执行所有路径。

包含网关在需要基于条件选择性执行多个并行路径的场景中非常有用。例如,在审批流程中,可能需要根据不同的审批条件同时触发多个审批人进行审批。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VueUse 基于 Vue 3 Composition API 的高质量 Hooks 库

VueUse 是什么? VueUse 是基于 Vue 3 Composition API 的高质量 Hooks 库。例如获取滚动的距离 VueUse 官网:VueUse | VueUse VueUse 什么使用? 1、通过npm安装 VueUse npm i @vueuse/core 2、搜索需要使用的函数,例如搜索 useScroll 滚动 3、使用useScroll 滚动函数 …

C语言传递指针给函数

C 语言允许您传递指针给函数&#xff0c;只需要简单地声明函数参数为指针类型即可。 下面的实例中&#xff0c;我们传递一个无符号的 long 型指针给函数&#xff0c;并在函数内改变这个值 实例1&#xff1a;获取系统的时间值 能接受指针作为参数的函数&#xff0c;也能接受数…

为什么Pandas是最流行的Python数据分析库?

本文将从Python生态、Pandas历史背景、Pandas核心语法、Pandas学习资源四个方面去聊一聊Pandas&#xff0c;期望能带给大家一点启发。 一、Python生态里的Pandas 五月份TIOBE编程语言排行榜&#xff0c;Python追上Java又回到第二的位置。Python如此受欢迎一方面得益于它崇尚简…

零成本 API 服务搭建,用 GitHub Actions 自动爬取文章?

前言 本着将成本降到最低&#xff0c;我目前做的应用或小程序都是单机的&#xff0c;也就是不用请求接口&#xff0c;只要一上架就没有任何支出。但是写死的数据毕竟有限&#xff0c;应用的内容单一无法紧跟时事热点&#xff0c;每次打开一个样&#xff0c;自然就没有留存。遇…

Redis13-多级缓存

目录 概述 JVM进程缓存 Caffeine 实现进程缓存 Lua语法 初识Lua 变量和循环 Lua的数据类型 声明变量 循环 条件控制、函数 函数 条件控制 实现多级缓存 安装OpenResty OpenResty快速入门 请求参数处理 查询Tomcat 发送http请求的API 封装http工具 CJSON工…

CSS小玩意儿:文字适配背景

一&#xff0c;效果 二&#xff0c;代码 1&#xff0c;搭个框架 添加一张背景图片&#xff0c;在图片中显示一行文字。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" conte…

【Python】高效的Web自动化测试利器—Python+Playwright快速上手自动化实战指南(限时开放)

文章目录 前言一.playwright是什么二.python引入playwright1.安装2.playwright命令行参数3.playwright codegen自动生成代码4.Chrome和Chromium有什么关系&#xff1f; 三.基本概念1. 无头浏览器&#xff08;Headless Browser&#xff09;2.同步和异步模式操作playwright2.1.同…

SQL每日一练-0815

今日SQL题难度&#xff1a;&#x1f31f;☆☆☆☆☆☆☆☆☆ 1、题目要求 计算每个产品类别在每个月的总销售额和总销量。找出每个月销售额最高的产品类别&#xff0c;显示类别名称、销售月份、总销售额和总销量。 2、表和虚拟数据 现有两个表&#xff1a;Products 和…

RockerMQ学习

消息中间件以前常用RabbitMQ和ActiveMQ&#xff0c;由于业务需要&#xff0c;后期业务偏向大数据&#xff0c;现着重学习一下RocketMQ&#xff08;RocketqMQ原理同ctg-mq&#xff09;&#xff0c;后续更新Kafka 一、RocketMQ特性 Kafka特性 &#xff08;高性能分布式&#xff…

day34-nginx常用模块

## 0. 网络面试题 网络面试题: TCP三次握手 TCP四次挥手 DNS解析流程 OSI七层模型 抓包工具 tcpdump RAID级别区别 开机启动流程 如何实现不同的网段之间通信(路由器) ip route add 192.168.1.0 255.255.255.0 下一跳的地址或者接口 探测服务器开启了哪些端口(无法登录服务器…

渗透测试-行业术语

1.肉鸡 肉鸡用来比喻哪些可以随意被我们控制的计算机&#xff0c;可以是普通的个人电脑&#xff0c;也可以是大型服务器或者其他网络设备&#xff0c;我们可以像操作自己的电脑一样操作他们而不被发觉。 2.木马 表面上伪装成了正常程序&#xff0c;但是当程序被运行的时候&a…

配置MySQL主从,配置MySQL主主 +keeplive高可用

在大数据-Hadoop体系中 配置MySQL主主keeplive高可用 注意&#xff1a;这个是我两年前的word文档&#xff0c;可以当作参考文档有个思路参考一下&#xff0c;但是里面可能有些地方有误 另外 :关于一些企业级实战技术可以参考这篇mysql 物理备份 MySQL 全量备份 增量备份 差异…

Linux_vi vim的使用

目录 vi和vim的基本介绍 vi和vim常用的三种模式 案例演示 vim的快捷键 快捷键使用练习 vi和vim的基本介绍 linux系统内会内置vi文本编译器。vim可以简单认为是vi的增强版本。 vi和vim常用的三种模式 有正常模式&#xff0c;移动光标&#xff0c;删除字符等。插入模式可以进…

Flask 线上高并发部署方案实现

目录 1、Flask默认多线程执行 2、使用gevent.pywsgi实现 3、是用uWSGI服务器实现 1、Flask默认多线程执行 前言&#xff1a;在Flask的较早版本中&#xff0c;默认并不支持多线程模式。然而&#xff0c;从Flask 0.9版本开始&#xff0c;引入了多线程模式的支持&#xff0c;并…

【自动驾驶】ROS中的TF坐标变换(一):静态坐标变换

目录 引子ros中的右手坐标系补充&#xff1a;欧拉角及四元数理解旋转平移操作复合操作 运行坐标变换的例子坐标转换 静态坐标变换-发布坐标系信息创建功能包 静态坐标变换-订阅坐标系信息添加cpp订阅者主文件修改cmakelist文件编译报错的解决方案运行程序进行测试 引子 机器人…

【MySQL数据库】单机、集群、分布式的区别

单机、集群和分布式是计算机系统中三种不同的架构模型,它们在资源管理、任务执行和性能优化方面有显著区别。 图片来源 1. 单机(Standalone) 单机指的是单一计算机系统,即所有的计算任务和数据都在一台计算机上处理。单机系统的特点包括: 硬件限制:受限于单台机器的计…

Visual Studio 2022 无法打开源文件atlimage.h

最近在搞tcp socket 通信demo&#xff0c;网上抄了一下源码&#xff08;代码参考&#xff1a;C中的Socket编程使用协议发送图片_快速传输 照片 c-CSDN博客&#xff09;&#xff0c;还没开始编译就提示 无法打开源文件atlimage.h&#xff0c;全局搜了一下没有这个文件&#xff0…

JSON Web Token (JWT): 理解与应用

JWT&#xff08;JSON Web Token&#xff09;是一种开放标准&#xff08;RFC 7519&#xff09;&#xff0c;它定义了一种紧凑且自包含的方式&#xff0c;用于在各方之间以JSON对象的形式安全地传输信息。JWT通常用于身份验证和授权目的&#xff0c;因为它可以使用JSON对象在各方…

Unity开发抖音小游戏广告部分接入

Unity开发抖音小游戏广告部分接入 介绍环境确保开通流量主获取广告位广告部分代码测试如下总结 介绍 最近在使用Unity做抖音小游戏这块的内容&#xff0c;因为要接入广告&#xff0c;所以这里我把我接入广告的部分代码和经验分享一下。 环境确保 根据抖音官方的文档我们是先…

Linux网络编程—socket、bind

一、socket创建套接字 socket是用来创建网络通信或本地通信的套接字&#xff0c;跟文件有关&#xff1a;告诉系统&#xff0c;PCB&#xff08;进程控制块&#xff09;控制的数据应该向哪个套接字写入、或读取&#xff1b;这个套接字是在TCP/IP协议下运行的 #include <sys/t…