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台服务器平均分担负载,不会因为某一台服务器负载高宕机而影响用户访问网站,负载均衡至少需要三台服务器, 既 ...
随机推荐
- easyEZbaby_app
for循环,这里给它化简255-i+2-98-未知数x需要等于'0'对应的ASCII值48,那么求x的值,x=111-i,而i的值就是从0到14,这样便可以计算出15位的密码 所以写出来的脚本
- 零基础快速上手STM32开发(手把手保姆级教程)
零基础快速上手STM32开发(手把手保姆级教程) 1. 前言 作为一名嵌入式工程师,STM32 是必须要学习的一款单片机,同时这款单片机资料足够多,而且比较简单,非常适合初学者入门. STM32 是一 ...
- 最佳实践-使用Github Actions来构建跨平台容器镜像
公众号「架构成长指南」,专注于生产实践.云原生.分布式系统.大数据技术分享. 前言 最近在写K8s的相关系列文章,因为有涉及到镜像构建,发现在Mac m1的Arm架构下构建的部分镜像,没法在X86架构 ...
- string函数部分解释
```c1. 运算符重载+.+= 连接字符串= 字符串赋值>.>=.<.<= 字符串比较(例如a < b, aa < ab)==.!= 比较字符串<<. ...
- Linux TTY/PTS
转载:https://segmentfault.com/a/1190000009082089 当我们在键盘上敲下一个字母的时候,到底是怎么发送到相应的进程的呢?我们通过ps.who等命令看到的类似tt ...
- .NET8.0 AOT 经验分享 - 专项测试各大 ORM 是否支持
AOT 特点 发布和部署本机 AOT 应用具有以下优势: 最大程度减少磁盘占用空间:使用本机 AOT 发布时,将生成一个可执行文件,其中仅包含支持程序所需的外部依赖项的代码.减小的可执行文件大小可能会 ...
- [ABC309Ex] Simple Path Counting Problem
Problem Statement We have a grid with $N$ rows and $M$ columns. We denote by $(i,j)$ the cell in the ...
- [ABC270Ex] add 1
Problem Statement You are given a tuple of $N$ non-negative integers $A=(A_1,A_2,\ldots,A_N)$ such t ...
- k8s安装Ingress-Nginx
目前,DHorse(https://gitee.com/i512team/dhorse)只支持Ingress-nginx的Ingress实现,下面介绍Ingress-nginx的安装过程. 下载安装文 ...
- # C# 重新认识一下 IEnumerable<T>,IAsyncEnumerable<T> 以及搭配异步可能遇到的问题
C# 重新认识一下 IEnumerable<T>,IAsyncEnumerable<T> 以及搭配异步可能遇到的问题 前言 为啥会想到写这个 为了这碟醋,包了这顿饺子 作为老鸟 ...