41. 干货系列从零用Rust编写负载均衡及代理,websocket与tcp的映射,WS与TCP互转
wmproxy
wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket代理等,会将实现过程分享出来,感兴趣的可以一起造个轮子
项目地址
国内: https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
项目设计目标
针对有一些应用场景需要将TCP转成websocket的,就比如旧的客户端或者旧的服务端比较不合适进行改造,但是又需要借助阿里的全站加速DCDN等这类服务或者其它可能需要特定浏览器协议的情况下,需要进行协议的转化而服务。
Tcp转Websocket
流程图
以下展示Tcp转Websocket的流程图,就纯粹的Tcp客户端在不经过任何源码修改的情况下成功连接websocket服务端
A[tcp客户端] -->|连接服务| B[服务节点]
B -->|服务转化| C[websocket客户端]
C -->|连接服务| D[websocket服务端]
比较适合原生客户端,又不想引入第三方库,又能在需要的时候直接使用websocket来做配合。
源码实现
实现源码在stream_to_ws
/// 将tcp的流量转化成websocket的流量
pub struct StreamToWs<T: AsyncRead + AsyncWrite + Unpin> {
url: Url,
io: T,
}
- 需要传入的参数为原生的tcp,此处tcp是具备异步读异步写功能的虚拟tcp
- 传入连接websocket的url地址,可以连接到websocket的服务端地址
定义回调类:
struct Operate {
/// 将tcp来的数据流转发到websocket
stream_sender: Sender<Vec<u8>>,
/// 从websocket那接收信息
receiver: Option<Receiver<OwnedMessage>>,
}
- stream_sender将数据进行发送到websocket中
- receiver从websocket中获取信息流
核心转发逻辑:
pub async fn copy_bidirectional(self) -> ProtResult<()> {
let (ws_sender, ws_receiver) = channel::<OwnedMessage>(10);
let (stream_sender, stream_receiver) = channel::<Vec<u8>>(10);
let url = self.url;
tokio::spawn(async move {
if let Ok(mut client) = Client::builder().url(url).unwrap().connect().await {
client.set_callback_ws(Box::new(Operate {
stream_sender,
receiver: Some(ws_receiver),
}));
let _e = client.wait_ws_operate().await;
}
});
Self::bind(self.io, ws_sender, stream_receiver).await?;
Ok(())
}
创建两对发送接收对分别为OwnedMessage及Vec<u8>来进行双向绑定,并在协程中发起对websocket的连接请求。更多的逻辑请查看源码。
测试demo
示例文件ws_stw,当下监听8082的流量并将其转发到8081的websocket服务上,测试借助websocat做测试服务端
cargo run --example ws_stw启动转发监听8082websocat -s 8081监听8081telnet 127.0.0.1 8082手动建立8082的端口

