c++ Steamworks软件开发工具包ISteamNetworkingSockets->连接点对点

ru9i0ody  于 2023-03-14  发布在  其他
关注(0)|答案(1)|浏览(276)

我尝试使用SteamNetworkingSockets->ConnectP2P连接两个对等点,这应该在两个对等点之间建立数据报中继。我的服务器使用CreateListenSocketP2P,客户端使用ConnectP2P和SteamNetworkingIdentity。问题是客户端无法连接,我不知道原因。我运行服务器,API加载它,可以显示我的流ID,但连接拒绝。

#include <iostream>
#include <steam/steam_api.h>
#include <steam/isteamnetworking.h>
#include <steam/isteamfriends.h>
#include <steam/isteamnetworkingmessages.h>
#include <steam/isteamnetworkingsockets.h>
#include <steam/isteamnetworkingutils.h>
#include <thread>
#include <assert.h>
#define VALID_64_STEAMID 0xFFFFFFFFFFFFFFFF
bool g_bQuit = false;

class Client {
public:
    SteamNetworkingIdentity sni;
    ISteamNetworkingSockets* m_pInterface;
    HSteamNetConnection m_hConnection;
    Client() {
        memset(&sni, 0, sizeof(SteamNetworkingIdentity));
        sni.m_eType = k_ESteamNetworkingIdentityType_SteamID;
        sni.SetSteamID64(VALID_64_STEAMID);
        if (sni.IsInvalid()) {
            printf("invalid id\n");
            exit(0);
        }
        m_pInterface = SteamNetworkingSockets();
        SteamNetworkingConfigValue_t opt;
        opt.SetPtr(k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged, (void*)SteamNetConnectionStatusChangedCallback);

        m_hConnection = m_pInterface->ConnectP2P(sni, 0, 1, &opt);
        if (m_hConnection == k_HSteamNetConnection_Invalid) {
            printf(":failed to connect\n");
            exit(0);
        }

    }
    void Run() {
        while (!g_bQuit) {
            PollIncomingMessages();
            PollConnectionStateChanges();
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    void PollIncomingMessages() {
        while (!g_bQuit)
        {
            ISteamNetworkingMessage* pIncomingMsg = nullptr;
            int numMsgs = m_pInterface->ReceiveMessagesOnConnection(m_hConnection, &pIncomingMsg, 1);
            if (numMsgs == 0)
                break;
            if (numMsgs < 0) {
                printf("Fatal error on incoming messages\n");
                exit(0);
            }
            printf("message received\n");
            // We don't need this anymore.
            pIncomingMsg->Release();
        }
    }
    void OnSteamNetConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t* pInfo)
    {
        assert(pInfo->m_hConn == m_hConnection || m_hConnection == k_HSteamNetConnection_Invalid);

        // What's the state of the connection?
        switch (pInfo->m_info.m_eState)
        {
        case k_ESteamNetworkingConnectionState_None:
            // NOTE: We will get callbacks here when we destroy connections.  You can ignore these.
            break;

        case k_ESteamNetworkingConnectionState_ClosedByPeer:
        case k_ESteamNetworkingConnectionState_ProblemDetectedLocally:
        {
            g_bQuit = true;

            // Print an appropriate message
            if (pInfo->m_eOldState == k_ESteamNetworkingConnectionState_Connecting)
            {
                // Note: we could distinguish between a timeout, a rejected connection,
                // or some other transport problem.
                printf("Unable to connect to host", pInfo->m_info.m_szEndDebug);
            }
            else if (pInfo->m_info.m_eState == k_ESteamNetworkingConnectionState_ProblemDetectedLocally)
            {
                printf("Lost connection to host", pInfo->m_info.m_szEndDebug);
            }
            else
            {
                // NOTE: We could check the reason code for a normal disconnection
                printf("The host hath bidden us farewell.  (%s)", pInfo->m_info.m_szEndDebug);
            }

            // Clean up the connection.  This is important!
            // The connection is "closed" in the network sense, but
            // it has not been destroyed.  We must close it on our end, too
            // to finish up.  The reason information do not matter in this case,
            // and we cannot linger because it's already closed on the other end,
            // so we just pass 0's.
            m_pInterface->CloseConnection(pInfo->m_hConn, 0, nullptr, false);
            m_hConnection = k_HSteamNetConnection_Invalid;
            break;
        }

        case k_ESteamNetworkingConnectionState_Connecting:
            // We will get this callback when we start connecting.
            // We can ignore this.
            break;

        case k_ESteamNetworkingConnectionState_Connected:
            printf("Connected to server OK");
            break;

        default:
            // Silences -Wswitch
            break;
        }
    }
    static Client* s_pCallbackInstance;
    static void SteamNetConnectionStatusChangedCallback(SteamNetConnectionStatusChangedCallback_t* pInfo)
    {
        s_pCallbackInstance->OnSteamNetConnectionStatusChanged(pInfo);
    }

    void PollConnectionStateChanges()
    {
        s_pCallbackInstance = this;
        m_pInterface->RunCallbacks();
    }
};
Client* Client::s_pCallbackInstance = nullptr;
int main(int argc, const char* argv[]) {
    // insert code here...
    if (!SteamAPI_Init()) {
        printf("Failed to initialize Steamworks SDK\n");
        exit(1);
    }

    // Get local user Steam ID
    CSteamID localID = SteamUser()->GetSteamID();
    printf("Local user Steam ID: %llu\n", localID.ConvertToUint64());
    printf("%s\n", SteamFriends()->GetPersonaName());
    ISteamNetworkingUtils* utils;
    utils = SteamNetworkingUtils();
    utils->InitRelayNetworkAccess();

    printf("Running client\n");
    //run client
    Client c;
    c.Run();


    SteamAPI_Shutdown();
    return 0;
}

它尝试连接,但随后在内部打印printf("Unable to connect to host", pInfo->m_info.m_szEndDebug);

// Print an appropriate message
            if (pInfo->m_eOldState == k_ESteamNetworkingConnectionState_Connecting)
            {
                // Note: we could distinguish between a timeout, a rejected connection,
                // or some other transport problem.
                printf("Unable to connect to host", pInfo->m_info.m_szEndDebug);
            }

我很困惑为什么这会失败,而且蒸汽ID是有效的0xFFFFFFFFFFFF只是一个占位符。
有没有人有使用SteamNetworkingSockets和SteamNetworkingIdentity的经验,可以对点对点连接有所帮助?

b09cbbtk

b09cbbtk1#

我看到了两个可能导致代码失败的问题。如何分配SteamID和网络可用性。
我测试了你的代码,虽然我没有提供一个手动的CSteamID,而是使用了SteamFriends来收集两个独立账户之间可用好友的Steam ID,并且连接通过了。即使sni.IsInvalid()返回false,我也假设Steam的后端有一些安全措施来防止两个不知名的账户通信。还有一件事要注意,我不确定你使用的是哪个APP_ID。我们正在使用它进行测试,但我猜它是APP_ID 480,它在对等连接匹配方面有一些限制。
但我是这样连接客户端和主机的,首先我确定两个对等体是朋友,然后我做了:

#include <iostream>
#include <steam/steam_api.h>
#include <steam/isteamnetworking.h>
#include <steam/isteamfriends.h>
#include <steam/isteamnetworkingmessages.h>
#include <steam/isteamnetworkingsockets.h>
#include <steam/isteamnetworkingutils.h>
#include <thread>
#include <assert.h>
bool g_bQuit = false;
#define TARGET_FRIEND_ID 0xFFFFFFFFFFFFFFFF
class Client {
public:
    SteamNetworkingIdentity sni;
    ISteamNetworkingSockets* m_pInterface;
    HSteamNetConnection m_hConnection;
    ISteamFriends* m_pFriends;
    Client() {
        bool bFoundFriend = false;
        m_pFriends = SteamFriends();
        m_pInterface = SteamNetworkingSockets();
        
        
        memset(&sni, 0, sizeof(SteamNetworkingIdentity));
        sni.m_eType = k_ESteamNetworkingIdentityType_SteamID;
        
        //IMPORTANT BIT HERE
        int nFriendCount = m_pFriends->GetFriendCount(k_EFriendFlagFriendshipRequested | k_EFriendFlagRequestingFriendship );
        for(int nIndex = 0; nIndex < nFriendCount ; ++nIndex)
        {
            CSteamID csFriendId = m_pFriends->GetFriendByIndex(nIndex, k_EFriendFlagFriendshipRequested | k_EFriendFlagRequestingFriendship);
            if(csFriendId.ConvertToUint64() == TARGET_FRIEND_ID)
            {
                sni.SetSteamID(csFriendId);
                bFoundFriend = true;
                break;
            }
        }

        if(bFoundFriend)
        {
            SteamNetworkingConfigValue_t opt;
            opt.SetPtr(k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged, (void*)SteamNetConnectionStatusChangedCallback);

            m_hConnection = m_pInterface->ConnectP2P(sni, 0, 1, &opt);
            if (m_hConnection == k_HSteamNetConnection_Invalid) {
                fprintf(stderr,"Bad socket\n");
                exit(0);
            }
        }
        else
        {
            fprintf(stderr,"Unable locate friend");
        }
        

    }
    void Run() {
        while (!g_bQuit) {
            PollIncomingMessages();
            PollConnectionStateChanges();
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    void PollIncomingMessages() {
        while (!g_bQuit)
        {
            ISteamNetworkingMessage* pIncomingMsg = nullptr;
            int numMsgs = m_pInterface->ReceiveMessagesOnConnection(m_hConnection, &pIncomingMsg, 1);
            if (numMsgs == 0)
                break;
            fprintf(stderr,"We got a message with size:%d\n",pIncomingMsg->m_cbSize);
            pIncomingMsg->Release();
        }
    }
    void OnSteamNetConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t* pInfo)
    {
        assert(pInfo->m_hConn == m_hConnection || m_hConnection == k_HSteamNetConnection_Invalid);
        switch (pInfo->m_info.m_eState)
        {

        case k_ESteamNetworkingConnectionState_ClosedByPeer:
        case k_ESteamNetworkingConnectionState_ProblemDetectedLocally:
        {
            fprintf(stderr,"Unable to connect to host\n");
            g_bQuit = true;
            m_pInterface->CloseConnection(pInfo->m_hConn, 0, nullptr, false);
            m_hConnection = k_HSteamNetConnection_Invalid;
            break;
        }

        case k_ESteamNetworkingConnectionState_Connected:
                fprintf(stderr,"Connection OK\n");
            break;

        default:
            // Silences -Wswitch
            break;
        }
    }
    static Client* s_pCallbackInstance;
    static void SteamNetConnectionStatusChangedCallback(SteamNetConnectionStatusChangedCallback_t* pInfo)
    {
        s_pCallbackInstance->OnSteamNetConnectionStatusChanged(pInfo);
    }

    void PollConnectionStateChanges()
    {
        s_pCallbackInstance = this;
        m_pInterface->RunCallbacks();
    }
};
Client* Client::s_pCallbackInstance = nullptr;
int main(int argc, const char* argv[]) {
    // insert code here...
    if (!SteamAPI_Init()) {
        fprintf(stderr,"Failed to init steam\n");
        exit(1);
    }

    // Get local user Steam ID
    CSteamID localID = SteamUser()->GetSteamID();
    fprintf(stderr,"Local user Steam ID: %llu\n", localID.ConvertToUint64());
    fprintf(stderr,"%s\n", SteamFriends()->GetPersonaName());
    ISteamNetworkingUtils* utils;
    utils = SteamNetworkingUtils();
    utils->InitRelayNetworkAccess();
    //ALSO VERY IMPORTANT
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    ESteamNetworkingAvailability eAvailable = utils->GetRelayNetworkStatus(nullptr);
    if(k_ESteamNetworkingAvailability_Current == eAvailable)
    {
        //Our network is ready
        //run client
        Client c;
        c.Run();
    }
    SteamAPI_Shutdown();
    return 0;
}

需要注意的是,在调用m_pFriends->GetFriendByIndex之前,你必须先调用m_pFriends->GetFriendCount(...),它们都需要使用位标志,有几个标志可以使用,但是那些请求成为朋友的人和那些你请求成为朋友的人是最常见的。你也可以比较朋友的名字s,而不必查找每个对等体的64位ID。这是通过m_pFriends->GetFriendPersonaName()完成的。

//IMPORTANT BIT HERE
        int nFriendCount = m_pFriends->GetFriendCount(k_EFriendFlagFriendshipRequested | k_EFriendFlagRequestingFriendship );
        
