经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Rust » 查看文章
49从零开始用Rust编写nginx,我竟然在同一个端口上绑定了多少IP
来源:cnblogs  作者:问蒙服务框架  时间:2024/2/19 9:19:35  对本文有异议

wmproxy

wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 负载均衡, 静态文件服务器,websocket代理,四层TCP/UDP转发,内网穿透等,会将实现过程分享出来,感兴趣的可以一起造个轮子

项目地址

国内: https://gitee.com/tickbh/wmproxy

github: https://github.com/tickbh/wmproxy

设计目标

快速的设置多IP绑定,及IP端口段的支持,方便快速的自定义能力。

IP解析示例

以下是常见的IP解析示例情况,本地ip为192.168.0.100示例:

  • 正常IP解析

    • 127.0.0.1:8869 解析成 ipv4 127.0.0.1 端口 8869,只接受本地来的连接信息
    • 0.0.0.0:8869 解析成 ipv4 0.0.0.0 端口 8869,可接受所有来自ipv4的连接信息
  • :开头的地址,且不包含-

    • :8869 解析成 ipv4 127.0.0.1 端口 8869 及 ipv4 192.168.0.100 端口 8869
  • 包含-的地址

    • :8869-:8871 解析成 ipv4 127.0.0.1 端口 8869 - 8871 三个端口地址 及 ipv4 192.168.0.100 端口 8869 - 8871 三个端口地址,总共6个端口地址
    • 127.0.0.1:8869-:8871 解析成 ipv4 127.0.0.1 端口 8869 - 8871 三个端口地址 总共3个端口地址
    • 127.0.0.1:8869-192.168.0.100:8871 解析成 ipv4 127.0.0.1 端口 8869 - 8871 三个端口地址 总共3个端口地址,忽略后面的地址,只接受端口号
  • 手动多个地址,可以空格或者,做间隔

    • 127.0.0.1:8869 127.0.0.1:8899 192.168.0.100:8899 就相应的解析成三个端口地址

定义类

由于解析出来的地址可能是多个或者个单个,这里用数组来进行表示

  1. #[derive(Debug, Clone)]
  2. pub struct WrapVecAddr(pub Vec<SocketAddr>);

通常序列化会用到FromStr将字符串转化成类
反序列化都会用到Display将类转化成字符串
所以在这里,我们将实现FromStrDisplay

  1. impl FromStr for WrapVecAddr {
  2. type Err = AddrParseError;
  3. fn from_str(s: &str) -> Result<Self, Self::Err> {
  4. // 范围的如:8080-:8090, 表示11端口
  5. if s.contains("-") {
  6. let vals = s
  7. .split(&['-'])
  8. .filter(|s| !s.is_empty())
  9. .collect::<Vec<&str>>();
  10. let start = parse_socker_addr(vals[0])?;
  11. if vals.len() != 2 {
  12. return Ok(WrapVecAddr(start));
  13. } else {
  14. let end = parse_socker_addr(vals[1])?;
  15. let mut results = vec![];
  16. for port in start[0].port()..=end[1].port() {
  17. for idx in &start {
  18. let mut addr = idx.clone();
  19. addr.set_port(port);
  20. results.push(addr);
  21. }
  22. }
  23. return Ok(WrapVecAddr(results));
  24. }
  25. } else {
  26. let vals = s
  27. .split(&[',', ' '])
  28. .filter(|s| !s.is_empty())
  29. .collect::<Vec<&str>>();
  30. let mut results = vec![];
  31. for s in vals {
  32. results.extend(parse_socker_addr(s)?);
  33. }
  34. Ok(WrapVecAddr(results))
  35. }
  36. }
  37. }
  38. impl Display for WrapVecAddr {
  39. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  40. let mut values = vec![];
  41. for a in &self.0 {
  42. values.push(format!("{}", a));
  43. }
  44. f.write_str(&values.join(","))
  45. }
  46. }