成功测试转发
Websocket转Tcp
流程图
以下展示Websocket转Tcp的流程图,通常由浏览器环境中发起(因为浏览器的标准全双工就是websocket)。然后服务器这边由TCP的方案
A[websocket客户端] -->|连接服务| B[服务节点]
B -->|服务转化| C[tcp客户端]
C -->|连接服务| D[tcp服务端]
比较适合原生服务端,又不想引入第三方库,又能兼容TCP及websocket协议,适合在这个做个中间层。
源码实现
实现源码在ws_to_stream
/// 将websocket的流量转化成的tcp流量
pub struct WsToStream<T: AsyncRead + AsyncWrite + Unpin + Send + 'static, A: ToSocketAddrs> {
addr: A,
io: T,
}
- 需要传入的参数为原生的tcp,此处tcp是具备异步读异步写功能的虚拟tcp,其中
'static表示io为一个类,而不是引用 - 传入连接tcp的SocketAddr地址,可以连接到Tcp的服务端地址
定义回调类:
struct Operate {
/// 将tcp来的数据流转发到websocket
stream_sender: Sender<Vec<u8>>,
/// 从websocket那接收信息
receiver: Option<Receiver<OwnedMessage>>,
}
- stream_sender将数据进行发送到websocket中
- receiver从websocket中获取信息流
核心转发逻辑:
pub async fn copy_bidirectional(self) -> ProtResult<()> {
let (ws_sender, ws_receiver) = channel(10);
let (stream_sender, stream_receiver) = channel::<Vec<u8>>(10);
let stream = TcpStream::connect(self.addr).await?;
let io = self.io;
tokio::spawn(async move {
let mut server = Server::new(io, None);
server.set_callback_ws(Box::new(Operate {
stream_sender,
receiver: Some(ws_receiver),
}));
let e = server.incoming().await;
println!("close server ==== addr = {:?} e = {:?}", 0, e);
});
Self::bind(stream, ws_sender, stream_receiver).await?;
Ok(())
}
与tcp转websocket类似,但是此时是将io流量转成Server的处理函数。
测试demo
示例文件ws_wts,当下监听8082的流量并将其转发到8081的websocket服务上,测试借助websocat做测试服务端
新建测试TCP的监听,原样转发的测试代码:
#[tokio::main]
async fn main() -> std::io::Result<()> {
use tokio::{net::TcpListener, io::{AsyncReadExt, AsyncWriteExt}};
let tcp_listener = TcpListener::bind(format!("127.0.0.1:{}", 8082)).await?;
loop {
let mut stream = tcp_listener.accept().await?;
tokio::spawn(async move {
let mut buf = vec![0;20480];
loop {
if let Ok(size) = stream.0.read(&mut buf).await {
println!("receiver = {:?} size = {:?}", &buf[..size], size);
let _ = stream.0.write_all(b"from tcp:").await;
let _ = stream.0.write_all(&buf[..size]).await;
} else {
break;
}
}
});
}
}
cargo run --example tcp监听8082的端口,收到数据原样转发cargo run --example ws_wts启动转发监听8081websocat ws://127.0.0.1:8081用websocket的方式连接到8081

