遊戲的網路功能Networking for games

了解如何在您的 DirectX 遊戲中開發與納入網路功能。Learn how to develop and incorporate networking features into your DirectX game.

概念一覽Concepts at a glance

不論您的 DirectX 遊戲是簡單的獨立遊戲或大型的多人遊戲,都可在遊戲中使用多種不同的網路功能。A variety of networking features can be used in your DirectX game, whether it is a simple standalone game to massively multi-player games. 最簡單的網路運用就是將使用者名稱與遊戲分數儲存在中央網路伺服器。The simplest use of networking would be to store user names and game scores on a central network server.

使用基礎結構 (主從式架構或網際網路點對點) 模型的多人遊戲以及臨機操作 (本機點對點) 遊戲皆需要網路 API。Networking APIs are needed in multi-player games that use the infrastructure (client-server or internet peer-to-peer) model and also by ad hoc (local peer-to-peer) games. 對於伺服器型的多人遊戲,通常中央遊戲伺服器會處理大部分遊戲作業,而用戶端遊戲應用程式則用於輸入、顯示圖形、播放音訊與其他功能。For server-based multi-player games, a central game server usually handles most of the game operations and the client game app is used for input, displaying graphics, playing audio, and other features. 網路傳輸的速度與延遲是獲得滿意的遊戲體驗的一個關鍵點。The speed and latency of network transfers is a concern for a satisfactory game experience.

對於點對點遊戲,每個玩家的應用程式會處理輸入與圖形。For peer-to-peer games, each player's app handles the input and graphics. 在大多數情況下,遊戲玩家位於鄰近位置,因此網路延遲的狀況較輕微,但仍然值得關注。In most cases, the game players are located in close proximity so that network latency should be lower but is still a concern. 如何探索其他玩家,並建立連線成為一個重要問題。How to discovery peers and establish a connection becomes a concern.

對於單人遊戲,通常會使用中央 Web 伺服器或服務來儲存使用者名稱、遊戲分數與其他資訊。For single-player games, a central Web server or service is often used to store user names, game scores, and other miscellaneous information. 在這些遊戲中,因為網路傳輸的速度與延遲不會直接影響遊戲運作,因此不是太重要。In these games, the speed and latency of networking transfers is less of a concern since it doesn't directly affect game operation.

網路狀況會隨時改變,因此所有使用網路功能 API 的遊戲都必須能處理可能發生的網路例外狀況。Network conditions can change at any time, so any game that uses networking APIs needs to handle network exceptions that may occur. 若要深入了解如何處理網路例外狀況,請參閱網路功能基本知識To learn more about handling network exceptions, see Networking basics.

防火牆與 Web Proxy 很常見,且可能影響網路功能的使用。Firewalls and web proxies are common and can affect the ability to use networking features. 使用網路的遊戲必須預備好能正確處理防火牆與 Proxy。A game that uses networking needs to be prepared to properly handle firewalls and proxies.

對於行動裝置,在漫遊或資料成本昂貴的計量付費網路中的,監控可用的網路資源及操作行為是很重要的。For mobile devices, it is important to monitor available network resources and behave accordingly when on metered networks where roaming or data costs can be significant.

網路隔離是 Windows 使用的應用程式安全性模型的一部分。Network isolation is part of the app security model used by Windows. Windows 會主動探索網路界限並為網路隔離強制網路存取限制。Windows actively discovers network boundaries and enforces network access restrictions for network isolation. 應用程式必須宣告網路隔離功能,才能定義網路存取的範圍。Apps must declare network isolation capabilities in order to define the scope of network access. 若未宣告這些功能,應用程式將無法存取網路資源。Without declaring these capabilities, your app will not have access to network resources. 若要 深入了解 Windows 如何強制執行應用程式的網路隔離,請參閱如何設定網路隔離功能To learn more about how Windows enforces network isolation for apps, see How to configure network isolation capabilities.

設計考量Design considerations

DirectX 遊戲可使用多種不同的網路 API。A variety of networking APIs can be used in DirectX games. 因此,挑選正確的 API 是很重要的。So, it is important to pick the right API. Windows 支援各種不同的網路 API,讓您的 應用程式可以透過網際網路或私人網路與其他電腦或裝置通訊。Windows supports a variety of networking APIs that your app can use to communicate with other computers and devices over either the Internet or private networks. 您首先要做的是找出應用程式所需的網路功能。Your first step is to figure out what networking features your app needs.

