首页 > 其他分享 >41. 干货系列从零用Rust编写负载均衡及代理,websocket与tcp的映射,WS与TCP互转

41. 干货系列从零用Rust编写负载均衡及代理,websocket与tcp的映射,WS与TCP互转

时间:2024-01-16 09:02:20浏览次数:39  
标签:websocket stream tcp 41 ws receiver 互转 let

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服务端

flowchart TD 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(())
}

创建两对发送接收对分别为OwnedMessageVec<u8>来进行双向绑定,并在协程中发起对websocket的连接请求。更多的逻辑请查看源码。

测试demo

示例文件ws_stw,当下监听8082的流量并将其转发到8081websocket服务上,测试借助websocat做测试服务端

  • cargo run --example ws_stw 启动转发监听8082
  • websocat -s 8081 监听8081
  • telnet 127.0.0.1 8082 手动建立8082的端口

成功测试转发

Websocket转Tcp

流程图

以下展示Websocket转Tcp的流程图,通常由浏览器环境中发起(因为浏览器的标准全双工就是websocket)。然后服务器这边由TCP的方案

flowchart TD 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的流量并将其转发到8081websocket服务上,测试借助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 启动转发监听8081
  • websocat ws://127.0.0.1:8081 用websocket的方式连接到8081

成功测试转发

组合方案

当我们现存的网络方案为Tcp到Tcp或者为Websocket到Websocket而我们在中间的传输过程中如想利用DCDN做源地址保护,而他只支持Websocket,此时我们就可以利用数据的转化,将我们的数据包通过DCDN做转发:

flowchart TD A[TCP客户端] -->|连接服务| B[服务节点] B -->|转化成websocket通过加速| C[DCDN全站加速] C -->|连接服务| E[服务节点] E -->|转化成Tcp并串连到服务端| F[TCP服务端]

这样子我们就可以利用基础网络中的CDN或者DCDN等服务,又不用对旧的数据进行修改或者无法修改的程序就比如远程服务通过CDN进行加速等。

小结

协议的自由转化可以帮助我们创建更合适的网络环境,可以让运维更自由的构建系统。利用转化可以用好全站加速DCDN这类的功能,可以更好的保护源站,防止被DDOS攻击。

点击 [关注][在看][点赞] 是对作者最大的支持

标签:websocket,stream,tcp,41,ws,receiver,互转,let
From: https://www.cnblogs.com/wmproxy/p/17966771/wmproxy41_1

相关文章

  • 4412 设备树 qt busybox , ctrl+c 无法终止 程序
    问题: 在系统中,ctrl+c无法终止程序。背景: 软件:迅为网盘设备树镜像。硬件:迅为4412板卡。  网上的截图:   我自己的改动如下;     结果显示: ......
  • tcpdump
    目录简介步骤1:安装tcpdump步骤2:捕获数据包步骤3:过滤和分析数据包步骤4:将捕获结果写入文件步骤5:查看捕获结果常用参数使用场景简介tcpdump是一个命令行实用程序,允许您捕获和分析通过系统的网络流量。它通常用于帮助排除网络问题,以及作为安全工具。以下是使用tcpdump的基本步骤:......
  • 5.HTTP和TCP
    6.1http1.0和http1.1有什么区别。HTTP1.1相较于HTTP1.0增加了长连接、管道。长连接:为解决HTTP/1.0发送一次请求,建立一次TCP,因此HTTP/1.1新增了长连接,减少连接重复创建和断开管道:解决HTTP/1.0在一个TCP连接中每发送一个请求需等待一个响应的问题,HTTP/1.1新增管道,一个TCP中......
  • Kong网关转发TCP协议和WebSocket协议的请求
    一、TCP协议1、修改配置文件,开启流端口vim/etc/kong/kong.confstream_listen=0.0.0.0:9000,0.0.0.0:90012、利用KongA配置tcp和websocket协议上游服务配置 路由配置:Destinations写kong网关的9000端口(只支持ip+port的形式)   二、WebSocket协议上游服务配置......
  • QTcpSocket发送存储QObject的QList
     #include<QTcpSocket>#include<QDataStream>#include<QByteArray>voidsendObjectList(QTcpSocket*socket,constQList<QObject*>&objectList){QByteArrayserializedData;QDataStreamout(&serializedData,QIODev......
  • CF414B - Mashmokh and ACM
    思路dp。dp[i][j]表示第i位填j时的方案数ac代码#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;consti64inf=8e18;typedefpair<int,int>pii;constintmod=1e9+7;constintN=2e3+5;intdp[N][N];vector<int>g[N];voi......
  • TCP之三次握手四次挥手与UDP区别
    目录1TCP三次握手四次挥手1.1数据包说明1.1.1TCP数据包1.1.2UDP数据包1.1.3TCP和UDP差异1.1.4TCP可靠性传输机制1.2三次握手1.2.1三次握手定义1.2.2三次握手问题1.2.2.1问题引入分析1.2.2.2历史连接1.2.2.3同步双方初始序列号1.2.2.4避免资源浪费1.3四次挥手1TCP......
  • 16.TcpDump 与 WireShark 的使用
    协议分析工具 网络监听:TcpDump+WireShark代理Proxy推荐工具:手工测试charles[全平台]、安全测试burpsuite[全平台java]自动化测试:mitmproxy其他代理:fiddler[仅windows]、AnyProxy[全平台]协议客户端工具:curl、postmantcpdump 参数:-x十六进......
  • ORA-01041: internal error: hostdef extension doesn't exist错误侦察
    如果在使用netca工具安装监听时就发生了ORA-01041:internalerror:hostdefextensiondoesn'texist的错误,可能是由于配置或环境设置的问题。以下是一些建议的步骤:检查环境变量:确保ORACLE_HOME和ORACLE_SID等必要的环境变量已经正确设置。在使用netca工具时,确保使用了......
  • 【Powershell】Powershell实现TCP通讯
    为什么要在做这个?PowerShell进行TCP通信可以在许多场景中发挥作用,包括但不限于以下几个方面:网络服务监控和管理:通过PowerShell,你可以编写脚本来检查服务器上的网络服务是否可用,例如检查某个端口是否打开、某个服务是否正在运行等。这对于监控和管理网络服务的健康状态非常有用......