C++编程:ISteamNetworkingSockets示例工程2

      P2P(Peer-to-Peer)通信是一种去中心化的网络通信模式,参与者(节点)既作为客户端也作为服务器,直接进行数据交换,无需依赖中心服务器中转。

P2P通信核心原理

1. NAT穿透技术

  • STUN协议:通过公网服务器发现NAT后的公网IP和端口

  • TURN协议:当直接连接失败时,通过中继服务器转发数据

  • ICE框架:综合STUN/TURN,自动选择最佳连接路径

  • UDP打洞:利用NAT映射规则建立直接UDP连接

2. 节点发现机制

  • 中央服务器索引:通过中心服务器记录在线节点信息

  • 分布式哈希表(DHT):如Kademlia算法实现的去中心化节点发现

  • 组播/广播发现:在局域网内自动发现对等节点

3. 连接建立过程

  1. 节点发现与身份验证

  2. NAT类型检测与穿透方案选择

  3. 连接尝试(直接连接或中继)

  4. 安全通道建立与加密协商

TeamNetworkingSockets  P2P通信

核心功能特点

  1. NAT穿透能力

    • 自动使用STUN/TURN技术穿透大多数NAT设备

    • 内置ICE协议实现连接最佳路径选择

    • 支持中继备用连接方式

  2. 连接可靠性

    • 提供可靠和不可靠两种传输模式

    • 自动重传和拥塞控制

    • 消息分段和重组支持

  3. 安全机制

    • 默认启用加密通信

    • 身份验证系统防止中间人攻击

    • 可配置的安全选项

P2P连接建立流程

1. 初始化设置
// 初始化SteamNetworkingSockets
SteamNetworkingErrMsg errMsg;
if(!GameNetworkingSockets_Init(nullptr, errMsg)) {
    printf("初始化失败: %s", errMsg);
    return;
}

// 设置身份标识
SteamNetworkingIdentity identityLocal;
identityLocal.SetSteamID( steamIDLocal ); // 或其他标识方式

// 配置STUN服务器
SteamNetworkingUtils()->SetGlobalConfigValueString(
    k_ESteamNetworkingConfig_P2P_STUN_ServerList,
    "stun.l.google.com:19302"
);
2. 创建监听端(服务器)
// 创建P2P监听套接字
HSteamListenSocket hListenSock = SteamNetworkingSockets()->CreateListenSocketP2P(
    0,  // 虚拟端口
    0,  // 选项数量
    nullptr // 选项数组
);

// 设置连接状态变更回调
SteamNetworkingUtils()->SetGlobalCallback_SteamNetConnectionStatusChanged(
    OnSteamNetConnectionStatusChanged
);
3. 发起连接(客户端)
// 准备连接选项
SteamNetworkingConfigValue_t options[] = {
    { k_ESteamNetworkingConfig_SymmetricConnect, k_ESteamNetworkingConfig_Int32, 1 }
};

// 发起P2P连接
HSteamNetConnection hConn = SteamNetworkingSockets()->ConnectP2P(
    identityRemote,  // 远程身份
    0,              // 虚拟端口
    1,              // 选项数量
    options         // 选项数组
);
4. 信令服务集成(可选)
// 创建自定义信令客户端
ITrivialSignalingClient* pSignaling = CreateTrivialSignalingClient(
    "signaling.example.com:10000",
    SteamNetworkingSockets(),
    errMsg
);

// 使用自定义信令建立连接
ISteamNetworkingConnectionSignaling* pConnSignaling = pSignaling->CreateSignalingForConnection(
    identityRemote,
    errMsg
);

HSteamNetConnection hConn = SteamNetworkingSockets()->ConnectP2PCustomSignaling(
    pConnSignaling,
    &identityRemote,
    0,  // 虚拟端口
    0,  // 选项数量
    nullptr // 选项数组
);

消息收发处理

发送消息
void SendP2PMessage(HSteamNetConnection hConn, const void* pData, uint32 cbData)
{
    EResult result = SteamNetworkingSockets()->SendMessageToConnection(
        hConn,
        pData,
        cbData,
        k_nSteamNetworkingSend_Reliable, // 或k_nSteamNetworkingSend_Unreliable
        nullptr
    );
    
    if(result != k_EResultOK) {
        // 处理发送失败
    }
}
接收消息
void PollIncomingMessages()
{
    SteamNetworkingMessage_t* pMsg;
    int numMsgs = SteamNetworkingSockets()->ReceiveMessagesOnConnection(
        hConnection,
        &pMsg,
        1
    );
    
    if(numMsgs > 0) {
        // 处理消息
        ProcessMessage(pMsg->GetData(), pMsg->GetSize());
        
        // 释放消息
        pMsg->Release();
    }
}