        for(int nIndex = 0; nIndex < nFriendCount ; ++nIndex)
        {
            CSteamID csFriendId = m_pFriends->GetFriendByIndex(nIndex, k_EFriendFlagFriendshipRequested | k_EFriendFlagRequestingFriendship);
            std::string sFriendName = m_pFriends->GetFriendPersonaName(csFriendId);
            
            if(sFriendName.compare("friendname"))
            {
                sni.SetSteamID(csFriendId);
                bFoundFriend = true;
                break;
            }
        }

关于蒸汽文档的最后注解:
如果您知道将使用中继网络(例如,因为您预期进行P2P连接),请调用此函数以初始化中继网络。如果不调用此函数,初始化将延迟到您第一次使用需要访问中继网络的功能时,这将延迟第一次访问。
如果前一次尝试失败,你也可以调用这个函数来强制重试。执行任何需要访问中继网络的操作也会触发重试,所以调用这个函数从来都不是严格必要的,但是把它称为程序启动时间可能会很有用,如果预期访问中继网络。请使用GetRelayNetworkStatus或侦听SteamRelayNetworkStatus_t回调来了解初始化何时完成。通常初始化在几秒钟内完成。
这是非常重要的!在您的代码中,您没有检查GetRelayNetworkStatus的结果,而InitRelayNetworkAccess是一个非阻塞函数,可能需要几秒钟才能完成。您可能需要循环,直到您的状态可用。在我的示例中,我只是在线程上休眠了5秒钟,以查看它是否可用。

相关问题