【Linux】【网络】不同子网下的客户端和服务器通信

server/2025/2/23 3:28:09/

【Linux】【网络】不同子网下的客户端和服务器通信

前两天在进行socket()网络编程并进行测试时,发现在不同wifi下两个电脑无法进行连接,大概去查找了如何解决 看到可以使用
frp 这个快速反向代理实现。

  • frp 可让您将位于 NAT 或防火墙后面的本地服务器暴露到互联网。它目前支持TCP和UDP,以及HTTP和HTTPS协议,允许通过域名将请求转发到内部服务。
  • github官网:https://github.com/fatedier/frp?tab=readme-ov-file#tcp-stream-multiplexing

先提一些基础概念,为什么在不同wifi下无法进行通信:

不同wifi下无法进行通信原因

1公网 IP与 内网 IP

IP地址是网络中的唯一标识符,用于区分网络中不同设备的位置

  • 公网 IP地址由互联网服务提供商(ISP)分配给用户,可以唯一地标识全球设备,以实现互联网上的通信。
  • 内网 IP地址旨在用于组织内部网络,不会在全球互联网上路由,从而确保内部网络的安全。
1.1公网 IP

定义:公网 IP 是由互联网服务提供商(ISP)分配给你设备的 IP 地址,它是唯一的,且在互联网上是可以直接访问的。
作用:公网 IP 用于设备之间的通信,允许设备通过互联网相互访问。例如,当你访问一个网站时,浏览器会使用你的公网 IP 发送请求。

  1. 可以直接在互联网上被访问。
  2. 每个公网 IP 是唯一的,全球只有一个设备可以使用一个特定的公网 IP 地址。
  3. 公网 IP 的分配是有限的,IP 地址资源稀缺。
1.2 内网 IP

定义:内网 IP 是在局域网(LAN)内部使用的 IP 地址,它们不能在互联网上直接访问。内网设备可以通过 NAT(网络地址转换)技术与公网通信,但公网设备无法直接访问内网设备。

作用:内网 IP 用于局域网内设备之间的通信,通常由路由器通过 DHCP(动态主机配置协议)分配。

  1. 只能在局域网内使用,无法直接访问互联网。
  2. 由于内网 IP 地址的重复性,多个局域网可以使用相同的内网 IP 地址。
  3. 内网 IP地址范围是预定义的,常见的内网 IP 地址段有:
    Class A:10.0.0.0 ~ 10.255.255.255
    Class B:172.16.0.0 ~ 172.31.255.255
    Class C:192.168.0.0 ~ 192.168.255.255
1.3区分公网 IP 和内网 IP

可以通过以下几个步骤来分辨一个 IP 地址是公网 IP 还是内网 IP:

  • a. 查看 IP 地址是否在内网地址范围内
    内网 IP 地址有固定的地址段,如果某个 IP 地址属于以下任意一个范围,那么它就是内网 IP:
    10.0.0.0 ~ 10.255.255.255 (Class A)
    172.16.0.0 ~ 172.31.255.255 (Class B)
    192.168.0.0 ~ 192.168.255.255 (Class C)

  • b. 检查是否可通过外部访问
    如果你能够直接访问某个 IP 地址并且该地址位于互联网,那么它是公网 IP。
    内网 IP 地址只能在同一局域网内使用,无法通过互联网直接访问。

解决方案

拥有一台有公网IP的云服务器作为中转站,将局域网下的电脑将数据信息发送给中转的服务器,然后这个中转的服务器将收到的数据转给另外一台电脑,这样就可以实现两台电脑之间的互相通信。

原因:我们可以实现在局域网下的通信而不能在不是同一局域网下的通信是因为,不同的私网之间是无法通信的,我们使用的192.168.x.x都是私网,但是所有的私网却都可以和公网ip直接通信的。所以。想要在两个私网之间通信的话,我们就需要多一个步骤,也就是需要一个公网的IP作为中转站。

在这里插入图片描述

1云服务器

我的是阿里云服务器 当然只要是云服务器都可以
在这里插入图片描述

2 frp

2.1 云服务器上的配置

github下载frp网站:https://github.com/fatedier/frp/releases?page=5
因为我参考的文档使用的是0.33.0版本 我也就用了这个

ps:需要注意你的云服务器架构是什么

  • ARM架构下arm版本
  • x86_64架构选择amd 版本 我是这个版本的
    在这里插入图片描述
    可以使用命令下载:
wget https://github.com/fatedier/frp/releases/download/v0.33.0/frp_0.33.0_linux_amd64.tar.gz

然后解压缩:

tar xzvf frp_0.33.0_linux_amd64.tar.gz

将解压后的文件重命名:

mv frp_0.33.0_linux_amd64 frp       

查看文件内容:

cd frp
ls

frp 默认给出两个服务端配置文件,一个是简版的 frps.ini,另一个是完整版本 frps_full.ini。我们这里通过简版的 frps.ini配置,快速的搭建起一个 frp服务端。

查看frps.ini的配置:

cat frps.ini
#输出
[common]
bind_port = 7000 

由于默认配置中监听的是 7000 端口,但是用户可根据自己实际情况修改,我这里就没有修改了

