- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
gite: https://gitee.com/tickbh/wmproxy 。
github: https://github.com/tickbh/wmproxy 。
内网:也叫做局域网,通常指单一的网络环境。例如你家里的路由器网络、网吧、公司网络、学校网络。网络大小不定,内网中的主机可以互联互通,但是越出这个局域网访问,就无法访问该网络中的主机.
公网:就是互联网,其实也可以看做一个扩大版的内网,比如叫城际网,省域网,国网。有单独的公网IP,任何其它地址可以访问网络的可以直接访问该IP,从而实现服务.
https://www.baidu.com/s?wd=ip
查询地址 描述:线上项目有问题或者有某些新功能,必须进行Debug进行调试和测试。 特点:本地调试、网速要求低、需要HTTP或者HTTPS协议。 需求:必须本地,必须HTTP[S]网址.
描述:如外出进行工作,或者本地有大量的私有数据(敏感不适合上云),但是自己必须得进行访问,如git服务或者照片服务等 特点:需要远程能随时随地的访问,访问内容不确定,但是需要能提供 需求:要相对比较稳定的线路,但是带宽相对要求较低 。
描述:把自己的电脑做服务器,有时候云上的主机配置相对较高点的一个月费用极高,所以需要本地做私有服务器,或者把自己当做一台训练机 特点:对稳定性要求不用太高的,可以提供相应的服务 。
内网IP无法直接被访问,所以此时需求 。
- 内网服务器
- 公网服务器,有公网IP
此时网络如下,如此外部用户就能访问到内网服务器的数据,此时内网穿透客户端及服务端是保持长连接以方便进行推送,本质上是长链接在转发数据而实现穿透功能 。
wmproxy一款简单易用的内网穿透工具,简单示例如下:
客户端配置 client.yaml 。
# 连接服务端地址
server: 127.0.0.1:8091
# 连接服务端是否加密
ts: true
# 内网映射配置的数组
mappings:
#将localhost的域名转发到本地的127.0.0.1:8080
- name: web
mode: http
local_addr: 127.0.0.1:8080
domain: localhost
#将tcp的流量无条件转到127.0.0.1:8080
- name: tcp
mode: tcp
local_addr: 127.0.0.1:8080
domain:
启动客户端 。
wmproxy -c config/client.yaml
服务端配置 server.yaml 。
#绑定的ip地址
bind_addr: 127.0.0.1:8091
#代理支持的功能,1为http,2为https,4为socks5
flag: 7
#内网映射http绑定地址
map_http_bind: 127.0.0.1:8001
#内网映射tcp绑定地址
map_tcp_bind: 127.0.0.1:8002
#内网映射https绑定地址
map_https_bind: 127.0.0.1:8003
#内网映射的公钥证书,为空则是默认证书
map_cert:
#内网映射的私钥证书,为空则是默认证书
map_key:
#接收客户端是为是加密客户端
tc: true
#当前服务模式,server为服务端,client为客户端
mode: server
启动服务端 。
wmproxy -c config/server.yaml
在本地的8080端口上启动了一个简单的http文件服务器 。
http-server .
此时,8001的端口是http内网穿透通过服务端映射到客户端,并指向到8080端口,此时若访问 http://127.0.0.1:8001 则会显示 。
http映射是根据域名做映射此时我们的域名是127.0.0.1,所以直接返回404无法访问 此时若访问 http://localhost:8001 ,结果如下 。
我们就可以判定我们的内网转发成功了.
tcp就是在该端口上的流量无条件转发到另一个端口上,此时我们可以预测tcp映射与域名无关,我们在8002上转发到了8080上,此时我们访问 http://127.0.0.1:8002 和 http://localhost:8002 都可以得到一样的结果 。
此时tcp转发成功 。
因为TLS连接与协议无关,只要把普通的TCP转成TLS,剩下的均和普通连接一样处理即可,那么,此时我们只需要处理TCP和HTTP的请求转发即可.
在程序启动的时候看我们是否配置了相应的http/https/tcp的内网穿透转发,如果有我们对相应的端口做监听,此时如果我们是https转发,要配置相应的证书,将会对 TcpStream 升级为 TlsStream<TcpStream> 。
let http_listener = if let Some(ls) = &self.option.map_http_bind {
Some(TcpListener::bind(ls).await?)
} else {
None
};
let mut https_listener = if let Some(ls) = &self.option.map_https_bind {
Some(TcpListener::bind(ls).await?)
} else {
None
};
let map_accept = if https_listener.is_some() {
let map_accept = self.option.get_map_tls_accept().await.ok();
if map_accept.is_none() {
let _ = https_listener.take();
}
map_accept
} else {
None
};
let tcp_listener = if let Some(ls) = &self.option.map_tcp_bind {
Some(TcpListener::bind(ls).await?)
} else {
None
};
转发相关代码,主要在两个类里,分别为 trans/http.rs 和 trans/tcp.rs 。
在 http 里面需要预处理相关的头文件消息, 。
X-Forwarded-For
添加IP信息,从而使内网可以知道访问的IP来源 Host
,重写Host信息,让内网端如果配置负载均衡可以正确的定位到位置 Server
,重写Server信息,让内网可以明确知道这个服务端的类型 以下为部分代码,后续将进行比较正规的HTTP服务,以适应HTTP2 。
pub async fn process<T>(self, mut inbound: T) -> Result<(), ProxyError<T>>
where
T: AsyncRead + AsyncWrite + Unpin,
{
let mut request;
let host_name;
let mut buffer = BinaryMut::new();
loop {
// 省略读信息
request = webparse::Request::new();
// 通过该方法解析标头是否合法, 若是partial(部分)则继续读数据
// 若解析失败, 则表示非http协议能处理, 则抛出错误
// 此处clone为浅拷贝,不确定是否一定能解析成功,不能影响偏移
match request.parse_buffer(&mut buffer.clone()) {
Ok(_) => match request.get_host() {
Some(host) => {
host_name = host;
break;
}
None => {
if !request.is_partial() {
Self::err_server_status(inbound, 503).await?;
return Err(ProxyError::UnknownHost);
}
}
},
// 数据不完整,还未解析完,等待传输
Err(WebError::Http(HttpError::Partial)) => {
continue;
}
Err(e) => {
Self::err_server_status(inbound, 503).await?;
return Err(ProxyError::from(e));
}
}
}
// 取得相关的host数据,对内网的映射端做匹配,如果未匹配到返回错误,表示不支持
{
let mut is_find = false;
let read = self.mappings.read().await;
for v in &*read {
if v.domain == host_name {
is_find = true;
}
}
if !is_find {
Self::not_match_err_status(inbound, "no found".to_string()).await?;
return Ok(());
}
}
// 有新的内网映射消息到达,通知客户端建立对内网指向的连接进行双向绑定,后续做正规的http服务以支持拓展
let create = ProtCreate::new(self.sock_map, Some(host_name));
let (stream_sender, stream_receiver) = channel::<ProtFrame>(10);
let _ = self.sender_work.send((create, stream_sender)).await;
// 创建传输端进行绑定
let mut trans = TransStream::new(inbound, self.sock_map, self.sender, stream_receiver);
trans.reader_mut().put_slice(buffer.chunk());
trans.copy_wait().await?;
// let _ = copy_bidirectional(&mut inbound, &mut outbound).await?;
Ok(())
}
tcp处理相对比较简单,因为我们无法确定协议里是哪个类型的源码,所以对我们来说,就是单纯的把接收的数据完全转发到新的端口里。以下是部分源码 。
pub async fn process<T>(self, inbound: T) -> Result<(), ProxyError<T>>
where
T: AsyncRead + AsyncWrite + Unpin,
{
// 寻找是否有匹配的tcp转发协议,如果有,则进行转发,如果没有则丢弃数据
{
let mut is_find = false;
let read = self.mappings.read().await;
for v in &*read {
if v.mode == "tcp" {
is_find = true;
}
}
if !is_find {
log::warn!("not found tcp client trans");
return Ok(());
}
}
// 通知客户端数据进行连接的建立,客户端的tcp配置只能存在有且只有一个,要不然无法确定转发源
let create = ProtCreate::new(self.sock_map, None);
let (stream_sender, stream_receiver) = channel::<ProtFrame>(10);
let _ = self.sender_work.send((create, stream_sender)).await;
let trans = TransStream::new(inbound, self.sock_map, self.sender, stream_receiver);
trans.copy_wait().await?;
Ok(())
}
到此部分细节已基本调通,后续将优化http的处理相关,以方便支持http的头信息重写和tcp的错误信息将写入正确的日志,以方便进行定位.
最后此篇关于7.用Rust手把手编写一个wmproxy(代理,内网穿透等),HTTP及TCP内网穿透原理及运行篇的文章就讲到这里了,如果你想了解更多关于7.用Rust手把手编写一个wmproxy(代理,内网穿透等),HTTP及TCP内网穿透原理及运行篇的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我是 ZMQ 的新手。我发现 ZMQ 套接字实现比 winsock 简单得多。但我怀疑 “使用 ZMQ TCP 套接字创建的客户端可以与传统的 TCP 服务器通信吗?” 换句话说我的 ZMQ 客户端可
我想使用 TCP 协议(protocol) 将数据发送到 Logstash。为了发送数据,我正在使用 Node-RED。一个简单的配置如下所示: 在 Logstash 文件夹中,我创建了一个名为 no
当我尝试更改窗口缩放选项时,作为 root,我可以通过在 /proc/sys/net/中执行 net.ipv4.tcp_mem=16777000 来更改值。如果我必须更改这 100 个系统,那将需要大
明天做一些练习题,这道做不出来 TCP 服务器连接 TCP 客户端进行通信所需的最小套接字端口数是多少? 肯定只有两个吧?一个用于服务器,一个用于客户端,但这似乎是显而易见的。我的伙伴们认为 TCP
考虑一个存在一个服务器和多个客户端的场景。每个客户端创建 TCP 连接以与服务器交互。 TCP alive的三种用法: 服务器端保活:服务器发送 TCP 保活以确保客户端处于事件状态。如果客户端死了,
TCP TAHOE 和 TCP RENO 有什么区别。 我想知道的是关于 3-dup-ack 和超时的行为? SST 发生了什么变化? 谢谢! 最佳答案 TCP Tahoe 和 Reno 是处理 TC
大家早上好。我一直在阅读(其中大部分在堆栈溢出中)关于如何进行安全密码身份验证(散列 n 次,使用盐等)但我怀疑我将如何在我的 TCP 客户端中实际实现它-服务器架构。 我已经实现并测试了我需要的方法
在遍历 RFC793 时,我开始知道应该以这种方式选择初始序列号段重叠被阻止。 有人能解释一下如果发生重叠,重复段将如何影响 TCP? 最佳答案 不同的操作系统有不同的行为。参见 http://ins
你能举例说明一下tcp/ip中nagle算法的概念吗? 最佳答案 我认为Wikipedia在开头的段落中做得很好。 Nagle's document, Congestion Control in IP
似乎最大 TCP 接收窗口大小为 1GB(使用缩放时)。因此,仍然可以用一个连接填充 100Gb 管道的最大 RTT 是 40ms(因为 2 * 40E-3 * 100E9/8 = 1GB)。这会将这
考虑在两个 TCP 端点之间建立的 TCP 连接,其中一个调用: 关闭():此处,不允许进一步读取或写入。 关机(fd,SHUT_WR):这会将全双工连接转换为单工连接,其中调用 SHUT_WR 的端
我是在 Lua 中编写解析器的新手,我有两个简短的问题。我有一个包含 TCP 选项的数据包,如 MSS、TCP SACK、时间戳、NOP、窗口比例、未知。我基本上是在尝试剖析 TCP 选项字段中的未知
TCP 是否不负责通过在传输过程中发生丢失等情况时采取任何可能必要的措施来确保通过网络完整地发送流? 它做的不对吗? 为什么更高的应用层协议(protocol)及其应用程序仍然执行校验和? 最佳答案
考虑使用 10 Mbps 链路的单个 TCP (Reno) 连接。假设此链路不缓冲数据并且接收方的接收缓冲区比拥塞窗口大得多。设每个 TCP 段的大小为 1500 字节,发送方和接收方之间连接的双向传
考虑这样一个场景,有client-a和server-b。 server-b 禁用了 TCP keepalive。 server-b 没有任何应用程序逻辑来检查 TCP 连接是否打开。 client-a
我正在尝试用 Rust 编写回显服务器。 use std::net::{TcpStream, TcpListener}; use std::io::prelude::*; fn main() {
听说对于TCP连接,服务器会监听一个端口,并使用另一个端口发送数据。 例如,Web 服务器监听端口 80。每当客户端连接到它时,该服务器将使用另一个端口(比如 9999)向客户端发送数据(Web 内容
我试图了解带有标记 PSH 和标记 URG 的 TCP 段之间的区别。我阅读了 RFC,但仍然无法理解,其中一个在将数据发送到进程之前缓冲数据而另一个没有吗? 最佳答案 它们是两种截然不同的机制。 #
有第三方服务公开 TCP 服务器,我的 Node 服务器(TCP 客户端)应使用 tls Node 模块与其建立 TCP 连接。作为 TCP 客户端, Node 服务器同时也是 HTTP 服务器,它应
我正在发送一些 TCP SYN 数据包以获得 TCP RST 的返回。为了识别每个探测器,我在 TCP 序列字段中包含一个计数器。我注意到以下几点: 当SYN probe中的sequence numb
我是一名优秀的程序员,十分优秀!