连接状态管理

void OnSteamNetConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t* pInfo)
{
    switch(pInfo->m_info.m_eState) {
        case k_ESteamNetworkingConnectionState_Connected:
            // 连接成功建立
            break;
            
        case k_ESteamNetworkingConnectionState_ClosedByPeer:
        case k_ESteamNetworkingConnectionState_ProblemDetectedLocally:
            // 连接断开
            SteamNetworkingSockets()->CloseConnection(pInfo->m_hConn, 0, nullptr, false);
            break;
            
        // 其他状态处理...
    }
}

高级配置选项

  1. 传输优化

// 启用SDR传输后备
SteamNetworkingConfigValue_t opt;
opt.SetInt32(k_ESteamNetworkingConfig_Transport_ICE_Enable, 
    k_nSteamNetworkingConfig_Transport_ICE_Enable_All);
  1. 性能调优

// 调整发送缓冲区大小
SteamNetworkingUtils()->SetGlobalConfigValueInt32(
    k_ESteamNetworkingConfig_SendBufferSize,
    256 * 1024 // 256KB
);
  1. 调试支持

// 启用详细日志
SteamNetworkingUtils()->SetGlobalConfigValueInt32(
    k_ESteamNetworkingConfig_LogLevel_P2PRendezvous,
    k_ESteamNetworkingSocketsDebugOutputType_Debug
);

完整示例工程

ITrivialSignalingClient类

// Client of our dummy trivial signaling server service.
// Serves as an example of you how to hook up signaling server
// to SteamNetworkingSockets P2P connections

#pragma once

#include <steam/steamnetworkingcustomsignaling.h>

class ISteamNetworkingSockets;

/// Interface to our client.
class ITrivialSignalingClient
{
public:

	/// Create signaling object for a connection to peer
    virtual ISteamNetworkingConnectionSignaling *CreateSignalingForConnection(
        const SteamNetworkingIdentity &identityPeer,
        SteamNetworkingErrMsg &errMsg ) = 0;

	/// Poll the server for incoming signals and dispatch them.
	/// We use polling in this example just to keep it simple.
	/// You could use a service thread.
	virtual void Poll() = 0;

	/// Disconnect from the server and close down our polling thread.
	virtual void Release() = 0;
};

// Start connecting to the signaling server.
ITrivialSignalingClient *CreateTrivialSignalingClient(
	const char *address, // Address:port
	ISteamNetworkingSockets *pSteamNetworkingSockets, // Where should we send signals when we get them?
	SteamNetworkingErrMsg &errMsg // Error message is retjrned here if we fail
);

	


// Client of our dummy trivial signaling server service.
// Serves as an example of you how to hook up signaling server
// to SteamNetworkingSockets P2P connections

#include "../tests/test_common.h"

#include <string>
#include <mutex>
#include <deque>
#include <assert.h>

#include "trivial_signaling_client.h"
#include <steam/isteamnetworkingsockets.h>
#include <steam/isteamnetworkingutils.h>
#include <steam/steamnetworkingcustomsignaling.h>

#ifndef _WIN32
	#include <unistd.h>
	#include <sys/socket.h>
	#include <sys/types.h>
	#include <netinet/in.h>
	#include <netdb.h>
	#include <sys/ioctl.h>
	typedef int SOCKET;
	constexpr SOCKET INVALID_SOCKET = -1;
	inline void closesocket( SOCKET s ) { close(s); }
	inline int GetSocketError() { return errno; }
	inline bool IgnoreSocketError( int e )
	{
		return e == EAGAIN || e == ENOTCONN || e == EWOULDBLOCK;
	}
	#ifndef ioctlsocket
		#define ioctlsocket ioctl
	#endif
#endif
#ifdef _WIN32
	#include <winsock2.h>
	#include <ws2tcpip.h>
	typedef int socklen_t;
	inline int GetSocketError() { return WSAGetLastError(); }
	inline bool IgnoreSocketError( int e )
	{
		return e == WSAEWOULDB
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值