启动frp服务端:

./frps -c ./frps.ini

输出:

root@iZ2vc4j4f5dy4g5cif3dnxZ:~/frp_set/frp# ./frps -c ./frps.ini
2025/02/20 14:13:02 [I] [service.go:178] frps tcp listen on 0.0.0.0:7000
2025/02/20 14:13:02 [I] [root.go:209] start frps success
2025/02/20 14:13:09 [I] [service.go:432] [e17730b293054812] client login info: ip [223.104.11.108:15309] version [0.33.0] hostname [] os [linux] arch [amd64]

在这里插入图片描述

2.2 云服务器上打开对应端口

可参考这个:https://blog.csdn.net/aa390481978/article/details/96837655
在这里插入图片描述
在这里插入图片描述

服务器配置

同样先下载,再解压,然后修改配置文件,之后启动
下载:

wget https://github.com/fatedier/frp/releases/download/v0.33.0/frp_0.33.0_linux_amd64.tar.gz

解压缩:

tar xzvf frp_0.33.0_linux_amd64.tar.gz

将解压后的文件重命名:

mv frp_0.33.0_linux_amd64 frp       

修改查看frpc.ini的配置:

[common]
server_addr = your_server_ip  # 公网服务器 IP 地址
server_port = 7000           # FRP 服务器端口[ssh]
type = tcp
local_ip = 192.168.x.x       # 局域网电脑的 IP 地址
local_port = 23              # 局域网中要转发的服务端口(例如 SSH)
remote_port = 6001           # 在公网服务器上暴露的端口

server_addr是你服务器的公网ip
在这里插入图片描述

启动frp服务端:

./frpc -c ./frpc.ini

输出:

025/02/20 14:17:46 [I] [service.go:282] [c78168a348dd212e] login to server success, get run id [c78168a348dd212e], server udp port [0]
2025/02/20 14:17:46 [I] [proxy_manager.go:144] [c78168a348dd212e] proxy added: [ssh]
2025/02/20 14:17:46 [I] [control.go:179] [c78168a348dd212e] [ssh] start proxy success
客户端配置

同样先下载,再解压,然后修改配置文件,之后启动
下载:

wget https://github.com/fatedier/frp/releases/download/v0.33.0/frp_0.33.0_linux_amd64.tar.gz

解压缩:

tar xzvf frp_0.33.0_linux_amd64.tar.gz

将解压后的文件重命名:

mv frp_0.33.0_linux_amd64 frp       

修改查看frpc.ini的配置:

[common]
server_addr = your_server_ip  # 公网服务器 IP 地址
server_port = 7000           # FRP 服务器端口[ssh]
type = tcp
local_ip = 192.168.x.x       # 局域网电脑的 IP 地址
local_port = 22              # 局域网中要转发的服务端口(例如 SSH)
remote_port = 6000           # 在公网服务器上暴露的端口

server_addr是你服务器的公网ip
在这里插入图片描述
启动frp服务端:

./frpc -c ./frpc.ini

输出:
在这里插入图片描述

注意事项
  • 1 客户端和服务器的local_port = 22,remote_port = 6000 要设置为不一致的
  • 2 客户端和服务器的名称也要设置为不一致的 ssh和ssh1
  • 3 云服务器打开对应端口
    ps:这边1,2设置为一致的是会导致 端口号 名称被占用的问题 但是按理说不应该 这边后续可以再试试
[W] [control.go:177] [0a1ee9193b4f3b0e] [ssh] start error: proxy name [ssh] is already in use
[W] [control.go:177] [0b9fe4453b7ae8ea] [ssh1] start error: port already used

3测试连接

配置完成后 测试客户端和服务器能否连接成功
下面是我的测试代码:

3.1 服务器
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>int main() {const char* host_ip = "192.168.xx.xx";  // A 局域网服务器的内网 IP 地址int host_port = 23;  // 局域网服务端口// 创建 socketint server_socket = socket(AF_INET, SOCK_STREAM, 0);if (server_socket < 0) {std::cerr << "Socket creation failed!" << std::endl;return -1;}sockaddr_in server_address;server_address.sin_family = AF_INET;server_address.sin_port = htons(host_port);server_address.sin_addr.s_addr = inet_addr(host_ip);// 绑定并监听if (bind(server_socket, (sockaddr*)&server_address, sizeof(server_address)) < 0) {std::cerr << "Binding failed!" << std::endl;return -1;}if (listen(server_socket, 5) < 0) {std::cerr << "Listening failed!" << std::endl;return -1;}std::cout << "Server A is waiting for connections..." << std::endl;// 接受连接sockaddr_in client_address;socklen_t client_len = sizeof(client_address);int client_socket = accept(server_socket, (sockaddr*)&client_address, &client_len);if (client_socket < 0) {std::cerr << "Connection acceptance failed!" << std::endl;return -1;}std::cout << "Connection established with " << inet_ntoa(client_address.sin_addr) << std::endl;char buffer[1024];int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);  // 接收数据if (bytes_received > 0) {buffer[bytes_received] = '\0';std::cout << "Received from client: " << buffer << std::endl;}const char* response = "Hello, Client B!";send(client_socket, response, strlen(response), 0);  // 发送响应// 关闭连接close(client_socket);close(server_socket);return 0;
}
3.2 客户端
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>int main() {const char* server_ip = "xx.xx.xx.xx";  // 云服务器的公网 IPint server_port = 6001;  // FRP 映射的端口// 创建 socketint client_socket = socket(AF_INET, SOCK_STREAM, 0);if (client_socket < 0) {std::cerr << "Socket creation failed!" << std::endl;return -1;}sockaddr_in server_address;server_address.sin_family = AF_INET;server_address.sin_port = htons(server_port);server_address.sin_addr.s_addr = inet_addr(server_ip);// 连接到服务器if (connect(client_socket, (sockaddr*)&server_address, sizeof(server_address)) < 0) {std::cerr << "Connection failed!" << std::endl;return -1;}const char* message = "Hello, Server A!";send(client_socket, message, strlen(message), 0);  // 发送数据char buffer[1024];int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);  // 接收数据if (bytes_received > 0) {buffer[bytes_received] = '\0';std::cout << "Received from server: " << buffer << std::endl;}// 关闭连接close(client_socket);return 0;
}
连接结果