成功测试转发
组合方案
当我们现存的网络方案为Tcp到Tcp或者为Websocket到Websocket而我们在中间的传输过程中如想利用DCDN做源地址保护,而他只支持Websocket,此时我们就可以利用数据的转化,将我们的数据包通过DCDN做转发:
A[TCP客户端] -->|连接服务| B[服务节点]
B -->|转化成websocket通过加速| C[DCDN全站加速]
C -->|连接服务| E[服务节点]
E -->|转化成Tcp并串连到服务端| F[TCP服务端]
这样子我们就可以利用基础网络中的CDN或者DCDN等服务,又不用对旧的数据进行修改或者无法修改的程序就比如远程服务通过CDN进行加速等。
小结
协议的自由转化可以帮助我们创建更合适的网络环境,可以让运维更自由的构建系统。利用转化可以用好全站加速DCDN这类的功能,可以更好的保护源站,防止被DDOS攻击。
点击 [关注],[在看],[点赞] 是对作者最大的支持
41. 干货系列从零用Rust编写负载均衡及代理,websocket与tcp的映射,WS与TCP互转的更多相关文章
- Docker系列-(3) Docker-compose使用与负载均衡
上一篇文章介绍了docker镜像的制作与发布,本文主要介绍实际docker工程部署中经常用到的docker-compose工具,以及docker的网络配置和负载均衡. Docker-compose介绍 ...
- Nginx服务器部署 负载均衡 反向代理
Nginx服务器部署负载均衡反向代理 LVS Nginx HAProxy的优缺点 三种负载均衡器的优缺点说明如下: LVS的优点: 1.抗负载能力强.工作在第4层仅作分发之用,没有流量的产生,这个特点 ...
- [架构]辨析: 高可用 | 集群 | 主从 | 负载均衡 | 反向代理 | 中间件 | 微服务 | 容器 | 云原生 | DevOps | ...
词汇集 灾备 冷备份 双机热备份 异地容灾备份 云备份 灾难演练 磁盘阵列(RAID) 故障切换 心跳监测 高可用 集群 主从复制(Master-Slave) 多集群横向扩容(master-clust ...
- 死磕nginx系列--使用upsync模块实现负载均衡
问题描述 nginx reload是有一定损耗的,如果你使用的是长连接的话,那么当reload nginx时长连接所有的worker进程会进行优雅退出,并当该worker进程上的所有连接都释放时,进程 ...
- hbase源码系列(一)Balancer 负载均衡
看源码很久了,终于开始动手写博客了,为什么是先写负载均衡呢,因为一个室友入职新公司了,然后他们遇到这方面的问题,某些机器的硬盘使用明显比别的机器要多,每次用hadoop做完负载均衡,很快又变回来了. ...
- 架构之Nginx(负载均衡/反向代理)
Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理 服务器 ,也是一个 IMAP/POP3/SMTP 代理 服务器 . Nginx 是由 Igor Sys ...
- nginx负载均衡(反向代理)
6,安装nginx 6.1 依赖库安装 要安装在root根目录里,不要装在虚拟环境里面 yum install gcc-c++ pcre pcre-devel zlib zlib-devel ope ...
- nginx域名转发 负载均衡 反向代理
公司有三台机器在机房,因为IP不够用,肯定要分出来,所以要建立单IP 多域名的反向代理, 就是当请求www.abc.com 跳转到本机, 请求www.bbc.com 跳转到192.168.0.35 机 ...
- Nginx HTTP负载均衡/反向代理的相关参数测试
原文地址:http://www.cnblogs.com/xiaochaohuashengmi/archive/2011/03/15/1984976.html 测试目的 (1)弄清楚HTTP Upstr ...
- nginx 负载均衡 反向代理
nginx 通过方向代理实现负载均衡,负载均衡是大流量网站要做的措施,单从字面上的意思来理解为N台服务器平均分担负载,不会因为某一台服务器负载高宕机而影响用户访问网站,负载均衡至少需要三台服务器, 既 ...
随机推荐
- mediakit 源码 轻微微 学习总结
mediakit 源码 轻微微 学习总结 概要 项目地址:https://github.com/ZLMediaKit/ZLMediaKit 此项目我们把他做为一个流媒体服务器,我们会有srt和rtsp ...
- 打造我的 Windows 开发环境
@charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...
- 哪一个更好?Spring boot还是Node.js
前言 本篇文章有些与众不同,由于我自己手头有些关于这个主题的个人经验,受其启发写出此文.虽然SpringBoot和Node.js服务于很不一样的场景,但是这两个框架共性惊人.其实每种语言都有不计其数的 ...
- 海康单筒红外相机SDK调用方法
目录 配置环境 1.准备文件 2.配置 3.路径 程序 1.错误警告 2.导入头文件: 3.修改SDK 配置环境 1.准备文件 通过VS创建空白项目后,将海康SDK文件夹: CH-HCNetSDKV6 ...
- 1. Linux 软件介绍
重点: rpm -i -e -qi -ql -qf -qa --scripts. yum install remove info list repolist provides. 配置系统源. 搭建私有 ...
- 2020牛客多校第一场B(虚树)
参考博客 #include<cstdio> typedef long long ll; const int N = 2e5 + 50; int n, cnt, top, tot; int ...
- 洛谷P2757 [国家集训队]等差子序列 (hash+线段树)
题目连接 这题只要令 $len=3$看是否符合即可.因为是一个 $1$到 $n$的排列,考虑数列中项,那么对于一个数 $x$,令 $k=\max(n-x, x-1)$,只要存在 $d\in(1,k)$ ...
- Unicode编码解码
一.Unicode概述 Unicode是一种字符编码标准,旨在解决不同字符集之间的兼容性问题.它为全球所有语言提供了一种统一的编码方式,使得各种字符能够在计算机系统中正确显示和处理.Unicode字符 ...
- ELT安装
前言: ETL是将业务系统的数据经过抽取.清洗转换之后加载到数据仓库的过程, 目的是将企业中的分散.零乱.标准不统一的数据整合到一起,为企业的决策提供分析依据, ETL是BI(商业智能)项目重要的一个 ...
- [GDOI22pj2D] 机器人
第四题 机器人 提交文件: robot.cpp 输入文件: robot.in 输出文件: robot.out 时间空间限制: 3 秒, 512 MB 刚上初一的小纯特别喜欢机器人,这周末,她报名了学校 ...