远场P2P穿越

什么叫远场P2P穿越

  • 远场P2P穿越(NAT穿越)是一种网络技术,用于实现不同NAT(网络地址转换)网络中的设备之间的直接通信,主要解决的是不同局域网下的设备如何建立直接连接的问题

相关概念

  • NAT:将私有IP转换为公网IP的技术
  • 穿透:突破NAT限制建立连接的过程

常见的穿透技术

  • STUN(simple Traversaf of UDP through NAT)
cpp 复制代码
struct stun_header {
    unit16_t type;
    unit16 length;
    unit32_t cookie;
    uint8_t transaction_id[12];
}
  • TURN( Traversal Using Relays around NAT)
cpp 复制代码
// TURN中继服务器配置示例
const char* TURN_SERVER = "turn:stun.example.com:3478";
const char* USERNAME = "user";
const char* PASSWORD = "pass";
  • ICE (Interactive Connectivity Establishment)
cpp 复制代码
enum class ICECandidateType {
    Host, // 本地地址
    ServerReflexive, // STUN反射地址
    PeerReflexive, // 对等反射地址
    Relay // TURN 中继地址
};

实现远场P2P穿透的基本步骤

cpp 复制代码
class P2PConnection {
public:
    P2PConnection() {
        // 1.初始化STUN/TURN客户端
        initSTUN();
        initTURN();
        // 2.收集ICE候选者
        gatherCandidates();
    }
private:
    void initSTUN() {
        // 配置STUN服务器
        const char* STUN_SERVER = "stun:stun.l.google.com:19302";
    }
  • 穿透流程:
    • 设备A和B都连接到了STUN服务器
    • 获取各自的公网IP和端口
    • 通过信令服务器交换链接信息
    • 尝试直接连接
    • 如果直接连接失败,使用TURN中继

常见的应用场景

  • 视频通过
  • P2P文件传输
  • 远程控制
  • 游戏联机

示例代码

  • 使用libnice库
cpp 复制代码
/**
 * @file testRemoteP2pCross.cpp
 * @brief P2P文件传输程序
 * @details 实现基于ICE和SSL的P2P安全文件传输功能
 */

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <thread>
#include <nice/agent.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <glib.h>

class P2PFileTransfer {
private:
    NiceAgent* agent;
    GMainLoop* loop;
    guint stream_id;
    SSL_CTX* ssl_ctx;
    SSL* ssl;
    std::string remote_credentials;
    bool is_sender;
    
    // 文件传输缓冲区大小
    static const int BUFFER_SIZE = 8192;

public:
    P2PFileTransfer(bool sender) : is_sender(sender) {
        // 初始化 GLib
        g_networking_init();
        loop = g_main_loop_new(NULL, FALSE);
        
        // 初始化 ICE agent
        agent = nice_agent_new(g_main_loop_get_context(loop),
                             NICE_COMPATIBILITY_RFC5245);
                             
        // 配置 STUN 服务器
        g_object_set(G_OBJECT(agent), 
                    "stun-server", "stun.l.google.com",
                    "stun-server-port", 19302,
                    NULL);
                    
        // 初始化 SSL
        initializeSSL();
    }
    
    ~P2PFileTransfer() {
        g_object_unref(agent);
        g_main_loop_unref(loop);
        SSL_free(ssl);
        SSL_CTX_free(ssl_ctx);
    }

private:
    void initializeSSL() {
        SSL_library_init();
        OpenSSL_add_all_algorithms();
        SSL_load_error_strings();
        
        ssl_ctx = SSL_CTX_new(TLS_method());
        if (!ssl_ctx) {
            throw std::runtime_error("SSL context creation failed");
        }
        
        // 配置 SSL 证书和私钥
        if (SSL_CTX_use_certificate_file(ssl_ctx, "cert.pem", SSL_FILETYPE_PEM) <= 0) {
            throw std::runtime_error("Certificate loading failed");
        }
        if (SSL_CTX_use_PrivateKey_file(ssl_ctx, "key.pem", SSL_FILETYPE_PEM) <= 0) {
            throw std::runtime_error("Private key loading failed");
        }
    }

    static void candidateGatheringDone(NiceAgent* agent, guint stream_id, gpointer data) {
        P2PFileTransfer* p2p = static_cast<P2PFileTransfer*>(data);
        gchar* local_sdp = nice_agent_generate_local_sdp(agent);
        std::cout << "Local SDP:\n" << local_sdp << std::endl;
        g_free(local_sdp);
    }