這些是更受歡迎的遊戲網路 Api。These are the more popular network APIs for games.

  • TCP 與通訊端 - 提供可靠的連線。TCP and sockets - Provides a reliable connection. 在不需安全性的遊戲中使用 TCP。Use TCP for game operations that don’t need security. TCP 能輕鬆擴充伺服器,因此常用於使用基礎結構 (主從式架構或網際網路點對點) 模型的遊戲。TCP allows the server to easily scale, so it is commonly used in games that use the infrastructure (client-server or internet peer-to-peer) model. Wi-Fi Direct 與藍牙上的臨機 (本機點對點) 遊戲也可使用 TCP。TCP can also be used by ad hoc (local peer-to-peer) games over Wi-Fi Direct and Bluetooth. TCP 常用於遊戲物件動作、角色互動、文字交談與其他作業。TCP is commonly used for game object movement, character interaction, text chat, and other operations. StreamSocket類別提供可用於 Microsoft Store 遊戲的 TCP 通訊端。The StreamSocket class provides a TCP socket that can be used in Microsoft Store games. StreamSocket 類別與 Windows::Networking::Sockets 命名空間中的相關類別搭配使用。The StreamSocket class is used with related classes in the Windows::Networking::Sockets namespace.
  • 使用 SSL 的 TCP 與通訊端 - 提供能防竊聽的可靠連線。TCP and sockets using SSL - Provides a reliable connection that prevents eavesdropping. 針對需要安全性的遊戲使用 TCP 連線搭配 SSL。Use TCP connections with SSL for game operations that need security. SSL 的加密與額外負荷會增加延遲並影響效能,請只在需要安全性時使用。The encryption and overhead of SSL adds a cost in latency and performance, so it is only used when security is needed. TCP 搭配 SSL 常用於登入、購買與交易資產、遊戲角色建立與管理。TCP with SSL is commonly used for login, purchasing and trading assets, game character creation and management. StreamSocket 類別提供支援 SSL 的 TCP 通訊端。The StreamSocket class provides a TCP socket that supports SSL.
  • UDP 與通訊端 - 提供不可靠的網路傳輸,但額外負荷小。UDP and sockets - Provides unreliable network transfers with low overhead. UDP 用於要求低延遲且可容許某些封包遺失的遊戲作業。UDP is used for game operations that require low latency and can tolerate some packet loss. 它常用於搏鬥遊戲、射擊與追蹤、網路音訊與視訊聊天。This is often used for fighting games, shooting and tracers, network audio, and voice chat. DatagramSocket類別提供可用於 Microsoft Store 遊戲的 UDP 通訊端。The DatagramSocket class provides a UDP socket that can be used in Microsoft Store games. DatagramSocket 類別與 Windows::Networking::Sockets 命名空間中的相關類別搭配使用。The DatagramSocket class is used with related classes in the Windows::Networking::Sockets namespace.
  • HTTP 用戶端 - 提供可靠的 HTTP 伺服器連線。HTTP Client - Provides a reliable connection to HTTP servers. 最常見的網路案例是存取網站以擷取或儲存資訊。The most common networking scenario is to access a web site to retrieve or store information. 使用網站來儲存使用者資訊與遊戲分數的遊戲就是一個簡單的例子。A simple example would be a game that uses a website to store user information and game scores. 為求安全性搭配 SSL 使用時,可使用 HTTP 用戶端來登入、購買、交易資產、遊戲角色建立與管理。When used with SSL for security, an HTTP client can be used for login, purchasing, trading assets, game character creation, and management. HttpClient類別提供新式 HTTP 用戶端 API,可用於 Microsoft Store 遊戲中。The HttpClient class provides a modern HTTP client API for use in Microsoft Store games. HttpClient 類別與 Windows::Web::Http 命名空間中的相關類別搭配使用。The HttpClient class is used with related classes in the Windows::Web::Http namespace.

處理您的 DirectX 遊戲中的網路例外狀況Handling network exceptions in your DirectX game

