- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
wasmtime是一个可以运行WebAssembly代码的运行时环境.
WebAssembly是一种可移植的二进制指令集格式,其本身与平台无关,类似于Java的class文件字节码.
WebAssembly本来的设计初衷是想让浏览器可以运行C语言这种编译型语言的代码。通常我们的C语言代码会使用gcc或clang等编译器直接编译链接成与平台相关的二进制可执行文件,这种与平台相关的二进制文件浏览器是无法直接运行的。如果想让浏览器运行C语言代码,就需要使用可将C语言编译成WebAssembly指令的编译器,编译好的代码是wasm格式。然后就可以使用各种wasm运行时来执行wasm代码,这就类似于JVM虚拟机执行class文件.
由于指令集和运行时环境本身与web场景并不绑定,因此随着后来的发展,WebAssembly指令集出现了可以脱离浏览器的独立运行时环境,WebAssembly的用途也变得更加广泛.
相比于浏览器的运行时,wasmtime是一个独立运行时环境,它可以脱离Web环境来执行wasm代码。它本身提供了命令行工具和API两种方式来执行wasm代码。本文主要介绍如何使用API方式来运行wasm代码.
wasmtime-cli包含 wasmtime 命令,可以让我们直接在shell中运行 wasm 格式的代码。我们这里安装wasmtime主要是为了测试方便.
在shell中执行如下命令 。
curl https://wasmtime.dev/install.sh -sSf | bash
wasmtime的可执行文件会被安装在 ${HOME}/.wasmtime 目录下 。
运行以上命令后会在 ${HOME}/.bashrc 或 ${HOME}/.bash_profile 文件中帮我们添加以下环境变量 。
export WASMTIME_HOME="${HOME}/.wasmtime"
export PATH="$WASMTIME_HOME/bin:$PATH"
如果希望所有用户(包括root)可以使用 wasmtime 命令,可以将以上环境变量设置到 /etc/profile.d 中,我们可以在该目录下创建 wasmtime.sh 文件,并添加一下代码 。
export WASMTIME_HOME=/home/<xxx>/.wasmtime # 将xxx替换成自己的home目录
export PATH="$WASMTIME_HOME/bin:$PATH"
可以使用如下命令直接运行 wasm 文件 。
wasmtime hello.wasm
如果想在代码中加载 wasm 文件并运行其中的代码,我们需要为我们使用的语言安装wasmtime库。注意这里的wasmtime库是为了让我们从代码中能够加载wasm文件并在wasmtime运行时中运行。wasmtime并不是wasm编译器,不能将C++或Rust代码编译成wasm文件,如果我们想将其他语言编译成wasm代码,需要下载各个语言自己的wasm编译器,具体安装方式在本文第3节.
目前wasmtime支持的语言有:
我们这里以Rust和C++为例介绍如何安装wasmtime库 。
在Rust中使用wasmtime库非常简单,我们只需要在 Cargo.toml 配置文件中添加如下依赖 。
[dependencies]
wasmtime = "12.0.2"
wasmtime的C++库需要我们引入 wasmtime-cpp 这个项目,wasmtime-cpp依赖wasmtime的C API,因此需要先安装C API.
可以在 wasmtime的release 中找到后缀为 -c-api 的包,比如我们安装的平台是 x86_64-linux ,那么我们可以下载如下文件 。
wget https://github.com/bytecodealliance/wasmtime/releases/download/v12.0.2/wasmtime-v12.0.2-x86_64-linux-c-api.tar.xz
解压以上文件并将其移动到 /usr/local 目录下 。
tar -xvf wasmtime-v12.0.2-x86_64-linux-c-api.tar.xz
sudo mv ./wasmtime-v12.0.2-x86_64-linux-c-api /usr/local/wasmtime
在 /etc/profile.d/wasmtime.sh 中添加环境变量 。
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/wasmtime/lib
export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/wasmtime/lib
export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/local/wasmtime/include
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/wasmtime/include
下载wasmtime-cpp项目的 include/wasmtime.hh 文件,将其放到 wasmtime.h 所在的目录下,按照我们的安装步骤,需要放置到 /usr/local/wasmtime/include 目录下 。
如此就可以在我们的C++项目中引入wasmtime库了 。
#include <wasmtime.hh>
Rust语言的编译器目前其实是一个LLVM的编译前端,它将代码编译成LLVM IR,然后经过LLVM编译成相应的目标平台代码.
因此我们并不需要替换Rust语言本身的编译器,只需要在编译时设置目标平台为wasm即可。我们在安装rust时,通常只会安装本机平台支持的目标,因此我们需要先安装wasm目标.
# 列出所有可安装的target列表
rustup target list
使用上面的命令后可以看到很多可以安装的target列表,其中已经安装的target后面会有 (installed) 标示。注意到其中有3个wasm相关的target.
wasm32-unknown-emscripten
wasm32-unknown-unknown
wasm32-wasi
wasm32-unknown-emscripten
:这个target是为了在Emscripten工具链下编译Wasm。Emscripten是一个将C/C++代码编译为Wasm和JavaScript的工具链。使用这个target,你可以在浏览器环境中运行编译后的Wasm代码。wasm32-unknown-unknown
:这个target是为了在没有任何操作系统支持的情况下运行WebAssembly代码而设计的。这种情况下,WebAssembly代码将运行在一个“裸机”环境中,没有任何操作系统提供的支持。因此,如果你需要在裸机环境中运行WebAssembly代码,那么使用这个target是一个不错的选择。wasm32-wasi
:这个target是为了在WebAssembly System Interface (WASI)上运行WebAssembly代码而设计的。WASI是一个标准接口,它提供了一些操作系统级别的功能,如文件系统和网络访问等。因此,如果你需要在WebAssembly中访问这些操作系统级别的功能,那么使用这个target是一个不错的选择。
由于我们不需要在Web环境中运行Rust代码,因此我们选择安装 wasm32-unknown-unknown 和 wasm32-wasi 两个目标。运行以下两条指令,将这两个目标平台加入到当前使用的Rust工具链中.
rustup target add wasm32-unknown-unknown
rustup target add wasm32-wasi
当我们需要将一个Rust项目编译成wasm时,可以选择执行如下的两种编译命令 。
# 在项目根目录执行
cargo build --target wasm32-unknown-unknown # 将在target/wasm32-unknown-unknown目录中生成build中间结果和wasm文件
# 或者执行
cargo build --target wasm32-wasi # 将在target/wasm32-wasi目录中生成build中间结果和wasm文件
目前,要将C++项目编译成WebAssembly,最常用的工具链是 emscripten 。emscripten支持将C,C++或任何使用了LLVM的语言编译成浏览器,Node.js或wasm运行时可以运行的代码.
Emscripten is a complete compiler toolchain to WebAssembly, using LLVM, with a special focus on speed, size, and the Web platform. 。
WebAssembly目前支持两种标准API:
- Web APIs
- WASI APIs
Emscripten对JavaScript API做了重构,将其包装在与WASI接口一样的API中,然后Emscripten在编译代码时,将尽可能的使用WASI APIs,以此来避免不必要的API差异。因此Emscripten编译出来的wasm文件大部分时候可以同时运行在Web和非Web环境中.
使用如下命令下载 emsdk 。
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
使用如下命令安装最新的工具 。
git pull
./emsdk install latest
./emsdk activate latest
如果临时将emsdk的工具目录加入环境变量,可以运行 。
source ./emsdk_env.sh
或者可以在 /etc/profile.d 目录中创建 emsdk.sh 文件,并加入如下环境变量的配置,需要将 <emsdk_installed_dir> 替换为 emsdk 所在的目录.
export PATH=$PATH:<emsdk_installed_dir>/emsdk:<emsdk_installed_dir>/emsdk/node/16.20.0_64bit/bin:<emsdk_installed_dir>/emsdk/upstream/emscripten
export EMSDK=<emsdk_installed_dir>/emsdk
export EMSDK_NODE=<emsdk_installed_dir>/emsdk/node/16.20.0_64bit/bin/node
使用如下命令测试是否安装成功,如果输出下面的信息,说明我们已经可以正常使用emscripten的工具链.
> emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.45 (ef3e4e3b044de98e1811546e0bc605c65d3412f4)
clang version 18.0.0 (https://github.com/llvm/llvm-project d1e685df45dc5944b43d2547d0138cd4a3ee4efe)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: <emsdk_installed_dir>/emsdk/upstream/bin
由于我们不使用Web运行时,下面将只介绍将C或C++代码编译成独立wasm二进制文件的使用方法.
emcc -O3 hello.cpp -o hello.wasm
当我们将输出目标的后缀名指定为 wasm 时,编译器会自动帮我们设置如下连接选项,上面的命令与下面的命令时等价的 。
emcc -O3 hello.cpp -o hello.wasm -s STANDALONE_WASM
这样编译出来的结果不会包含 js 文件,只会包含一个可被 wasmtime 运行的 wasm 文件.
更常用的方式通常是将整个C++项目编译成wasm,因此我们需要将工具链与cmake结合来构建整个项目.
假设我们有一个cmake项目有如下项目结构 。
hello_project
|-hello.cpp
|-CMakeLists.txt
其中 hello.cpp 中有如下代码 。
#include <stdio.h>
int main() {
printf("hello, world!\n");
return 0;
}
CMakeLists.txt 应该按照下面的方式进行改写 。
cmake_minimum_required(VERSION 3.26)
project(hello_project)
add_definitions(-std=c++17)
set(CMAKE_CXX_STANDARD 17)
if (DEFINED EMSCRIPTEN)
add_executable(hello hello.cpp)
set(CMAKE_EXECUTABLE_SUFFIX ".wasm")
set_target_properties(foo PROPERTIES COMPILE_FLAGS "-Os")
set_target_properties(foo PROPERTIES LINK_FLAGS "-Os -s WASM=1 -s STANDALONE_WASM")
else()
add_executable(hello hello.cpp)
endif ()
以上 CMakeLists.txt 表示,当我们使用 emscripten 工具链进行编译时,将输出 .wasm 文件,且添加对应的编译和连接选项。当我们使用其他工具链编译时,将直接输出对应平台的可执行文件.
按照上面的方式写好 CMakeLists.txt 后,需要使用以下命令来执行编译的过程 。
# 在项目根目录下
mkdir build
cd build
# 执行emcmake命令会帮我们自动配置cmake中指定的工具链为emscripten的工具链,这样就确保了使用的编译工具为emcc或em++,同时使用的标准库更改为emscripten提供的标准库
emcmake cmake ..
# 再执行make进行编译,编译后可以发现build目录中生成了hello.wasm文件
make
使用wasmtime-cli运行 hello.wasm 文件 。
> wasmtime hello.wasm
hello, world!
需要测试Rust代码被编译成wasm,C++代码被编译成wasm,在wasmtime中正确运行。其中C++代码可以调用Rust代码中的函数,然后外部可以调用C++代码中的函数.
add
函数,做两个整数的加法并返回结果,可以被外部调用。需要编译成wasm。 foo
函数,调用Rust中的 add
函数并返回结果。需要编译成wasm。 foo
函数,看是否能获取正确的结果。 创建一个项目叫做 demo-rust-wasmtime 。
cargo new demo-rust-wasmtime --lib
创建好的项目结构如下 。
demo-rust-wasmtime
├── Cargo.lock
├── Cargo.toml
└── src
└── lib.rs
首先需要在 Cargo.toml 中配置生成的库为 cdylib ,这表示按照C语言的FFI来生成动态库,要想不同语言之间能够互相调用对方的函数,通常需要将不同的语言按照相同的FFI来进行编译,确保函数调用的方式是相同的。这里同时我们将Rust项目的名称修改为 calc .
[package]
name = "calc"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[dependencies]
在 lib.rs 中实现我们需要的 add 函数 。
#[no_mangle]
pub extern "C" fn add(left: i32, right: i32) -> i32 {
left + right
}
这里有两个地方需要注意:
#[no_mangle]
会通知Rust编译器,其后面的函数编译时名字不要进行混淆,确保使用 add
这个名称进行链接时能找到正确的函数。 extern "C"
表示编译器需要确保函数在编译时使用与C语言相同的调用约定(ABI),从而使得函数可以与C语言代码无缝地进行交互,当然如果我们将不同的语言都遵照C语言的ABI进行编译,那么它们之间就可以互相调用。 C语言的调用约定规定了函数参数的传递方式、返回值的处理方式以及堆栈的清理方式.
这样就定义好了Rust项目中可以让外部使用的 add 方法.
我们使用如下命令对项目进行编译 。
cargo build --target wasm32-unknown-unknown
# 或
cargo build --target wasm32-wasi
这里两种target都可以使用,因为我们的项目中并没有使用任何系统的API,所以通常使用第一种target即可.
编译后可以在 target/wasm-xxx/debug/ 目录下看到生成的 calc.wasm 文件.
可以使用wasmtime-cli实验一下是否能够调用 add 方法:
> wasmtime calc.wasm --invoke add 101 202
warning: using `--invoke` with a function that takes arguments is experimental and may break in the future
warning: using `--invoke` with a function that returns values is experimental and may break in the future
303
可以看到已经正确输出了结果,说明这个Rust项目已经被正确编译成了wasm.
创建一个项目叫做 demo-cpp-wasmtime ,使用cmake作为构建工具,其目录结构如下 。
demo-cpp-wasmtime
├── CMakeLists.txt
├── toolbox.cpp
└── toolbox.h
正如第3节讲到的,我们需要使用emscripten工具链代替gcc工具链来将这个C++项目编译成wasm.
因此我们需要按照如下方式配置 CMakeLists.txt 文件 。
cmake_minimum_required(VERSION 3.26)
project(demo_cpp_wasmtime)
add_definitions(-std=c++17)
set(CMAKE_CXX_STANDARD 17)
if (DEFINED EMSCRIPTEN)
add_executable(toolbox toolbox.cpp toolbox.h)
set(CMAKE_EXECUTABLE_SUFFIX ".wasm")
set_target_properties(toolbox PROPERTIES COMPILE_FLAGS "-Os -s SIDE_MODULE=1")
set_target_properties(toolbox PROPERTIES LINK_FLAGS "-Os -s WASM=1 -s SIDE_MODULE=1 -s STANDALONE_WASM --no-entry")
else()
add_library(toolbox toolbox.cpp)
endif ()
这里有几点需要注意的 。
在使用emscripten时,我们使用 add_executable 指定编译目标为可执行文件,这是因为wasm本身是可执行的二进制代码,在没有特殊配置时,编译后的wasm代码中会生成一个 _start 函数,这个函数就是运行时执行wasm代码的入口。这里如果我们将 add_executable 替换成 add_library ,则使用emscripten编译后只会生成 libtoolbox.a 库文件,而不会生成wasm代码.
针对emscripten编译工具链,我们配置了编译参数和链接参数 。
-Os 表示开启编译优化 。
-s SIDE_MODULE=1 表示将 toolbox 编译成 module ,这样生成的wasm就类似动态链接库,可以让wasmtime在运行时动态链接这份wasm代码.
emscripten支持将代码编译成两种不同的 module 。
- Main modules :系统库会被链接进去
- Side modules :系统库不会被链接进去
通常一个完整的项目只能有一个 Main module ,这个 Main module 可以链接多个 Side module 。
这里的编译选项 SIDE_MODULE 可以被设置为 1 或者 2 ,设置成 2 则编译器会优化掉大量未被使用的代码或未被标记为 EMSCRIPTEN_KEEPALIVE 的代码,设置成 1 则会保留所有代码.
-s WASM=1 表示只输出 wasm 文件,设置为 0 表示只输出 js 代码,设置成 2 表示两种代码都输出 。
-s STANDALONE_WASM 表示编译的wasm是不依赖web环境而运行的 。
--no-entry 编译生成的wasm代码通常需要有一个入口函数,也就是C++中需要有 main 函数,然而我们这里 toolbox.cpp 中将只有一个 foo 函数,因此我们需要使用这个链接参数来表示我们不需要入口函数.
toolbox.h 头文件如下 。
#pragma once
extern "C" {
int foo(int right);
}
类似Rust,这里我们声明了一个函数 foo ,并使用 extern "C" 表示这个foo函数需要按照C语言ABI进行编译.
接下来是 toolbox.cpp 的实现 。
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#else
#define EMSCRIPTEN_KEEPALIVE
#define EM_IMPORT(NAME)
#endif
extern "C" {
EM_IMPORT(add) int add(int a, int b);
}
extern "C" {
EMSCRIPTEN_KEEPALIVE int foo(int right) {
return add(1, right);
}
}
下面解释一下代码中的几个宏的作用:
#ifdef __EMSCRIPTEN__ :当我们使用emscripten工具链编译这个项目时, __EMSCRIPTEN__ 会被自动定义 。
EMSCRIPTEN_KEEPALIVE 和 EM_IMPORT(NAME) :
这是头文件 emscripten.h 中定义的宏,查看源码可以发现 。
#define EMSCRIPTEN_KEEPALIVE __attribute__((used))
#ifdef __wasm__
#define EM_IMPORT(NAME) __attribute__((import_module("env"), import_name(#NAME)))
#else
#define EM_IMPORT(NAME)
#endif
__attribute__((used)) 的作用是告诉编译器,即使该变量或函数没有被直接使用,也不要将其优化掉。这在一些特殊的情况下很有用,例如当你想要确保某个变量或函数在编译后的可执行文件中存在,即使它在代码中没有被显式调用或使用。这样就确保了我们的 foo 函数不会被编译器优化掉 。
__attribute__((import_module("env"), import_name(#NAME))) 是用于WebAssembly的特殊属性,用于指定导入函数所属的模块和导入函数的名称。在WebAssembly中,可以从外部导入函数,这些函数通常由宿主环境(如浏览器或wasmtime)提供。当你使用 __attribute__((import_module("env"), import_name(#NAME))) 属性时,它告诉WebAssembly运行时,该函数属于名为"env"的模块,并且其导入名称为 #NAME .
使用 EM_IMPORT(add) 宏告诉编译器,这里声明的add方法其具体实现来自于其他模块,具体就是来自于 env 模块中的 add 函数。因此这里声明的 add 方法其实可以起任意的名字,只要签名与 env 模块中的 add 方法相同即可.
使用如下命令进行编译 。
# 在项目根目录下
mkdir build
cd build
emcmake cmake ..
make
编译后在 build 目录下会生成 toolbox.wasm 二进制文件.
我们可以使用 wasm2wat 命令将编译好的 wasm 二进制文件转换成可读的 wat 文件来看一下生成的代码的结构 。
如果没有安装 wasm2wat 命令可以使用一下命令来安装 。
sudo apt install wabt
执行 wasm2wat toolbox.wasm -o toolbox.wat 命令后,可以打开 toolbox.wat 文件查看其结构如下 。
(module
(type (;0;) (func (param i32 i32) (result i32)))
(type (;1;) (func))
(type (;2;) (func (param i32) (result i32)))
(import "env" "add" (func (;0;) (type 0)))
(func (;1;) (type 1))
(func (;2;) (type 2) (param i32) (result i32)
i32.const 1
local.get 0
call 0)
(export "__wasm_call_ctors" (func 1))
(export "__wasm_apply_data_relocs" (func 1))
(export "foo" (func 2)))
可以看出,代码中 import "env" "add" 表示 add 函数来自 env module 的 add 函数。同时 export "foo" 表示 toolbox.wasm 对外暴露了 foo 函数.
wasmtime项目可以使用wasmtime支持的各种语言实现,这里我们以C++为例,看看如何将前面两个项目生成的 .wasm 文件调用起来.
创建一个项目叫做 demo-run ,使用cmake进行项目构建,其目录结构如下 。
demo-run
├── CMakeLists.txt
└── main.cpp
wasmtime项目可以使用gcc工具链进行编译,因此它的 CMakeLists.txt 可以正常进行配置 。
cmake_minimum_required(VERSION 3.26)
project(demo_run)
set(CMAKE_CXX_STANDARD 17)
add_executable(demo_run main.cpp)
target_link_libraries(demo_run PUBLIC wasmtime)
因为我们需要在代码中使用 wasmtime 的库,因此这里需要使用 target_link_libraries(demo_run PUBLIC wasmtime) 将 wasmtime 链接进来。这也就要求必须先按照第2节中的安装方式配置好 wasmtime 的环境变量.
具体wasmtime提供的每个API的用法在这里不多做赘述,具体可以参考wasmtime官方文档和官方提供的examples 。
#include <iostream>
#include <wasmtime.hh>
#include <fstream>
using namespace wasmtime;
std::vector<unsigned char> readFile(const char *name) {
std::ifstream watFile(name, std::ios::binary);
std::vector<unsigned char> arr;
char byte;
while (watFile.get(byte)) {
arr.push_back(byte);
}
return arr;
}
int main() {
std::cout << "Compiling module" << std::endl;
Engine engine;
// 加载calc.wasm成为module
auto calcByteArr = readFile("calc.wasm");
Span<uint8_t> calcSpan(calcByteArr.data(), calcByteArr.size());
auto calcModule = Module::compile(engine, calcSpan).unwrap();
// 加载toolbox.wasm成为module
auto toolboxByteArr = readFile("toolbox.wasm");
Span<uint8_t> toolboxSpan(toolboxByteArr.data(), toolboxByteArr.size());
auto toolboxModule = Module::compile(engine, toolboxSpan).unwrap();
std::cout << "Initializing..." << std::endl;
Store store(engine);
store.context().set_wasi(WasiConfig()).unwrap();
std::cout << "Linking..." << std::endl;
Linker linker(engine);
linker.define_wasi().unwrap();
// 链接器初始化calc module,实例化成具体的Instance
auto calcInst = linker.instantiate(store, calcModule).unwrap();
// 将上一步的calcInst中的所有export的对象定义到env module名下
linker.define_instance(store, "env", calcInst).unwrap();
// 链接器初始化toolbox module,实例化成具体的Instance
auto toolboxInst = linker.instantiate(store, toolboxModule).unwrap();
// 获取toolboxInst中的foo方法
auto func = std::get<Func>(toolboxInst.get(store, "foo").value());
// 调用foo方法,传入参数7,
auto fooRes = func.call(store, {7}).unwrap();
// 打印结果 FooResult: 8
std::cout << "FooResult: " << fooRes[0].i32() << std::endl;
return 0;
}
就像注释中写的那样,我们将 calc.wasm 中 export 的方法 add 添加到了名称为 env 的 module 下,这样上一步中C++编译成的 .wasm 代码就可以链接到这个 add 方法.
mkdir build
cd build
cmake ..
make
执行编译后会生成可执行文件 demo_run ,由于代码还要依赖两个 .wasm 文件,因此我们这里 手动将前面两个项目生成的 .wasm 文件拷贝到 demo_run 可执行文件的同级目录下 .
运行生成的 demo_run 可执行文件后可得如下输出 。
> ./demo_run
Compiling module
Initializing...
Linking...
FooResult: 8
以上就实现了C++和Rust通过wasmtime实现相互调用的过程.
最后此篇关于WebAssembly实践指南——C++和Rust通过wasmtime实现相互调用实例的文章就讲到这里了,如果你想了解更多关于WebAssembly实践指南——C++和Rust通过wasmtime实现相互调用实例的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
这个问题已经有答案了: Resolve build errors due to circular dependency amongst classes (12 个回答) 已关闭 3 个月前。 如何允许
让 2 个 Swing 部件做同样的工作是很常见的。例如,我们可以在工具栏中使用一个 button 作为“保存”按钮,而 JMenuItem (文件保存...)也可以做同样的事情。 我的问题是: 有没
我使用 fragment 已经有一段时间了,但我经常遇到一个让我烦恼的问题。 fragment 有时会相互吸引。现在,我设法为此隔离了一个用例,它是这样的: Add fragment A(也使用 ad
我正在使用具有相互 SSL 安全性的 WCF 服务,我想检查一下我对何时使用什么证书的理解。 这是正确的吗? 客户端将客户端公共(public)证书交给服务器 服务器将服务器公共(public)证书交
假设您有一个相互 SSL 服务,除了 SSL 之外,它还有应用程序身份验证。因此,客户端提供证书(以及服务器),但客户端请求(例如 REST 请求)还包含后端应用程序服务器用于验证的用户名/密码。 就
有人让 Android uiautomator 可以同时在多个设备上运行,但做不同的事情吗? 我的意思是,我希望我的测试同时启动设备和应用程序,然后设备 A 执行设备 B 必须使用react的操作。这
我目前正在尝试在客户端和服务器之间实现双向 TLS 身份验证。我遇到了一个 SSL 错误,它的描述性不强。 StackOverflow 也没有太多与之相关的问题,因为大多数时候它是互联网上的单向 TL
这里是新手。我正在做我的第一个元素,我想为不同的人(普通人、 worker 、农民等)提供 slider ,但我不知道如何放置多个 slider 以及如何让它们全部工作。我从 W3schools 获取
我创建了一张翻转卡片,但卡片内的所有 div 似乎都浮在彼此之上。我希望 div 彼此相邻。 我看了很多问题,但似乎找不到答案。我尝试了多种显示:内联;职位:相对;向左飘浮;清除:两者;但我似乎无法让
我正在使用此控件来安排时间。我有一个单选按钮列表,然后是多个内容 Pane 。根据内容,我想在正确的控件中淡入淡出。但出于某种原因,在 div 上放置一个 float 并设置 z-index 并不能使
有什么方法可以解密双向 SSL(客户端和服务器,两种方式)? 我找到了这个链接:https://www.wireshark.org/lists/wireshark-users/201001/msg00
我正在开发一个 Web 应用程序,安全性是我们在此应用程序中的主要关注点之一。我正在查看不同的 API 安全方法(在 OWASP 中提到),无法理解相互 SSL 身份验证和基于 token 的身份验证
我正在尝试使用分配给 kube-dns 服务的集群 IP 从 dnstools pod ping kube-dns 服务。 ping 请求超时。在同一个 dnstools pod 中,我尝试使用暴露的
过去几天我一直在研究这个问题,但我一无所获。 场景是: 现场的 iOS 应用程序将调用我的 REST 服务 (.NET)。我的 REST 服务将使用相互 SSL 握手调用 Apache Web 服务。
我正在尝试向 java swing 应用程序添加 3 个 JSlider,以便三个 slider 的总值(value)总和为 100。每个 slider 都是一个概率, slider A 是将值添加到
我们正在使用 java 客户端(openJDK 1.8.0) 调用需要相互身份验证的 api。为此,我们使用 Java 标准 JKS 文件作为 keystore 和信任库(包含信任证书和身份证书/私钥
有人告诉我使用双向身份验证连接到客户的服务器。服务器身份验证工作顺利,但我们在获取客户端身份验证方面遇到了巨大的麻烦。让我试着解释一下我们的麻烦。 前段时间我公司在 GeoTrust 购买了一个证书,
正在试用 PAW 并且非常喜欢它。我唯一无法正常工作的是使用 HTTPS 相互身份验证。我需要与之交互的一些 API 需要相互验证的 https。 如何告诉 PAW 使用证书进行身份验证?该证书已经在
我们有一个在 Jboss EAP 5.1 中部署并使用 Spring 2.5 已经运行了一年多的 CXF webservice 我们现有的客户证书管理策略如下: 对于非 PROD,证书名为“NAME-
我正在创建一个将调用 API 的 Windows 服务。对于这个过程,我正在尝试建立相互(双向)SSL 身份验证。因为我是新手。我尝试实现一个简单的客户端和服务器项目,它们将相互进行身份验证。 我已经
我是一名优秀的程序员,十分优秀!