您可知道如何通过`HTTP2`实现TCP的内网穿透???

可能有人很疑惑应用层 转发传输层?,为什么会有这样的需求啊???哈哈技术无所不用其极,由于一些场景下,对于一个服务器存在某一个内部网站中,但是对于这个服务器它没有访问外网的权限,虽然也可以申请端口访问外部指定的ip+端口,但是对于访问服务内部的TCP的时候我们就会发现忘记申请了!这个时候我们又要提交申请,又要等审批,然后开通端口,对于这个步骤不是一般的麻烦,所以我在想是否可以直接利用现有的Http网关的端口进行转发内部的TCP服务?这个时候我询问了我们的老九大佬,由于我之前也做过通过H2实现HTTP内网穿透,可以利用H2将内部网络中的服务映射出来,但是由于底层是基于yarp的一些方法实现,所以并没有考虑过TCP,然后于老九大佬交流深究,决定尝试验证可行性,然后我们的Taibai项目就诞生了,为什么叫Taibai?您仔细看看这个拼音,翻译过来就是太白,确实全称应该叫太白金星,寓意上天遁地无所不能!下面我们介绍一下具体实现逻辑,确实您仔细看会发现实现是真的超级简单的!

创建Core项目用于共用的核心类库

创建项目名Taibai.Core

下面几个方法都是用于操作Stream的类

DelegatingStream.cs

namespace Taibai.Core;/// <summary>
/// 委托流
/// </summary>
public abstract class DelegatingStream : Stream
{/// <summary>/// 获取所包装的流对象/// </summary>protected readonly Stream Inner;/// <summary>/// 委托流/// </summary>/// <param name="inner"></param>public DelegatingStream(Stream inner){this.Inner = inner;}/// <inheritdoc/>public override bool CanRead => Inner.CanRead;/// <inheritdoc/>public override bool CanSeek => Inner.CanSeek;/// <inheritdoc/>public override bool CanWrite => Inner.CanWrite;/// <inheritdoc/>public override long Length => Inner.Length;/// <inheritdoc/>public override bool CanTimeout => Inner.CanTimeout;/// <inheritdoc/>public override int ReadTimeout{get => Inner.ReadTimeout;set => Inner.ReadTimeout = value;}/// <inheritdoc/>public override int WriteTimeout{get => Inner.WriteTimeout;set => Inner.WriteTimeout = value;}/// <inheritdoc/>public override long Position{get => Inner.Position;set => Inner.Position = value;}/// <inheritdoc/>public override void Flush(){Inner.Flush();}/// <inheritdoc/>public override Task FlushAsync(CancellationToken cancellationToken){return Inner.FlushAsync(cancellationToken);}/// <inheritdoc/>public override int Read(byte[] buffer, int offset, int count){return Inner.Read(buffer, offset, count);}/// <inheritdoc/>public override int Read(Span<byte> destination){return Inner.Read(destination);}/// <inheritdoc/>public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken){return Inner.ReadAsync(buffer, offset, count, cancellationToken);}/// <inheritdoc/>public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default){return Inner.ReadAsync(destination, cancellationToken);}/// <inheritdoc/>public override long Seek(long offset, SeekOrigin origin){return Inner.Seek(offset, origin);}/// <inheritdoc/>public override void SetLength(long value){Inner.SetLength(value);}/// <inheritdoc/>public override void Write(byte[] buffer, int offset, int count){Inner.Write(buffer, offset, count);}/// <inheritdoc/>public override void Write(ReadOnlySpan<byte> source){Inner.Write(source);}/// <inheritdoc/>public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken){return Inner.WriteAsync(buffer, offset, count, cancellationToken);}/// <inheritdoc/>public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default){return Inner.WriteAsync(source, cancellationToken);}/// <inheritdoc/>public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state){return TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count), callback, state);}/// <inheritdoc/>public override int EndRead(IAsyncResult asyncResult){return TaskToAsyncResult.End<int>(asyncResult);}/// <inheritdoc/>public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback,object? state){return TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count), callback, state);}/// <inheritdoc/>public override void EndWrite(IAsyncResult asyncResult){TaskToAsyncResult.End(asyncResult);}/// <inheritdoc/>public override int ReadByte(){return Inner.ReadByte();}/// <inheritdoc/>public override void WriteByte(byte value){Inner.WriteByte(value);}/// <inheritdoc/>public sealed override void Close(){base.Close();}
}

SafeWriteStream.cs