这样子后我们将配置加上就可以自动实现序列化及反序列化了

  1. #[serde_as]
  2. #[derive(Debug, Clone, Serialize, Deserialize)]
  3. pub struct ServerConfig {
  4. #[serde_as(as = "DisplayFromStr")]
  5. pub bind_addr: WrapVecAddr,
  6. // ...
  7. }

获取本机地址

通过库local-ip-address获取本地IP地址,再根据缺省IP时添加本地IP地址访问,因为缺省时有可能是需要本地内网进行访问。所以需要补上本地网卡地址。所以我们在解析以:时做了特殊处理:

  1. fn parse_socker_addr(s: &str) -> Result<Vec<SocketAddr>, AddrParseError> {
  2. if s.starts_with(":") {
  3. let port = s.trim_start_matches(':');
  4. let mut results = vec![];
  5. if let Ok(port) = port.parse::<u16>() {
  6. if let Ok(v) = local_ip() {
  7. results.push(SocketAddr::new(v, port));
  8. }
  9. if let Ok(v) = local_ipv6() {
  10. results.push(SocketAddr::new(v, port));
  11. }
  12. results.push(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port));
  13. } else {
  14. results.push(format!("127.0.0.1{s}").parse::<SocketAddr>()?);
  15. }
  16. Ok(results)
  17. } else {
  18. let addr = s.parse::<SocketAddr>()?;
  19. Ok(vec![addr])
  20. }
  21. }

参数获取

以下举例file-server的参数

  1. #[derive(Debug, Clone, Bpaf)]
  2. #[allow(dead_code)]
  3. struct FileServerConfig {
  4. /// 静态文件根目录路径
  5. #[bpaf(short, long, fallback(String::new()))]
  6. pub(crate) root: String,
  7. #[bpaf(
  8. short,
  9. long,
  10. fallback(WrapVecAddr(vec![SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8869)])),
  11. display_fallback
  12. )]
  13. /// 监听地址
  14. pub(crate) listen: WrapVecAddr,
  15. #[bpaf(long)]
  16. /// 监听地址
  17. pub(crate) listen_ssl: Option<WrapVecAddr>,
  18. /// ...
  19. }

如此我们就可以轻松的用SSL监听及普通的监听添加多个端口的支持。

  1. wmproxy file-server --listen :8869-8871

此时我们可以同时监听3个端口均支持文件服务器。此时我们就可以轻松控制多个端口地址。

绑定多个地址

以下是负载均衡中的绑定示例

  1. for v in &value.bind_addr.0 {
  2. if bind_addr_set.contains(&v) {
  3. continue;
  4. }
  5. bind_addr_set.insert(v);
  6. let url = format!("http://{}", v);
  7. log::info!("HTTP服务:{},提供http处理及转发功能。", Style::new().blink().green().apply_to(url));
  8. let listener = Helper::bind(v).await?;
  9. listeners.push(listener);
  10. tlss.push(false);
  11. }
  12. for v in &value.bind_ssl.0 {
  13. if bind_addr_set.contains(&v) {
  14. continue;
  15. }
  16. bind_addr_set.insert(v);
  17. if !is_ssl {
  18. return Err(crate::ProxyError::Extension("配置SSL端口但未配置证书"));
  19. }
  20. let url = format!("https://{}", v);
  21. log::info!("HTTPs服务:{},提供https处理及转发功能。", Style::new().blink().green().apply_to(url));
  22. let listener = Helper::bind(v).await?;
  23. listeners.push(listener);
  24. tlss.push(is_ssl);
  25. }

支持ssl绑定及非ssl绑定同一个location。

其中链接信息使用了console,输出了绿色的,可以点击的url链接。可以方便在启动的时候进行点击。

图中圈圈位置是可以点击跳转成url,方便本地开发环境的时候测试使用。

总结

通过FromStrDisplay的重定义,我们可以支持更强大的自定义的序列化操作,系统绑定端口既认端口号也认绑定IP,所以我们可以对同个端口进行多次绑定。

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

原文链接:https://www.cnblogs.com/wmproxy/p/18020327/wmproxy49

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号