當您的 DirectX 遊戲中發生網路例外狀況時,表示有嚴重的問題或失敗。When a network exception occurs in your DirectX game, this indicates a significant problem or failure. 使用網路 API 時,有許多原因會導致例外狀況發生。Exceptions can occur for many reasons when using networking APIs. 例外狀況通常是因為網路連線變更,或是遠端主機或伺服器發生其他網路問題。Often, the exception can result from changes in network connectivity or other networking issues with the remote host or server.

使用網路 API 時,發生例外狀況的一些原因包括下列項目:Some causes of exceptions when using networking APIs include the following:

  • 使用者輸入的主機名稱或 URI 包含錯誤和無效。Input from the user for a hostname or a URI contains errors and is not valid.
  • 查詢主機名稱或 URI 時名稱解析失敗。Name resolutions failures when looking up a hostname or a URi.
  • 網路連線中斷或變更。Loss or change in network connectivity.
  • 使用通訊端或 HTTP 用戶端 API 的網路連線失敗。Network connection failures using sockets or the HTTP client APIs.
  • 網路伺服器或遠端端點錯誤。Network server or remote endpoint errors.
  • 其他網路錯誤。Miscellaneous networking errors.

網路錯誤 (例如,連線中斷或變更、連線失敗、伺服器故障) 的例外狀況隨時都有可能發生。Exceptions from network errors (for example, loss or change of connectivity, connection failures, and server failures) can happen at any time. 這些錯誤會造成擲出例外狀況。These errors result in exceptions being thrown. 如果您的應用程式不處理例外狀況,它可能會造成您整個應用程式被執行階段終止。If an exception is not handled by your app, it can cause your entire app to be terminated by the runtime.

您必須撰寫程式碼,以處理在呼叫大多數非同步網路方法時的例外狀況。You must write code to handle exceptions when you call most asynchronous network methods. 有時候,在例外狀況發生時,會以重試網路方法的方式來解決問題。Sometimes, when an exception occurs, a network method can be retried as a way to resolve the problem. 其他時候,您的 app 需要規劃,在沒有網路連線的情況下,使用之前快取的資料繼續工作。Other times, your app may need to plan to continue without network connectivity using previously cached data.

通用 Windows 平台 (UWP) app 通常會擲回單一例外狀況。Universal Windows Platform (UWP) apps generally throw a single exception. 您的例外狀況處理常式可以擷取例外狀況發生原因的詳細資訊,更清楚地了解失敗的情況並作出適當的決定。Your exception handler can retrieve more detailed information about the cause of the exception to better understand the failure and make appropriate decisions.

當 UWP app 的 DirectX 遊戲發生例外狀況時,會擷取導致錯誤的 HRESULT 值。When an exception occurs in a DirectX game that is a UWP app, the HRESULT value for the cause of the error can be retrieved. Winerror.h 包含的檔案含有一個大型的可能 HRESULT 值清單,其中包含網路錯誤。The Winerror.h include file contains a large list of possible HRESULT values that includes network errors.

網路 API 支援不同的方法來抓取例外狀況發生原因的更詳細資訊。The networking APIs support different methods for retrieving this detailed information about the cause of an exception.

  • 擷取導致例外狀況的錯誤 HRESULT 值的方法。A method to retrieve the HRESULT value of the error that caused the exception. 可能的 HRESULT 值的可能性清單過大且未指定。The possible list of potential HRESULT values is large and unspecified. 使用任何網路 API 都可擷取 HRESULT 值。The HRESULT value can be retrieved when using any of the networking APIs.
  • HRESULT 值轉換為列舉值的協助程式方法。A helper method that converts the HRESULT value to an enumeration value. 可能的列舉值清單已指定,而且相對過小。The list of possible enumeration values is specified and relatively small. Windows::Networking::Sockets 的通訊端類別可使用協助程式方法。A helper method is available for the socket classes in the Windows::Networking::Sockets.

Windows.Networking.Sockets 中的例外狀況Exceptions in Windows.Networking.Sockets