在这里插入图片描述
在这里插入图片描述

注意事项
  • 服务器绑定的端口是23 小于1024 要切换到管理员状态 否则会绑定失败
    在这里插入图片描述

参考文章:
https://blog.csdn.net/weixin_44917390/article/details/106685219
https://blog.csdn.net/weixin_51354739/article/details/144422320
https://blog.csdn.net/qq_34623639/article/details/140506034

下个文章说一下frp具体是如何实现能够将客户端的连接准确转发给服务器


http://www.ppmy.cn/server/170021.html

相关文章

Ubuntu 的RabbitMQ安装

目录 1.安装Erlang 查看erlang版本 退出命令 2. 安装 RabbitMQ 3.确认安装结果 4.安装RabbitMQ管理界面 5.启动服务并访问 1.启动服务 2.查看服务状态 3.通过IP:port 访问界面 4.添加管理员用户 a&#xff09;添加用户名&#xff1a;admin&#xff0c;密码&#xff1…

Javascript使用Sodium库实现 aead_xchacha20poly1305_ietf加密解密,以及与后端的密文交互

Node.js环境安装 sodium-native (其他库可能会出现加密解密失败&#xff0c;如果要使用不一样的库&#xff0c;请自行验证) npm install sodium-native 示例代码&#xff0c;使用的是 sodium-native v4.3.2 (其他版本可能会有变化&#xff0c;如果要使用&#xff0c;请自行验…

Python 学习之旅:高级阶段(十一)数据库操作 Redis

在 Python 编程的高级阶段,数据库操作是一项重要的技能,而 Redis 作为一款高性能的非关系型数据库,在现代应用开发中扮演着关键角色。它不仅能显著提升应用的性能,还能解决许多复杂的业务问题。接下来,让我们一同深入探索如何使用 Python 操作 Redis 数据库。 一、Redis …

选择免费VPN?了解这些关键因素,确保你的上网安全与隐私!

在互联网高度普及的今天&#xff0c;个人隐私和数据安全正面临越来越多的挑战。随着网络攻击、数据泄露和个人隐私侵犯事件的频发&#xff0c;保护个人信息变得尤为重要。VPN&#xff08;虚拟私人网络&#xff09;作为一种安全上网的工具&#xff0c;已成为许多人日常使用的必备…

机器人路径规划 | 基于极光PLO优化算法的机器人三维路径规划Matlab代码

基于极光PLO优化算法的机器人三维路径规划 完整代码私信回复基于极光PLO优化算法的机器人三维路径规划Matlab代码 一、引言 1.1、研究背景与意义 机器人路径规划是机器人技术中的一个核心问题&#xff0c;它涉及到在复杂环境中寻找一条从起点到终点的最优或次优路径。这一问…

js面试八股

es6里的箭头函数和普通函数有什么区别&#xff1f; 无法用作构造函数 普通函数&#xff1a;可以通过 new 关键字作为构造函数使用&#xff0c;创建新的对象实例。 箭头函数&#xff1a;不能作为构造函数&#xff0c;使用 new 调用箭头函数会抛出错误。 const NormalFunction…

解析HTML时需要注意什么?

在使用PHP爬虫解析HTML内容时&#xff0c;需要注意以下几个关键点&#xff0c;以确保数据提取的准确性和程序的稳定性。以下是一些重要的注意事项和最佳实践&#xff1a; 1. 选择合适的解析工具 PHP提供了多种工具来解析HTML&#xff0c;但选择合适的工具可以简化开发过程并提…

【Java高级篇】——第15篇:深入探讨Spring Boot与微服务架构

第15篇&#xff1a;深入探讨Spring Boot与微服务架构 Spring Boot作为Spring生态的革命性产品&#xff0c;通过 约定优于配置 和 自动装配 大幅简化了企业级应用开发。结合微服务架构的 服务自治、弹性扩展 和 去中心化治理 特性&#xff0c;成为构建云原生应用的黄金组合。本…