    static void componentStateChanged(NiceAgent* agent, guint stream_id, 
                                    guint component_id, guint state,
                                    gpointer data) {
        if (state == NICE_COMPONENT_STATE_READY) {
            std::cout << "ICE connection established!" << std::endl;
            P2PFileTransfer* p2p = static_cast<P2PFileTransfer*>(data);
            p2p->startTransfer();
        }
    }

public:
    void setupConnection() {
        // 添加媒体流
        stream_id = nice_agent_add_stream(agent, 1);
        
        // 设置回调函数
        g_signal_connect(G_OBJECT(agent), "candidate-gathering-done",
                        G_CALLBACK(candidateGatheringDone), this);
        g_signal_connect(G_OBJECT(agent), "component-state-changed",
                        G_CALLBACK(componentStateChanged), this);
                        
        // 开始收集候选者
        nice_agent_gather_candidates(agent, stream_id);
        
        // 运行主循环
        std::thread loop_thread([this]() {
            g_main_loop_run(this->loop);
        });
        loop_thread.detach();
    }

    void sendFile(const std::string& filename) {
        if (!is_sender) {
            throw std::runtime_error("This instance is not configured as sender");
        }

        std::ifstream file(filename, std::ios::binary);
        if (!file) {
            throw std::runtime_error("Cannot open file: " + filename);
        }

        // 首先发送文件名和大小
        file.seekg(0, std::ios::end);
        size_t filesize = file.tellg();
        file.seekg(0, std::ios::beg);

        // 发送文件信息
        std::string file_info = filename + ":" + std::to_string(filesize);
        SSL_write(ssl, file_info.c_str(), file_info.length());

        // 发送文件内容
        std::vector<char> buffer(BUFFER_SIZE);
        while (!file.eof()) {
            file.read(buffer.data(), BUFFER_SIZE);
            std::streamsize bytes_read = file.gcount();
            if (bytes_read > 0) {
                SSL_write(ssl, buffer.data(), bytes_read);
            }
        }

        file.close();
    }

    void receiveFile() {
        if (is_sender) {
            throw std::runtime_error("This instance is not configured as receiver");
        }

        // 接收文件信息
        char info_buffer[1024];
        int bytes = SSL_read(ssl, info_buffer, sizeof(info_buffer));
        std::string file_info(info_buffer, bytes);

        // 解析文件名和大小
        size_t pos = file_info.find(':');
        std::string filename = file_info.substr(0, pos);
        size_t filesize = std::stoull(file_info.substr(pos + 1));

        // 接收文件内容
        std::ofstream file(filename, std::ios::binary);
        std::vector<char> buffer(BUFFER_SIZE);
        size_t total_received = 0;

        while (total_received < filesize) {
            int bytes = SSL_read(ssl, buffer.data(), BUFFER_SIZE);
            if (bytes > 0) {
                file.write(buffer.data(), bytes);
                total_received += bytes;
            }
        }

        file.close();
    }

private:
    void startTransfer() {
        // 创建 SSL 连接
        ssl = SSL_new(ssl_ctx);
        // 设置 SSL 为服务器或客户端模式
        if (is_sender) {
            SSL_set_connect_state(ssl);
        } else {
            SSL_set_accept_state(ssl);
        }

        // 开始文件传输
        if (is_sender) {
            sendFile("example.txt");
        } else {
            receiveFile();
        }
    }
};

// 使用示例
int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cout << "Usage: " << argv[0] << " [send|receive]" << std::endl;
        return 1;
    }

    try {
        bool is_sender = std::string(argv[1]) == "send";
        P2PFileTransfer p2p(is_sender);
        p2p.setupConnection();

        // 等待用户输入远程端的连接信息
        std::cout << "Enter remote SDP: " << std::endl;
        std::string remote_sdp;
        std::getline(std::cin, remote_sdp);

        // 这里应该处理远程 SDP 并建立连接
        // ...

        // 保持程序运行
        std::string input;
        std::getline(std::cin, input);

    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
相关推荐
hgdlip13 分钟前
抖音评论区的IP属地可以关吗?详细解答
网络·网络协议·tcp/ip
wjcroom28 分钟前
【socketioxide和axum集成-实现websocket实时通信-Rust点滴】
网络·websocket·网络协议
Libby博仙1 小时前
asp.net core Web Api中的数据绑定
java·前端·asp.net
秋说11 小时前
【网络协议】什么是 BGP? | 解释 BGP 路由
网络协议·bgp
wangqiaowq12 小时前
HTTP、HTTP/2 和 gRPC 是网络通信协议或基于这些协议的技术,它们之间有显著的区别
网络·网络协议·http
InnovatorX12 小时前
Node.js 中 http 模块的深度剖析与实战应用
网络协议·http·node.js
橙子 chen12 小时前
工具函数 - 调用http
java·大数据·网络·数据库·python·网络协议·http
轨迹H12 小时前
DVWA靶场Open HTTP Redirect (重定向) 漏洞所有级别通关教程及源码审计
网络协议·渗透测试·dvwa·重定向·web漏洞
l1x1n012 小时前
【Hackthebox 中英 Write-Up】Web Request | 分析 HTTP 请求和响应
前端·网络协议·http
Libby博仙12 小时前
ASP.NET CORE 依赖注入的三种方式,分别是什么,使用场景
数据库·后端·asp.net·.netcore