如果傳送的字串不是有效的主機名稱 (包含不允許在主機名稱中使用的字元),與通訊端一起使用的 HostName 類別的建構函式會發生例外狀況。The constructor for the HostName class used with sockets can throw an exception if the string passed is not a valid hostname (contains characters that are not allowed in a host name). 如果 app 取得使用者在遊戲的對等連線為 HostName 輸入的值,則建構函式應在 try/catch 區塊中。If an app gets input from the user for the HostName for a peer connection for gaming, the constructor should be in a try/catch block. 如果發生例外狀況,app 可通知使用者並要求新的主機名稱。If an exception is thrown, the app can notify the user and request a new hostname.

新增程式碼以驗證使用者輸入的主機名稱字串Add code to validate a string for a hostname from the user

// Define some variables at the class level.
Windows::Networking::HostName^ remoteHost;

bool isHostnameFromUser = false;
bool isHostnameValid = false;

///...

// If the value of 'remoteHostname' is set by the user in a control as input 
// and is therefore untrusted input and could contain errors. 
// If we can't create a valid hostname, we notify the user in statusText 
// about the incorrect input.

String ^hostString = remoteHostname;

try 
{
    remoteHost = ref new Windows::Networking:Host(hostString);
    isHostnameValid = true;
}
catch (InvalidArgumentException ^ex)
{
    statusText->Text = "You entered a bad hostname, please re-enter a valid hostname.";
    return;
}

isHostnameFromUser = true;

// ... Continue with code to execute with a valid hostname.

Windows.Networking.Sockets 命名空間有便利的協助程式方法及列舉,在使用通訊端時用來處理錯誤。The Windows.Networking.Sockets namespace has convenient helper methods and enumerations for handling errors when using sockets. 這對於在您的應用程式中以不同的方式處理特定網路例外狀況時很有用。This can be useful for handling specific network exceptions differently in your app.

DatagramSocketStreamSocketStreamSocketListener 作業中若發生錯誤,會導致例外狀況。An error encountered on DatagramSocket, StreamSocket, or StreamSocketListener operation results in an exception being thrown. 例外狀況的原因是以 HRESULT 值表示的錯誤值。The cause of the exception is an error value represented as an HRESULT value. 使用 SocketError.GetStatus 方法,將通訊端作業的網路錯誤轉換為 SocketErrorStatus 列舉值。The SocketError.GetStatus method is used to convert a network error from a socket operation to a SocketErrorStatus enumeration value. 大多數 SocketErrorStatus 列舉值對應原始 Windows 通訊端作業傳回的錯誤。Most of the SocketErrorStatus enumeration values correspond to an error returned by the native Windows sockets operation. app 可以篩選特定 SocketErrorStatus 列舉值,依據例外狀況的發生原因來修改 app 行為。An app can filter on specific SocketErrorStatus enumeration values to modify app behavior depending on the cause of the exception.

針對參數驗證錯誤,app 也可以使用來自例外狀況的 HRESULT,深入了解更多關於導致例外狀況的錯誤詳細資訊。For parameter validation errors, an app can also use the HRESULT from the exception to learn more detailed information about the error that caused the exception. 可能的 HRESULT 值列在 Winerror.h 標頭檔中。Possible HRESULT values are listed in the Winerror.h header file. 針對大部分的參數驗證錯誤,傳回的 HRESULTE _ INVALIDARGFor most parameter validation errors, the HRESULT returned is E_INVALIDARG.

新增程式碼以處理嘗試建立串流通訊端連線時發生的例外狀況Add code to handle exceptions when trying to make a stream socket connection