public class SafeWriteStream(Stream inner) : DelegatingStream(inner)
{private readonly SemaphoreSlim semaphoreSlim = new(1, 1);public override async ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default){try{await this.semaphoreSlim.WaitAsync(CancellationToken.None);await base.WriteAsync(source, cancellationToken);await this.FlushAsync(cancellationToken);}finally{this.semaphoreSlim.Release();}}public override ValueTask DisposeAsync(){this.semaphoreSlim.Dispose();return this.Inner.DisposeAsync();}protected override void Dispose(bool disposing){this.semaphoreSlim.Dispose();this.Inner.Dispose();}
}

创建服务端

创建一个WebAPI的项目项目名Taibai.Server并且依赖Taibai.Core项目

创建ServerService.cs,这个类是用于管理内网的客户端的,这个一般是部署在内网服务器上,用于将内网的端口映射出来,但是我们的Demo只实现了简单的管理不做端口的管理。

using System.Collections.Concurrent;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Timeouts;
using Taibai.Core;namespace Taibai.Server;public static class ServerService
{private static readonly ConcurrentDictionary<string, (CancellationToken, Stream)> ClusterConnections = new();public static async Task StartAsync(HttpContext context){// 如果不是http2协议,我们不处理, 因为我们只支持http2if (context.Request.Protocol != HttpProtocol.Http2){return;}// 获取queryvar query = context.Request.Query;// 我们需要强制要求name参数var name = query["name"];if (string.IsNullOrEmpty(name)){context.Response.StatusCode = 400;Console.WriteLine("Name is required");return;}Console.WriteLine("Accepted connection from " + name);// 获取http2特性var http2Feature = context.Features.Get<IHttpExtendedConnectFeature>();// 禁用超时context.Features.Get<IHttpRequestTimeoutFeature>()?.DisableTimeout();// 得到双工流var stream = new SafeWriteStream(await http2Feature.AcceptAsync());// 将其添加到集合中,以便我们可以在其他地方使用CreateConnectionChannel(name, context.RequestAborted, stream);// 注册取消连接context.RequestAborted.Register(() =>{// 当取消时,我们需要从集合中删除ClusterConnections.TryRemove(name, out _);});// 由于我们需要保持连接,所以我们需要等待,直到客户端主动断开连接。await Task.Delay(-1, context.RequestAborted);}/// <summary>/// 通过名称获取连接/// </summary>/// <param name="host"></param>/// <returns></returns>public static (CancellationToken, Stream) GetConnectionChannel(string host){return ClusterConnections[host];}/// <summary>/// 注册连接/// </summary>/// <param name="host"></param>/// <param name="cancellationToken"></param>/// <param name="stream"></param>public static void CreateConnectionChannel(string host, CancellationToken cancellationToken, Stream stream){ClusterConnections.GetOrAdd(host,_ => (cancellationToken, stream));}
}

然后再创建ClientMiddleware.cs,并且继承IMiddleware,这个是我们本地使用的客户端链接的时候进入的中间件,再这个中间件会获取query中携带的name去找到指定的Stream,然后会将客户端的Stream和获取的server的Stream进行Copy,在这里他们会将读取的数据写入到对方的流中,这样就实现了双工通信

using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Timeouts;
using Taibai.Core;namespace Taibai.Server;public class ClientMiddleware : IMiddleware
{public async Task InvokeAsync(HttpContext context, RequestDelegate next){// 如果不是http2协议,我们不处理, 因为我们只支持http2if (context.Request.Protocol != HttpProtocol.Http2){return;}var name = context.Request.Query["name"];if (string.IsNullOrEmpty(name)){context.Response.StatusCode = 400;Console.WriteLine("Name is required");return;}Console.WriteLine("Accepted connection from " + name);var http2Feature = context.Features.Get<IHttpExtendedConnectFeature>();context.Features.Get<IHttpRequestTimeoutFeature>()?.DisableTimeout();// 得到双工流var stream = new SafeWriteStream(await http2Feature.AcceptAsync());// 通过name找到指定的server链接,然后进行转发。var (cancellationToken, reader) = ServerService.GetConnectionChannel(name);try{// 注册取消连接cancellationToken.Register(() =>{Console.WriteLine("断开连接");stream.Close();});// 得到客户端的流,然后给我们的SafeWriteStream,然后我们就可以进行转发了var socketStream = new SafeWriteStream(reader);// 在这里他们会将读取的数据写入到对方的流中,这样就实现了双工通信,这个非常简单并且性能也不错。await Task.WhenAll(stream.CopyToAsync(socketStream, context.RequestAborted),socketStream.CopyToAsync(stream, context.RequestAborted));}catch (Exception e){Console.WriteLine("断开连接" + e.Message);throw;}}
}

打开Program.cs

using Taibai.Server;var builder = WebApplication.CreateBuilder(new WebApplicationOptions());builder.Host.ConfigureHostOptions(host => { host.ShutdownTimeout = TimeSpan.FromSeconds(1d); });builder.Services.AddSingleton<ClientMiddleware>();var app = builder.Build();app.Map("/server", app =>
{app.Use(Middleware);static async Task Middleware(HttpContext context, RequestDelegate _){await ServerService.StartAsync(context);}
});app.Map("/client", app => { app.UseMiddleware<ClientMiddleware>(); });app.Run();

在这里我们将server的所有路由都交过ServerService.StartAsync接管,再server会请求这个地址,

/client则给了ClientMiddleware中间件。

创建客户端

上面我们实现了服务端,其实服务端可以完全放置到现有的WebApi项目当中的,而且代码也不是很多。

客户端我们创建一个控制台项目名:Taibai.Client,并且依赖Taibai.Core项目

由于我们的客户端有些特殊,再server中部署的它不需要监听端口,它只需要将服务器的数据转发到指定的一个地址即可,所以我们需要将客户端的server部署的和本地部署的分开实现,再服务器部署的客户端我们命名为MonitorClient.cs

ClientOption.cs用于传递我们的客户端地址配置

public class ClientOption
{/// <summary>/// 服务地址/// </summary>public string ServiceUri { get; set; }}

MonitorClient.cs,作为服务器的转发客户端。

using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using Taibai.Core;namespace Taibai.Client;public class MonitorClient(ClientOption option)
{private string Protocol = "taibai";private readonly HttpMessageInvoker httpClient = new(CreateDefaultHttpHandler(), true);private readonly Socket socket = new(SocketType.Stream, ProtocolType.Tcp);private static SocketsHttpHandler CreateDefaultHttpHandler(){return new SocketsHttpHandler{// 允许多个http2连接EnableMultipleHttp2Connections = true,// 设置连接超时时间ConnectTimeout = TimeSpan.FromSeconds(60),SslOptions = new SslClientAuthenticationOptions{// 由于我们没有证书,所以我们需要设置为trueRemoteCertificateValidationCallback = (_, _, _, _) => true,},};}public async Task TransportAsync(CancellationToken cancellationToken){Console.WriteLine("链接中!");// 由于是测试,我们就目前先写死远程地址await socket.ConnectAsync(new IPEndPoint(IPAddress.Parse("192.168.31.250"), 3389), cancellationToken);Console.WriteLine("连接成功");// 将Socket转换为流var stream = new NetworkStream(socket);try{// 创建服务器的连接,然后返回一个流,这个是H2的流var serverStream = await this.CreateServerConnectionAsync(cancellationToken);Console.WriteLine("链接服务器成功");// 将两个流连接起来,这样我们就可以进行双工通信了。它们会自动进行数据的传输。await Task.WhenAll(stream.CopyToAsync(serverStream, cancellationToken),serverStream.CopyToAsync(stream, cancellationToken));}catch (Exception ex){Console.WriteLine(ex.Message);throw;}}/// <summary>/// 创建服务器的连接/// </summary> /// <param name="cancellationToken"></param>/// <exception cref="OperationCanceledException"></exception>/// <returns></returns>public async Task<SafeWriteStream> CreateServerConnectionAsync(CancellationToken cancellationToken){var stream = await Http20ConnectServerAsync(cancellationToken);return new SafeWriteStream(stream);}/// <summary>/// 创建http2连接/// </summary>/// <param name="cancellationToken"></param>/// <returns></returns>private async Task<Stream> Http20ConnectServerAsync(CancellationToken cancellationToken){var serverUri = new Uri(option.ServiceUri);// 这里我们使用Connect方法,因为我们需要建立一个双工流, 这样我们就可以进行双工通信了。var request = new HttpRequestMessage(HttpMethod.Connect, serverUri);// 如果设置了Connect,那么我们需要设置Protocolrequest.Headers.Protocol = Protocol;// 我们需要设置http2的版本request.Version = HttpVersion.Version20;// 我们需要确保我们的请求是http2的request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;// 设置一下超时时间,这样我们就可以在超时的时候取消连接了。using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(60));using var linkedTokenSource =CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, cancellationToken);// 发送请求,然后等待响应var httpResponse = await this.httpClient.SendAsync(request, linkedTokenSource.Token);// 返回h2的流,用于传输数据return await httpResponse.Content.ReadAsStreamAsync(linkedTokenSource.Token);}
}

创建我们的本地客户端实现类。

Client.cs这个就是在我们本地部署的服务,然后会监听本地的60112的端口,然后会吧这个端口的数据转发到我们的服务器,然后服务器会根据我们使用的name去找到指定的客户端进行交互传输。

using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using Taibai.Core;
using HttpMethod = System.Net.Http.HttpMethod;namespace Taibai.Client;public class Client
{private readonly ClientOption option;private string Protocol = "taibai";private readonly HttpMessageInvoker httpClient;private readonly Socket socket;public Client(ClientOption option){this.option = option;this.httpClient = new HttpMessageInvoker(CreateDefaultHttpHandler(), true);this.socket = new Socket(SocketType.Stream, ProtocolType.Tcp);// 监听本地端口this.socket.Bind(new IPEndPoint(IPAddress.Loopback, 60112));this.socket.Listen(10);}private static SocketsHttpHandler CreateDefaultHttpHandler(){return new SocketsHttpHandler{// 允许多个http2连接EnableMultipleHttp2Connections = true,ConnectTimeout = TimeSpan.FromSeconds(60),ResponseDrainTimeout = TimeSpan.FromSeconds(60),  SslOptions = new SslClientAuthenticationOptions{// 由于我们没有证书,所以我们需要设置为trueRemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,},};}public async Task TransportAsync(CancellationToken cancellationToken){Console.WriteLine("Listening on 60112");// 等待客户端连接var client = await this.socket.AcceptAsync(cancellationToken);Console.WriteLine("Accepted connection from " + client.RemoteEndPoint);try{// 将Socket转换为流var stream = new NetworkStream(client);// 创建服务器的连接,然后返回一个流, 这个是H2的流var serverStream = await this.CreateServerConnectionAsync(cancellationToken);Console.WriteLine("Connected to server");// 将两个流连接起来, 这样我们就可以进行双工通信了. 它们会自动进行数据的传输.await Task.WhenAll(stream.CopyToAsync(serverStream, cancellationToken),serverStream.CopyToAsync(stream, cancellationToken));}catch (Exception e){Console.WriteLine(e);throw;}}/// <summary>/// 创建与服务器的连接/// </summary> /// <param name="cancellationToken"></param>/// <exception cref="OperationCanceledException"></exception>/// <returns></returns>public async Task<SafeWriteStream> CreateServerConnectionAsync(CancellationToken cancellationToken){var stream = await this.Http20ConnectServerAsync(cancellationToken);return new SafeWriteStream(stream);}private async Task<Stream> Http20ConnectServerAsync(CancellationToken cancellationToken){var serverUri = new Uri(option.ServiceUri);// 这里我们使用Connect方法, 因为我们需要建立一个双工流var request = new HttpRequestMessage(HttpMethod.Connect, serverUri);// 由于我们设置了Connect方法, 所以我们需要设置协议,这样服务器才能识别request.Headers.Protocol = Protocol;// 设置http2版本request.Version = HttpVersion.Version20;// 强制使用http2request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(60));using var linkedTokenSource =CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, cancellationToken);// 发送请求,等待服务器验证。var httpResponse = await this.httpClient.SendAsync(request, linkedTokenSource.Token);// 返回一个流return await httpResponse.Content.ReadAsStreamAsync(linkedTokenSource.Token);}
}

然后再Program.cs中,我们封装一个简单的控制台版本。

using Taibai.Client;const string commandTemplate = @"当前是 Taibai 客户端,输入以下命令:- `help` 显示帮助
- `monitor` 使用监控模式,监听本地端口,将流量转发到服务端的指定地址- `monitor=https://localhost:7153/server?name=test`  监听本地端口,将流量转发到服务端指定的客户端名称为 test 的地址
- `client` 使用客户端模式,连接服务端的指定地址,将流量转发到本地端口- `client=https://localhost:7153/client?name=test`  连接服务端指定当前客户端名称为 test,将流量转发到本地端口
- `exit` 退出输入命令:";while (true)
{Console.WriteLine(commandTemplate);var command = Console.ReadLine();if (command?.StartsWith("monitor=") == true){var client = new MonitorClient(new ClientOption(){ServiceUri = command[8..]});await client.TransportAsync(new CancellationToken());}else if (command?.StartsWith("client=") == true){var client = new Client(new ClientOption(){ServiceUri = command[7..]});await client.TransportAsync(new CancellationToken());}else if (command == "help"){Console.WriteLine(commandTemplate);}else if (command == "exit"){Console.WriteLine("Bye!");break;}else{Console.WriteLine("未知命令");}
}

我们默认提供了命令去使用指定的一个模式去链接客户端,

然后我们发布一下Taibai.Client,发布完成以后我们使用ide启动我们的Taibai.Server,请注意我们需要使用HTTPS进行启动的,HTTP是不支持H2的!

然后再客户端中打开俩个控制台面板,一个作为监听的monitor,一个作为client进行链接到我们的服务器中。

然后我们使用远程桌面访问我们的127.0.0.1:60112,然后我们发现链接成功!如果您跟着写代码您会您发您也成功了,哦耶您获得了一个牛逼的技能,来源于微软MVP token的双休大法的传授!

技术交流分享

来自微软MVP token

token | 最有价值专家 (microsoft.com)

技术交流群:737776595

当然如果您需要Demo的代码您可以联系我微信wk28u9123456789

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

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

相关文章

Python数据分析大作业(ARIMA 自回归积分滑动平均模型) 4000+字 图文分析文档 销售价格库存分析+完整python代码

资源地址&#xff1a;Python数据分析大作业 4000字 图文分析文档 销售分析 完整python代码 完整代码分析 ​ 同时销售量后1000的sku品类占比中&#xff08;不畅销产品&#xff09;如上&#xff0c;精品类产品占比第一&#xff0c;达到66.7%&#xff0c;其次是香化类产品&#x…

Python使用设计模式中的建筑模式将数据写入Excel且满足条件内容标红

对于这个任务&#xff0c;适合使用"Builder"设计模式。Builder模式的主要目的是将对象的构建与其表示分离&#xff0c;以便相同的构建过程可以创建不同的表示。在这个情况下&#xff0c;我们需要一个构建器来逐行构建Excel表格&#xff0c;并根据给定的数据添加相应的…

JAVA系列 小白入门参考资料 继承

目录 1. 为什么需要继承 2. 继承的概念 3. 继承的语法 4. 父类成员访问 4.1 子类中访问父类的成员变量 1. 子类和父类不存在同名成员变量 2. 子类和父类成员变量同名 4.2 子类中访问父类的成员方法 1. 成员方法名字不同 2. 成员方法名字相同 ​5. super关键字 …

golang beego结合wire依赖注入及自动路由

1 安装wire 1.1 通过命令直接安装 go install github.com/google/wire/cmd/wirelatest 1.2 通过go get方式安装 go get github.com/google/wire/cmd/wire进入目录编译 cd C:\Users\leell\go\pkg\mod\github.com\google\wirev0.6.0\cmd\wire go build 然后将wire.exe移动到…

万兆以太网MAC设计(12)万兆UDP协议栈上板与主机网卡通信

文章目录 一、设置IP以及MAC二、上板效果2.1、板卡与主机数据回环测试2.2、板卡满带宽发送数据 一、设置IP以及MAC 顶层模块设置源MAC地址 module XC7Z100_Top#(parameter P_SRC_MAC 48h01_02_03_04_05_06,parameter P_DST_MAC 48hff_ff_ff_ff_ff_ff )(input …

【Docker】docker compose服务编排

docker compose 简介 Dockerfile模板文件可以定义一个单独的应用容器&#xff0c;如果需要定义多个容器就需要服务编排。 docker swarm&#xff08;管理跨节点&#xff09; Dockerfile可以让用户管理一个单独的应用容器&#xff1b;而Compose则允许用户在一个模板&#xff08…

某赛通电子文档安全管理系统 多处 SQL注入漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

微信小程序与web-view网页进行通信的尝试

首先&#xff0c;微信小程序向web-view传递数据一般通过地址栏传参的形式&#xff08;给src赋值或者修改hash&#xff09;&#xff0c;这样一般就已经能够满足实际开发需求了&#xff0c;所以这里主要探讨web-view向微信小程序传参。下面&#xff0c;我们从官方文档入手&#x…

C语言:项目实践(贪吃蛇)

前言&#xff1a; 相信大家都玩过贪吃蛇这款游戏吧&#xff0c;贪吃蛇是久负盛名的游戏&#xff0c;它也和俄罗斯方块&#xff0c;扫雷等游戏位列经典游戏的行列&#xff0c;那贪吃蛇到底是怎么实现的呢&#xff1f; 今天&#xff0c;我就用C语言带着大家一起来实现一下这款游戏…

Linux第十五章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

Three.js杂记(十五)—— 汽车展览(下)

在上一篇文章Three.js杂记&#xff08;十四&#xff09;—— 汽车展览上 - 掘金 (juejin.cn)中主要对切换相机不同位置和鼠标拖拽移动相机焦点做了简单的应用。 那么现在聊聊该如何实现汽车模型自带的三种动画展示了&#xff0c;实际上可以是两种汽车前后盖打开和汽车4车门打开…

网络安全之密码学技术

文章目录 网络信息安全的概念数据加密|解密概念密码学概论密码学分类古典密码学现代密码学 现代密码学的相关概念对称加密算法对称加密算法—DES对称加密算法—3DES对称加密算法—AES对称加密算法—IDEA 非对称加密算法非对称加密算法—RSA非对称加密算法—ElGamal非对称加密算…

Centos7安装K8S集群环境

一、系统设置 1、关闭swap 临时关闭swap swapoff -a 永久关闭 注释掉 /etc/fstab 中的下面配置 #/dev/mapper/centos-swap swap swap defaults 0 0 2、 关闭SELinux kubelet不支持SELinux, 这里需要将SELinux设置为permissive模式 setenforce 0 sed -i s/^SELINUXenfo…

Flutter 从 Assets 中读取 JSON 文件:指南 [2024]

在本教程中&#xff0c;我们将探讨如何从 Flutter 项目中的 asset 中读取 JSON 文件。您将找到详细的解释、实际示例和最佳实践&#xff0c;使您的 JSON 文件处理顺利高效。那么&#xff0c;让我们深入了解 Flutter 和 JSON 的世界吧&#xff01; 从 asset 中读取 JSON 文件 …

pkpmbs 建设工程质量监督系统 Ajax_operaFile.aspx 文件读取漏洞复现

0x01 产品简介 pkpmbs 建设工程质量监督系统是湖南建研信息技术股份有限公司一个与工程质量检测管理系统相结合的,B/S架构的检测信息监管系统。 0x02 漏洞概述 pkpmbs 建设工程质量监督系统 Ajax_operaFile.aspx接口处存在文件读取漏洞,未经身份认证的攻击者可以利用漏洞读…

使用 GitHub Actions 实现项目的持续集成(CI)

目录 什么是 GitHub Actions 基础概念 Workflow 文件 Workflow 语法 实例&#xff1a;编译 OpenWrt 什么是 GitHub Actions GitHub Actions 是 GitHub 推出的持续集成&#xff08;Continuous Integration&#xff0c;简称 CI&#xff09;服务它允许你创建自定义工作流&am…

SpringBoot 自定义 HandlerMethodArgumentResolver 搞定xml泛型参数解析

文章目录 介绍一、解析简单 xml 数据案例引入 Jackson 的 xml 支持定义 Message 对象&MessageHeader 对象定义 Controller 方法调用结果 二、解析带泛型的 XML 数据案例2.1 直接给 Message 加上泛型 T2.2 无法直接解析泛型参数了 三、自定义 MVC 的参数解析器实现泛型参数解…

全志ARM-蜂鸣器

sh操作准备&#xff1a; 1.使Tab键的缩进和批量对齐为4格 在/etc/vim/vimrc 中添加一项配置 set tabstop 4; 也可以再加一行 set nu显示代码的行数 vim的设置&#xff0c;修改/etc/vim/vimrc文件&#xff0c;需要用超级用户权限 /etc/vim/vimrc set shiftwidth4 设置批量…

助力企业部署国产云原生数据库 XSKY星辰天合与云猿生完成产品互兼容认证

近日&#xff0c;北京星辰天合科技股份有限公司&#xff08;简称&#xff1a;XSKY 星辰天合&#xff09;与杭州云猿生数据有限公司&#xff08;简称“云猿生”&#xff09;完成了产品互兼容认证&#xff0c;星辰天合企业级分布式统一数据平台 XEDP 与云猿生的开源数据库管控平台…

CJSON工具类

4.4.3.CJSON工具类 OpenResty提供了一个cjson的模块用来处理JSON的序列化和反序列化。 官方地址&#xff1a; https://github.com/openresty/lua-cjson/ 1&#xff09;引入cjson模块&#xff1a; local cjson require "cjson"2&#xff09;序列化&#xff1a; …