using namespace Windows::Networking;
using namespace Windows::Networking::Sockets;
    
    // Define some more variables at the class level.

    bool isSocketConnected = false
    bool retrySocketConnect = false;

    // The number of times we have tried to connect the socket.
    unsigned int retryConnectCount = 0;

    // The maximum number of times to retry a connect operation.
    unsigned int maxRetryConnectCount = 5; 
    ///...

    // We pass in a valid remoteHost and serviceName parameter.
    // The hostname can contain a name or an IP address.
    // The servicename can contain a string or a TCP port number.

    StreamSocket ^ socket = ref new StreamSocket();
    SocketErrorStatus errorStatus; 
    HResult hr;

    // Save the socket, so any subsequent steps can use it.
    CoreApplication::Properties->Insert("clientSocket", socket);

    // Connect to the remote server. 
    create_task(socket->ConnectAsync(
            remoteHost,
            serviceName,
            SocketProtectionLevel::PlainSocket)).then([this] (task<void> previousTask)
    {
        try
        {
            // Try getting all exceptions from the continuation chain above this point.
            previousTask.get();

            isSocketConnected = true;
            // Mark the socket as connected. We do not really care about the value of the property, but the mere 
            // existence of  it means that we are connected.
            CoreApplication::Properties->Insert("connected", nullptr);
        }
        catch (Exception^ ex)
        {
            hr = ex.HResult;
            errorStatus = SocketStatus::GetStatus(hr); 
            if (errorStatus != Unknown)
            {
                                                                switch (errorStatus) 
                   {
                    case HostNotFound:
                        // If the hostname is from the user, this may indicate a bad input.
                        // Set a flag to ask the user to re-enter the hostname.
                        isHostnameValid = false;
                        return;
                        break;
                    case ConnectionRefused:
                        // The server might be temporarily busy.
                        retrySocketConnect = true;
                        return;
                        break; 
                    case NetworkIsUnreachable: 
                        // This could be a connectivity issue.
                        retrySocketConnect = true;
                        break;
                    case UnreachableHost: 
                        // This could be a connectivity issue.
                        retrySocketConnect = true;
                        break;
                    case NetworkIsDown: 
                        // This could be a connectivity issue.
                        retrySocketConnect = true;
                        break;
                    // Handle other errors. 
                    default: 
                        // The connection failed and no options are available.
                        // Try to use cached data if it is available. 
                        // You may want to tell the user that the connect failed.
                        break;
                }
                }
                else 
                {
                    // Received an Hresult that is not mapped to an enum.
                    // This could be a connectivity issue.
                    retrySocketConnect = true;
                }
            }
        });
    }

Windows.Web.Http 中的例外狀況Exceptions in Windows.Web.Http

如果傳送的字串不是有效的 URI (包含不允許在 URI 中使用的字元),與 Windows::Web::Http::HttpClient 一起使用的 Windows::Foundation::Uri 類別的建構函式會發生例外狀況。The constructor for the Windows::Foundation::Uri class used with Windows::Web::Http::HttpClient can throw an exception if the string passed is not a valid URI (contains characters that are not allowed in a URI). 在 C++ 中,沒有可以嘗試將字串剖析為 URI 的方法。In C++, there is no method to try and parse a string to a URI. 如果 app 取得使用者為 Windows::Foundation::Uri 輸入的值,則建構函式應在 try/catch 區塊中。If an app gets input from the user for the Windows::Foundation::Uri, the constructor should be in a try/catch block. 如果發生例外狀況,app 可通知使用者並要求新的 URI。If an exception is thrown, the app can notify the user and request a new URI.

您的應用程式也應檢查 URI 中的配置是 HTTP 或 HTTPS,因為 Windows::Web::Http::HttpClient 僅支援這些配置。Your app should also check that the scheme in the URI is HTTP or HTTPS since these are the only schemes supported by the Windows::Web::Http::HttpClient.

新增程式碼以驗證使用者輸入的 URI 字串Add code to validate a string for a URI from the user

    // Define some variables at the class level.
    Windows::Foundation::Uri^ resourceUri;

    bool isUriFromUser = false;
    bool isUriValid = false;

    ///...

    // If the value of 'inputUri' is set by the user in a control as input 
    // and is therefore untrusted input and could contain errors. 
    // If we can't create a valid hostname, we notify the user in statusText 
    // about the incorrect input.

    String ^uriString = inputUri;

    try 
    {
        isUriValid = false;
        resourceUri = ref new Windows::Foundation:Uri(uriString);

        if (resourceUri->SchemeName != "http" && resourceUri->SchemeName != "https")
        {
            statusText->Text = "Only 'http' and 'https' schemes supported. Please re-enter URI";
            return;
        }
        isUriValid = true;
    }
    catch (InvalidArgumentException ^ex)
    {
        statusText->Text = "You entered a bad URI, please re-enter Uri to continue.";
        return;
    }

    isUriFromUser = true;


    // ... Continue with code to execute with a valid URI.

Windows::Web::Http 命名空間缺少便利的函式。The Windows::Web::Http namespace lacks a convenience function. 所以使用 HttpClient 的 app 及此命名空間中的其他類別需要使用 HRESULT 值。So, an app using HttpClient and other classes in this namespace needs to use the HRESULT value.

在使用 C++ 的 app 中,Platform::Exception 代表 app 執行期間發生例外狀況時的錯誤。In apps using C++, the Platform::Exception represents an error during app execution when an exception occurs. Platform:: Exception:: HResult屬性會傳回指派給特定例外狀況的HResultThe Platform::Exception::HResult property returns the HRESULT assigned to the specific exception. Platform:: Exception:: Message屬性會傳回與HRESULT值相關聯的系統提供的字串。The Platform::Exception::Message property returns the system-provided string that is associated with the HRESULT value. 可能的 HRESULT 值列在 Winerror.h 標頭檔中。Possible HRESULT values are listed in the Winerror.h header file. app 可以篩選特定 HRESULT 值,依據例外狀況的發生原因來修改 app 行為。An app can filter on specific HRESULT values to modify app behavior depending on the cause of the exception.

針對大部分的參數驗證錯誤,傳回的 HRESULTE _ INVALIDARGFor most parameter validation errors, the HRESULT returned is E_INVALIDARG. 針對某些不正確的方法呼叫,傳回的 HRESULTE_ILLEGAL_METHOD_CALLFor some illegal method calls, the HRESULT returned is E_ILLEGAL_METHOD_CALL.

新增程式碼以處理嘗試使用 HttpClient 連線至 HTTP 伺服器時發生的例外狀況Add code to handle exceptions when trying to use HttpClient to connect to an HTTP server

using namespace Windows::Foundation;
using namespace Windows::Web::Http;
    
    // Define some more variables at the class level.

    bool isHttpClientConnected = false
    bool retryHttpClient = false;

    // The number of times we have tried to connect the socket
    unsigned int retryConnectCount = 0;

    // The maximum number of times to retry a connect operation.
    unsigned int maxRetryConnectCount = 5; 
    ///...

    // We pass in a valid resourceUri parameter.
    // The URI must contain a scheme and a name or an IP address.

    HttpClient ^ httpClient = ref new HttpClient();
    HResult hr;

    // Save the httpClient, so any subsequent steps can use it.
    CoreApplication::Properties->Insert("httpClient", httpClient);

    // Send a GET request to the HTTP server. 
    create_task(httpClient->GetAsync(resourceUri)).then([this] (task<void> previousTask)
    {
        try
        {
            // Try getting all exceptions from the continuation chain above this point.
            previousTask.get();

            isHttpClientConnected = true;
            // Mark the HttClient as connected. We do not really care about the value of the property, but the mere 
            // existence of  it means that we are connected.
            CoreApplication::Properties->Insert("connected", nullptr);
        }
        catch (Exception^ ex)
        {
            hr = ex.HResult;
                                                switch (errorStatus) 
               {
                case WININET_E_NAME_NOT_RESOLVED:
                    // If the Uri is from the user, this may indicate a bad input.
                    // Set a flag to ask user to re-enter the Uri.
                    isUriValid = false;
                    return;
                    break;
                case WININET_E_CANNOT_CONNECT:
                    // The server might be temporarily busy.
                    retryHttpClientConnect = true;
                    return;
                    break; 
                case WININET_E_CONNECTION_ABORTED: 
                    // This could be a connectivity issue.
                    retryHttpClientConnect = true;
                    break;
                case WININET_E_CONNECTION_RESET: 
                    // This could be a connectivity issue.
                    retryHttpClientConnect = true;
                    break;
                case INET_E_RESOURCE_NOT_FOUND: 
                    // The server cannot locate the resource specified in the uri.
                    // If the Uri is from user, this may indicate a bad input.
                    // Set a flag to ask the user to re-enter the Uri
                    isUriValid = false;
                    return;
                    break;
                // Handle other errors. 
                default: 
                    // The connection failed and no options are available.
                    // Try to use cached data if it is available. 
                    // You may want to tell the user that the connect failed.
                    break;
            }
            else 
            {
                // Received an Hresult that is not mapped to an enum.
                // This could be a connectivity issue.
                retrySocketConnect = true;
            }
        }
    });

其他資源Other resources

參考Reference

範例應用程式Sample apps