Steamworks 文献库
steamnetworkingtypes.h
网络 API 使用的杂项类型和功能。

描述网络主机


描述网络主机所需的类型。

enum ESteamNetworkingIdentityType

远程主机可以具有的不同类型的标识。 多数情况下,在 Steam 上这会是 CSteamID。 但是,在某些情况下,您可以禁用身份验证,在这种情况下,将使用更为通用的标识。

enum ESteamNetworkingIdentityType { // 虚拟/未知/无效 k_ESteamNetworkingIdentityType_Invalid = 0, // // 基本的特定于平台的标识符。 // k_ESteamNetworkingIdentityType_SteamID = 16, // 64位 CSteamID // // 特殊标识符。 // // 使用其IP地址(和端口)作为“身份”。 // 这些类型的身份始终未经验证。 // 它在移植普通套接字代码 // 以及其他您不关心身份验证的情况下很有用。 在这种 // 情况下,本地标识将为“localhost”, // 而远程地址将为其网络地址。 // // 我们对 IPv4 或 IPv6 使用相同的类型,并且 // 地址始终存储为 IPv6。 我们使用 IPv4 // 映射的地址来处理 IPv4。 k_ESteamNetworkingIdentityType_IPAddress = 1, // 通用字符串/二进制 blob。 由您的应用决定如何解读。 // 此库可以告诉您远程主机是否提供了一个 // 由您选择信任的人签名的证书,并带有此标识。 // 由您最终决定此标识的含义。 k_ESteamNetworkingIdentityType_GenericString = 2, k_ESteamNetworkingIdentityType_GenericBytes = 3, // 确保此枚举存储在一个整数中。 k_ESteamNetworkingIdentityType__Force32bit = 0x7fffffff, }}

struct SteamNetworkingIdentity


表示网络主机标识的抽象方法。

struct SteamNetworkingIdentity { /// 标识类型 ESteamNetworkingIdentityType m_eType; // // 以各种格式获取/设置。 // void Clear(); bool IsInvalid() const; // 如果我们是无效类型,则返回 true。 不进行任何其他有效性检查(例如,SteamID 是否实际有效) void SetSteamID( CSteamID steamID ); CSteamID GetSteamID() const; // 如果标识不是 SteamID,则返回黑色 CSteamID(!IsValid()) void SetSteamID64( uint64 steamID ); // 将 SteamID 作为原始 64 位数字 uint64 GetSteamID64() const; // 如果标识不是 SteamID,则返回 0 void SetIPAddr( const SteamNetworkingIPAddr &addr ); // 设置为指定的 IP:端口 const SteamNetworkingIPAddr *GetIPAddr() const; // 如果我们不是 IP 地址,则返回 null。 // “ localhost”在许多方面等效于“匿名”。 我们的远程端 // 将通过我们使用的网络地址来识别我们。 void SetLocalHost(); // 设置为 localhost。 (为此,我们始终使用IPv6 :: 1,而不是127.0.0.1) bool IsLocalHost() const; // 如果此标识为 localhost,则返回 true。 bool SetGenericString( const char *pszString ); //如果长度无效,则返回 false。 const char *GetGenericString() const; // 如果不是通用字符串类型,则返回 nullptr bool SetGenericBytes( const void *data, size_t cbLen ); // 如果大小无效,则返回 false。 const uint8 *GetGenericBytes( int &cbLen ) const; // 如果不是通用字节类型,则返回 null /// 查看两个标识是否相同 bool operator==(const SteamNetworkingIdentity &x ) const; /// 打印为人可阅读的字符串。 这适用于调试消息 /// 或需要将标识编码为字符串时。 它具有 /// 类似 URL 的格式(类型:<type-data>)。 您的缓冲区应该至少有 /// k_cchMaxString 字节大,以避免被截断。 void ToString( char *buf, size_t cbBuf ) const; /// 解析使用 ToString 生成的字符串 bool ParseString( const char *pszStr ); // 最大大小 enum { k_cchMaxString = 128, // 保存任何标识所需的缓冲区的最大长度,以 ToString 的字符串格式设置 k_cchMaxGenericString = 32, //通用字符串标识的最大字符串长度。 包括终止 '\0' k_cbMaxGenericBytes = 32, }; // [内部表示] };

struct SteamNetworkingIPAddr


存储 IP 和 端口。

始终使用 IPv6;IPv4 使用“IPv4映射”地址表示:IPv4 aa.bb.cc.dd => IPv6 ::ffff:aabb:ccdd。 请参见 RFC 4291 第 2.5.5.2 节)。
struct SteamNetworkingIPAddr { void Clear(); // 将所有都设为 0。 如 [::]:0 bool IsIPv6AllZeros() const; // 如果 IP 为 ::0,则返回 true。 (不检查端口。) void SetIPv6( const uint8 *ipv6, uint16 nPort ); // 设置 IPv6 地址。 IP 被解读为字节,因此没有字节序问题 (与 inaddr_in6相同)。IP 可以是映射的 IPv4 地址。 void SetIPv4( uint32 nIP, uint16 nPort ); // 设置为 IPv4 映射地址。 IP 和端口按主机字节顺序排列。 bool IsIPv4() const; // 如果 IP 被映射为 IPv4,则返回 true uint32 GetIPv4() const; // 以主机字节顺序返回 IP(如 aa.bb.cc.dd 为 0xaabbccdd)。 如果 IP 未映射到 IPv4,则返回 0。 void SetIPv6LocalHost( uint16 nPort = 0); // 设置为 IPv6 本地主机地址 ::1 和指定的端口。 bool IsLocalHost() const; // 如果此身份为 localhost,则返回 true。 (要么是 IPv6 :: 1,要么是 IPv4 127.0.0.1) // 容纳使用 ToString 定义格式的 IP 所需的缓冲区的最大长度, // 包括 '\ 0'([0123:4567:89ab:cdef:0123:4567:89ab:cdef]:12345) enum { k_cchMaxString = 48 }; /// 打印到带有或不带有端口的字符串。 映射的 IPv4 地址打印为 /// 点分十进制(12.34.56.78),否则将根据 RFC5952 /// 打印规范格式。 如果您包含端口,则 IPv6 将用方括号 /// 括起来,例如 [::1:2]:80。 您的缓冲区至少应为 k_cchMaxString 字节, /// 以避免被截断 inline void ToString( char *buf, size_t cbBuf, bool bWithPort ) const; /// 解析 IP 地址和可选端口。 如果不存在端口,则将其设置为0。 /// (这意味着您无法确定是否明确指定了零端口。) inline bool ParseString( const char *pszStr ); union { uint8 m_ipv6[]; struct // IPv4 “映射地址” (rfc4038 section 4.2) { uint64 m_8zeros; uint16 m_0000; uint16 m_ffff; uint8 m_ip[]; // 注意:作为字节,也即网络字节顺序 } m_ipv4; }; uint16 m_port; // 主机字节顺序 /// 查看两个地址是否相同 bool operator==(const SteamNetworkingIPAddr &x ) const; };

连接状态

这些类型用于描述连接状态。 请查看 ISteamnetworkingSockets::GetConnectionInfoSteamNetConnectionStatusChangedCallback_t

enum ESteamNetworkingState

枚举高级连接状态。

enum ESteamNetworkingConnectionState { /// 虚拟值,用于指示 API 中的错误情况。 /// 指定的连接不存在或已经关闭。 k_ESteamNetworkingConnectionState_None = 0, /// 我们正在尝试确定对等端是否可以彼此通信, /// 是否希望彼此通信,执行基本身份验证 /// 以及交换密钥。 /// /// - 对于“客户端”端的连接(本地发起): /// 我们正在尝试建立连接。 /// 视连接类型而定,我们可能不知道它们是谁。 /// 请注意,无法确定我们是 /// 在网络上等待完成握手数据包, /// 还是等待应用程序层接受连接。 /// /// - 对于“服务器”端的连接(通过侦听套接字接受): /// 我们已经完成了一些基本的握手,并且客户提供了 /// 一些身份证明。 准备使用 AcceptConnection() /// 接受连接。 /// /// 在这两种情况下,几乎肯定会丢弃现在发送的任何 /// 不可靠的数据包。 尝试接收数据包肯定会失败。 /// 如果发送模式允许将消息排队,则可以发送消息。 /// 但是,如果在实际建立连接之前关闭连接, /// 则所有排队的消息将立即被丢弃。 /// (我们不会尝试刷新队列并确认传递到远程主机, /// 这通常是在关闭连接时发生的。) k_ESteamNetworkingConnectionState_Connecting = 1, /// 某些连接类型使用反向通道或 /// 受信任的第三方进行最早的通信。 如果服务器接受连接, /// 则这些连接将切换到集合状态。 在此状态下, /// 在此状态下,我们仍未建立端对端路由(通过中继网络), /// 因此,如果您发送任何不可靠的消息, /// 它们将被丢弃。 k_ESteamNetworkingConnectionState_FindingRoute = 2, /// 我们已经收到了对等端的通信(我们也知道其是谁), /// 一切良好。 如果您现在关闭连接, /// 我们将尽最大努力清除对等端未确认的 /// 任何可靠的已发送数据。 (但是请注意, /// 这是在应用程序进程中发生的,因此与 TCP 连接不同, /// 您并未完全将其交给操作系统来处理。) k_ESteamNetworkingConnectionState_Connected = 3, /// 对等端已关闭连接,但本地未关闭。 /// 从 API 角度来看,该连接仍然存在。 您必须关闭句柄 /// 以释放资源。 如果入站队列中有任何消息, /// 则可以检索它们。 否则,除非关闭连接, /// 就无法执行任何操作。 /// /// 此统计信息与 TCP 状态机中的 CLOSE_WAIT 相似。 k_ESteamNetworkingConnectionState_ClosedByPeer = 4, /// 在本地检测到连接中断。 (例如超时, /// 本地互联网连接中断等) /// /// 从 API 角度来看,该连接仍然存在。 您必须关闭句柄 /// 以释放资源。 /// /// 尝试发送更多消息将失败。 队列所有剩余的收到的 /// 消息均可用。 k_ESteamNetworkingConnectionState_ProblemDetectedLocally = 5, // // 以下值在内部使用,并且不会由任何 API 返回。 // 我们将其记录在此,以便于了解后台使用的 // 状态机。 // /// 我们已经断开了连接,从 API 角度看,连接已关闭。 /// 不能再发送或接收更多数据。 所有可靠的数据都已清除, /// 或者已被我们放弃并丢弃。 我们还不确定是否对等端知道 /// 连接已经关闭,所以目前会呆在原处, /// 如果确实从他们那里收到数据包,则可以向他们发送适当的数据包, /// 以便其知道为什么连接已关闭 (不必依靠超时, /// 不然看起来好像出了什么问题)。 k_ESteamNetworkingConnectionState_FinWait = -1, /// 我们已经断开了连接,从 API 角度看,连接已关闭。 /// 不能再发送或接收更多数据。 但是,从网络的角度来看, /// 我们尚未向对等端发出任何连接已关闭的指示。 /// 我们正在清除可靠数据的最后一部分。 完成此操作后, /// 我们将通知对等端该连接已关闭, /// 并转换为 FinWait 状态。 /// /// 请注意,在刷新数据之前,不会向远程主机 /// 发出已关闭连接的指示。 如果远程主机尝试向我们发送数据,我们将 /// 采取一切必要措施使连接保开启状态,直到可以正确关闭连接为止。 /// 但是实际上,数据将被丢弃,因为应用程序无法 /// 将其读回。 通常,这不是问题,因为利用延迟功能的应用程序协议 /// 是为远程主机设计的,以使其在发送更多数据之前 /// 等待响应。 k_ESteamNetworkingConnectionState_Linger = -2, /// 连接完全无效,已就绪待毁 k_ESteamNetworkingConnectionState_Dead = -3, k_ESteamNetworkingConnectionState__Force32Bit = 0x7fffffff };

enum ESteamNetConnectionEnd


枚举各种导致连接终止的原因。 它们的设计原理类似于 HTTP 错误代码:它们是一个数字范围,为您提​​供了有关问题来源的粗略分类。
enum ESteamNetConnectionEnd { // 无效/sentinel 值 k_ESteamNetConnectionEnd_Invalid = 0, // // 应用代码。 这些是您将传给 // ISteamNetworkingSockets::CloseConnection 的值。 如果要浏览特定于应用程序的原因代码, // 可以使用这些代码。 如果不需要 // 此功能,可随时传入 // k_ESteamNetConnectionEnd_App_Generic。 // // 如果发现“正常”和“异常”终止之间的区别 // 有用的话,您可以使用,但您并不是 // 必须这样做。 我们区分正常和异常的 // 唯一地方是连接分析。 如果很大一部分 // 连接以异常方式终止, // 则可能会触发警报。 // // 1xxx: 应用程序以“正常”方式终止了连接。 // 例如:用户故意与服务器断开连接, // 游戏正常结束等。 k_ESteamNetConnectionEnd_App_Min = 1000, k_ESteamNetConnectionEnd_App_Generic = k_ESteamNetConnectionEnd_App_Min, // 使用此范围内的代码表示“正常”断开连接 k_ESteamNetConnectionEnd_App_Max = 1999, // 2xxx: 应用程序以某种异常或 // 或不寻常的方式终止了连接,这可能表明存在错误 // 或配置问题。 // k_ESteamNetConnectionEnd_AppException_Min = 2000, k_ESteamNetConnectionEnd_AppException_Generic = k_ESteamNetConnectionEnd_AppException_Min, // 使用此范围内的代码表示“异常”断开连接 k_ESteamNetConnectionEnd_AppException_Max = 2999, // // 系统代码。 当连接状态为 // k_ESteamNetworkingConnectionState_ClosedByPeer 或 // k_ESteamNetworkingConnectionState_ProblemDetectedLocally 时,将由系统返回。 将此 // 将此范围内的代码传递给 ISteamNetworkingSockets::CloseConnection // 是非法的。 // 3xxx:由于本地主机或其与网络的 // 连接问题,连接失败或结束。 k_ESteamNetConnectionEnd_Local_Min = 3000, // 您无法做您想做的事情,因为您正在离线模式下运行。 k_ESteamNetConnectionEnd_Local_OfflineMode = 3001, // 我们无法联系许多(也许是全部)中继。 // 由于它们不可能同时全部脱机, // 因此最好的解释是问题出现在我们这端。 请注意,我们不会 // 区分“许多”和“全部”,因为在实践中, // 检测连接问题需要时间, // 并且在连接超时之前, // 即使我们能够同时联系它们, // 也可能无法主动探测所有的中继群。 因此,此代码仅表示: // // * 我们最近没有与任何中继成功通信。 // * 我们有证据表明最近无法与多个中继通信。 k_ESteamNetConnectionEnd_Local_ManyRelayConnectivity = 3002, // 托管服务器在与客户端使用的中继通信时 // 遇到问题,因此问题很可能 // 出在我们这端。 k_ESteamNetConnectionEnd_Local_HostedServerPrimaryRelay = 3003, // 我们无法获取网络配置。这*几乎* // 总是本地问题,因为网络配置来自 // CDN,而 CDN 非常可靠。 k_ESteamNetConnectionEnd_Local_NetworkConfig = 3004, // Steam 拒绝了我们的请求,因为我们没有 // 这样做的权力。 k_ESteamNetConnectionEnd_Local_Rights = 3005, k_ESteamNetConnectionEnd_Local_Max = 3999, // 4xxx:链接失败或结束,而且看起来原因 // 和本地主机或其网络连接 // 没有关系。 可能是由 // 远程主机造成的,也可能是在中间出了问题。 k_ESteamNetConnectionEnd_Remote_Min = 4000, // 连接已丢失,据我们所知, 与相关服务(中继) // 的连接并未中断。 这并不意味着 // 问题是“他们的错”,仅意味着看起来 // 我们这端没有网络问题。 k_ESteamNetConnectionEnd_Remote_Timeout = 4001, // 您提供给我的证书或加密握手信息有一部分无效, // 我不理解或不喜欢您的密钥类型 // 等。 k_ESteamNetConnectionEnd_Remote_BadCrypt = 4002, // 您提供了我可以解析的证书,*从技术上来讲*, // 我们可以使用加密通信。 // 但是有一个问题使我无法检查您的标识 // 或无法确保中间的人看不到我们的交流。 // 例如:- 丢失了 CA 密钥(并且我不接受未签名的证书) // - CA 密钥不是我信任的密钥, // - 证书未受到应用、用户、时间、数据中心等的适当限制。 // - 证书并不是发放给您的。 // - 其他 k_ESteamNetConnectionEnd_Remote_BadCert = 4003, // 我们无法与远程主机会合, // 因为其未登录 Steam。 k_ESteamNetConnectionEnd_Remote_NotLoggedIn = 4004, // 我们无法与远程主机会合, // 因为其没有运行正确的应用程序。 k_ESteamNetConnectionEnd_Remote_NotRunningApp = 4005, // 您使用的协议版本出了点问题。 // (可能您正在运行的代码太旧了。) k_ESteamNetConnectionEnd_Remote_BadProtocolVersion = 4006, k_ESteamNetConnectionEnd_Remote_Max = 4999, // 5xxx: 由于其他原因导致连接失败。 k_ESteamNetConnectionEnd_Misc_Min = 5000, // 连接失败的原因不一定是某个软件导致的, // 但其较为少见,不值得为其单独撰写 UI // 或创建本地化消息。 // 调试字符串种应当包含更多详情。 k_ESteamNetConnectionEnd_Misc_Generic = 5001, // 一般故障,很可能是软件错误。 k_ESteamNetConnectionEnd_Misc_InternalError = 5002, // 和远程主机的连接超时, // 但无法确定问题是出现在我端、中间 // 还是他端。 k_ESteamNetConnectionEnd_Misc_Timeout = 5003, // 在和相关中继通信时遇到问题。 // 我们没有足够的信息来确定问题 // 是否出现在我端。 k_ESteamNetConnectionEnd_Misc_RelayConnectivity = 5004, // 和 Steam 通信时遇到问题。 k_ESteamNetConnectionEnd_Misc_SteamConnectivity = 5005, // 处于专用托管状态的服务器没有可以用来和客户端对话的 // 仍在进行的中继会话。 (打开并 // 维持这些会话是客户端的工作。) k_ESteamNetConnectionEnd_Misc_NoRelaySessionsToClient = 5006, k_ESteamNetConnectionEnd_Misc_Max = 5999, };

SteamNetConnectionInfo_t


描述连接状态。
struct SteamNetConnectionInfo_t { /// 另一端是谁? 视连接类型和连接阶段而定,我们可能无法知晓 SteamNetworkingIdentity m_identityRemote; /// 由本地应用程序代码设置的任意用户数据 int64 m_nUserData; /// 侦听已连接的套接字的句柄,如果我们发起了连接,则为 k_HSteamListenSocket_Invalid HSteamListenSocket m_hListenSocket; /// 远程地址。 如果我们不知道地址,或者地址为 N/A,可能全部为 0。 /// (如除直接 UDP 连接外,几乎所有其他内容。) SteamNetworkingIPAddr m_addrRemote; uint16 m__pad1; /// 远程主机位于哪个数据中心? (如果不知道,则为0。) SteamNetworkingPOPID m_idPOPRemote; /// 我们使用什么中继与远程主机通信? /// (如果不适用,则为0。) SteamNetworkingPOPID m_idPOPRelay; /// 连接的高级状态 ESteamNetworkingConnectionState m_eState; /// 连接终止或问题的基本原因。 /// 有关使用的值,请参见 ESteamNetConnectionEnd int m_eEndReason; /// 对连接终止或问题的可阅读但 /// 未本地化的解释。 仅用于调试/诊断目的, /// 不用于显示给用户。 它可能包含 /// 一些特定于该问题的详情。 char m_szEndDebug[]; /// 调试描述。 这包括连接句柄, /// 连接类型(和对等端信息)以及应用名称。 /// 此字符串在各种内部日志记录消息中使用 char m_szConnectionDescription[]; /// 内部内容,可以轻松更改 API uint32 reserved[64]; };

SteamNetworkingQuickConnectionStatus

快速连接状态,简化为可以更频繁地调用的内容,而不会造成太大的性能问题。

struct SteamNetworkingQuickConnectionStatus { /// 连接的高级状态 ESteamNetworkingConnectionState m_eState; /// 当前 ping(ms) int m_nPing; /// 本地测得的连接质量,0...1。 (按顺序端到端传递的 /// 数据包的百分比)。 float m_flConnectionQualityLocal; /// 从远程主机观察到的数据包传递成功率 float m_flConnectionQualityRemote; /// 最近历史记录中的当前数据速率。 float m_flOutPacketsPerSec; float m_flOutBytesPerSec; float m_flInPacketsPerSec; float m_flInBytesPerSec; /// 估计我们认为可以将数据发送给对等端的速率。 /// 请注意,这可能大大高于 m_flOutBytesPerSec, /// 这意味着通道的容量高于您发送的数据量。 /// (没关系!) int m_nSendRateBytesPerSecond; /// 待发送的字节数。 这是您最近请求发送 /// 但尚未实际发送到网络的数据。 可靠的 /// 数字也包含以前放在网上的数据, /// 但现在已安排好重传。 因此, /// 即使没有进行调用以在检查之间发送可靠数据,也有可能 /// 观察到两次检查之间的 m_cbPendingReliable 增加。 等待 /// Nagle 延迟的数据将显示在这些数字中。 int m_cbPendingUnreliable; int m_cbPendingReliable; /// 已放置在网络上但尚未收到确认的 /// 可靠数据的字节数,因此我们可能必须 /// 重新传输。 int m_cbSentUnackedReliable; /// 如果您要求我们立即发送一条消息,那么在我们实际开始 /// 将数据包放入网络之前,该消息会在队列中停留多长时间? /// (并且假设 Nagle 不会导致任何数据包被延迟。) /// /// 通常,应用程序发送的数据受到 /// 通道带宽的限制。 如果您发送数据的速度超过此速度, /// 则必须将其排入队列,并以受到测量的速率连接到网络上。 即使 /// 发送少量数据(例如几个 3k 左右的 MTU),也将需要延迟 /// 一些数据。 /// /// 通常,预计的延迟大约等于 /// /// ( m_cbPendingUnreliable+m_cbPendingReliable ) / m_nSendRateBytesPerSecond /// /// 加上或减去一个 MTU。 这取决于自从将最后一个数据包放入网络以来 /// 过了多少时间。 例如,队列可能*刚刚*被清空, /// 最后一个数据包也已放置在网络上,而我们恰好达到了 /// 发送速率限制。 在这种情况下,我们可能需要等待一个数据包的时间过去, /// 然后才能再次发送。 另一方面,队列中可能包含等待 /// Nagle 的数据。 (这将始终少于一个数据包,因为一旦 /// 有了完整的数据包,我们将立即发送它。)在这种情况下,我们可能已 /// 准备发送数据,并且此值将为 0。 SteamNetworkingMicroseconds m_usecQueueTime; };

网络消息


const int k_cbMaxSteamNetworkingSocketsMessageSizeSend = 512 * 1024;
我们可以发送的单个消息的最大大小。
注意:我们及我们的对等端可能愿意接收更大的消息。

SteamNetworkingMessage_t


已收到的一条消息
struct SteamNetworkingMessage_t { /// 消息负载 void *m_pData; /// 负载的大小。 int m_cbSize; /// 对于在连接上收到的消息:它来自什么连接? /// 对于外发消息:将其发送到哪个连接? /// 在使用 ISteamNetworkingMessages 接口时不使用 HSteamNetConnection m_conn; /// 对于入站消息:谁发送给我们的? /// 对于连接上的出站消息:未使用。 /// 对于临时 ISteamNetworkingMessages 接口上的出站消息:我们应该将其发送给谁? SteamNetworkingIdentity m_identityPeer; /// 对于在连接上收到的消息,这是与 /// 连接关联的用户数据。 /// /// *通常*与调用 GetConnection() 然后获取 /// 与该连接关联的用户数据相同,但是存在 /// 以下细微差别: /// /// - 此用户数据将与 API 返回消息时 /// 捕获的连接的用户数据匹配。 /// 如果随后更改了连接上的用户数据, /// 则不会更新该数据。 /// - 这是一个内联调用,因此*快得多*。 /// - 您可能已经关闭了连接,因此无法 /// 获取用户数据。 /// /// 不用于发送消息, int64 m_nConnUserData; /// 收到消息的本地时间戳 /// 不用于出站消息。 SteamNetworkingMicroseconds m_usecTimeReceived; /// 发件人分配的消息号。 /// 不用于出站消息。 int64 m_nMessageNumber; /// 用于释放 m_pData 的函数。 之所以存在这种机制,是为了 /// 应用可以使用从自己的堆分配的缓冲区创建消息, /// 然后将其传到库中。 此函数 /// 通常类似于: /// /// free( pMsg->m_pData ); void (*m_pfnFreeData)( SteamNetworkingMessage_t *pMsg ); /// 用于减少内部引用计数的函数,如果 /// 该值为零,则释放消息。 您不应该设置此函数指针, /// 或者需要直接访问它! 请改用 Release() 函数! void (*m_pfnRelease)( SteamNetworkingMessage_t *pMsg ); /// 使用 ISteamNetworkingMessages 时,接收消息的通道号 /// (不用于“连接”上发送或接收的消息) int m_nChannel; /// k_nSteamNetworkingSend_xxx 标记的位掩码。 /// 对于收到的消息,只有 k_nSteamNetworkingSend_Reliable 位有效。 /// 对于出站消息,所有位都有效 int m_nFlags; /// 使用 ISteamNetworkingUtils::AllocateMessage 和 /// ISteamNetworkingSockets::SendMessage 发送消息时可以使用的任意用户数据。 ///(您在 m_pfnFreeData 中设置的回调可能会使用此字段。) /// /// 不用于接收的消息。 int64 m_nUserData; /// 处理完对象,释放内存等时, /// 必须调用此函数。 inline void Release(); };

用于消息发送的标记

这些值在位掩码参数中用于诸如 ISteamNetworkingSockets::SendMessageToConnection 之类的函数。

const int k_nSteamNetworkingSend_Unreliable = 0;
消息的发送不可靠。 可能会丢失。 消息*可以*大于单个 MTU(UDP 数据包),但是没有重新传输,因此,如果丢失任何一条消息,整个消息都将被丢弃。

发送 API 确实对基础连接有一定了解,因此如果没有完成 NAT 遍历或已经识别到一个在连接上发生的调整,数据包将会被批处理,直至连接再次打开。

迁移说明:这与 k_EP2PSendUnreliable 不完全相同! 您可能想要使用 k_ESteamNetworkingSendType_UnreliableNoNagle

const int k_nSteamNetworkingSend_NoNagle = 1;
禁用 Nagle 的算法

默认情况下,Nagle 的算法将应用于所有出站消息。 这意味着该消息将不会立即发送,以防您在发送后不久又发送了更多消息;这些消息可以组合在一起发送。 任何时候只要有足够的缓冲数据来填充数据包,这些数据包就会被立即送出,但是直到 Nagle 计时器结束时,才会发送并未全满的数据包。 请参阅 ISteamNetworkingSockets::FlushMessagesOnConnectionISteamNetworkingMessages::FlushMessagesToUser

注意:不要因为您希望数据包“更快到达”,就在不使用 Nagle 的情况下发送所有消息。 禁用它之前,请确保您了解 Nagle 正在解决的问题。 如果您要发送较小的消息(通常是同时发送多个消息),那么启用 Nagle 很有可能会更有效。 此标记的典型正确用法是,当您发送的消息是一段时间内发送的最后一条消息(例如,服务器模拟中的最后一条消息发送给特定的客户端)时,您可以使用此标记刷新所有消息。

const int k_nSteamNetworkingSend_UnreliableNoNagle = k_nSteamNetworkingSend_Unreliable|k_nSteamNetworkingSend_NoNagle;
消息的发送不可靠,绕过该消息以及 Nagle 计时器上当前待处理的任何消息的 Nagle 算法。 这等效于使用 steamnetworkingtypes::k_ESteamNetworkingSend_Unreliable,然后立即使用 ISteamNetworkingSockets::FlushMessagesOnConnection ISteamNetworkingMessages::FlushMessagesToUser 刷新消息。 (但是,使用此标记效率更高,因为您只进行一次 API 调用。)

const int k_nSteamNetworkingSend_NoDelay = 4;
如果消息不能很快发送(因为连接仍在进行初始握手、路由协商等),则只需将其丢弃。 这仅适用于不可靠的消息。 在可靠消息上使用此标记无效。


const int k_nSteamNetworkingSend_UnreliableNoDelay = k_nSteamNetworkingSend_Unreliable|k_nSteamNetworkingSend_NoDelay|k_nSteamNetworkingSend_NoNagle;
发送不可靠的消息,但是如果不能相对快速地发送消息,请丢弃它,而不是让其排队。 这对于延迟时间过长便无用的消息(例如语音数据)而言很有用。

在以下情况下,将丢弃一条消息:
  • 连接不完全。 (例如,“正在连接”或“查找路线”状态)。
  • 已经有非常多的消息在排队,以至于在大约 200 毫秒左右的时间内不会将当前消息放到网络上。

如果由于这些原因而丢弃了一条消息,则将返回 k_EResultIgnored。

注意:不使用 Nagle 算法,并且如果未丢弃该消息,则将立即刷新所有在 Nagle 计时器上等待的消息。

const int k_nSteamNetworkingSend_Reliable = 8;
可靠的消息发送。 最多可以在一条消息中发送 k_cbMaxSteamNetworkingSocketsMessageSizeSend 个字节。 可在后台对消息进行碎片化或重新合并,以及用滑动窗口高效发送大型数据块。

使用 Nagle 算法。 有关更多详细信息,请参见 k_ESteamNetworkingSendType_Unreliable 上的说明。 See k_ESteamNetworkingSendType_ReliableNoNagle, ISteamNetworkingSockets::FlushMessagesOnConnection, ISteamNetworkingMessages::FlushMessagesToUser

迁移说明:这与 k_EP2PSendReliable 不同,而更像是 k_EP2PSendReliableWithBuffering

const int k_nSteamNetworkingSend_ReliableNoNagle = k_nSteamNetworkingSend_Reliable|k_nSteamNetworkingSend_NoNagle;
消息的发送可靠,但绕过 Nagle 的算法。

迁移说明:这等效于 k_EP2PSendReliable

Ping 位置/测量

ISteamNetworkingutils 具有估算两个网络主机之间的 ping 时间而不发送任何数据包的函数。

SteamNetworkingPingLocation_t


struct SteamNetworkPingLocation_t { uint8 m_data[]; }
不透明的对象,它描述了网络上“位置”的详细信息,即使我们无法在主机之间建立直接路由,并且必须通过 Steam 数据报中继网络连接,也可以合理地估计两个主机之间 ping 的上限,

它不包含任何标识主机的信息。 的确,如果两个主机位于同一建筑物中,或者具有几乎相同的网络特性,则对两个主机使用相同的位置对象是有效的。

注意:此对象只能在同一流程中使用! 请勿序列化,通过网络发送它或将其保存在文件或数据库中! 如果需要这样做,请使用 ISteamNetworkingUtils 中的函数将其转换为字符串表示形式

k_cchMaxSteamNetworkingPingLocationString


const int k_cchMaxSteamNetworkingPingLocationString = 1024;
Ping 位置的最大可能长度,以字符串格式表示。 这是一个非常保守的最坏情况值,也为将来的语法增强保留了余地。 实际上,大多数字符串都短得多。 如果要存储其中的许多内容,则使用动态内存很有可能会让您受益良多。

配置

您可以在全局或特定连接的基础上控制不同的操作参数。 执行此操作的方法在 ISteamNetworkingUtils

ESteamNetworkingConfigScope


可以将配置值应用于不同类型的对象。
enum ESteamNetworkingConfigScope { /// 获取/设置全局选项,或默认设置。 就算是适用于更具体作用域的选项也具有 /// 全局作用域,您可能可以只更改全局默认值。 例如, /// 如果每个连接都需要不同的设置,则需要在 /// 更具体的作用域内设置这些选项。 k_ESteamNetworkingConfig_Global = 1, /// 有些选项是针对某个特定接口的。 请注意,所有连接 /// 和侦听套接字设置也可以在接口级别进行设置, /// 它们将应用于通过这些接口创建的对象。 k_ESteamNetworkingConfig_SocketsInterface = 2, /// 侦听套接字的选项。 如果您有多个侦听套接字,并且它们 /// 都使用相同的选项,则可以在接口层设置侦听套接字选项。 /// 您还可以在侦听套接字上设置连接选项,它们为通过此 /// 侦听套接字接受的所有连接设置默认值。 (如果您未设置 /// 连接选项,则将使用它们。) k_ESteamNetworkingConfig_ListenSocket = 3, /// 特定连接的选项。 k_ESteamNetworkingConfig_Connection = 4, };

ESteamNetworkingConfigDataType


不同的配置值具有不同的数据类型
enum ESteamNetworkingConfigDataType { k_ESteamNetworkingConfig_Int32 = 1, k_ESteamNetworkingConfig_Int64 = 2, k_ESteamNetworkingConfig_Float = 3, k_ESteamNetworkingConfig_String = 4, k_ESteamNetworkingConfig_FunctionPtr = 5, // 注意:设置回调时,应将指针放入变量中,并将指针传给该变量。 };

ESteamNetworkingConfigValue


配置选项
enum ESteamNetworkingConfigValue { k_ESteamNetworkingConfig_Invalid = 0, /// [global float, 0--100] 随机丢弃 N% 的数据包,而不是发送/接收 /// 这只是一个全局选项,因为它应用于 /// 我们没有太多上下文的低级别 k_ESteamNetworkingConfig_FakePacketLoss_Send = 2, k_ESteamNetworkingConfig_FakePacketLoss_Recv = 3, /// [global int32]。 将所有出站/入站数据包延迟 N ms k_ESteamNetworkingConfig_FakePacketLag_Send = 4, k_ESteamNetworkingConfig_FakePacketLag_Recv = 5, /// [global float] 0-100 百分比的数据包,我们将为其增加额外的延迟 /// (将导致重新排序) k_ESteamNetworkingConfig_FakePacketReorder_Send = 6, k_ESteamNetworkingConfig_FakePacketReorder_Recv = 7, /// [global int32] 额外的延迟(以毫秒为单位),应用于重新排序的数据包。 k_ESteamNetworkingConfig_FakePacketReorder_Time = 8, /// [global float 0--100] 全局复制我们发送的一定比例的数据包 k_ESteamNetworkingConfig_FakePacketDup_Send = 26, k_ESteamNetworkingConfig_FakePacketDup_Recv = 27, /// [global int32] 延迟重复数据包的延迟量(以毫秒为单位)。 /// (我们会选择介于 0 和此值之间的随机延迟) k_ESteamNetworkingConfig_FakePacketDup_TimeMax = 28, /// [connection int32] 首次连接时使用的超时值(以毫秒为单位) k_ESteamNetworkingConfig_TimeoutInitial = 24, /// [connection int32] 建立连接后要使用的超时值(以毫秒为单位) k_ESteamNetworkingConfig_TimeoutConnected = 25, /// [connection int32] 要发送的缓冲暂挂字节的上限, /// 如果达到此上限,SendMessage 将返回 k_EResultLimitExceeded /// 默认值为 512k(524288字节) k_ESteamNetworkingConfig_SendBufferSize = 9, /// [connection int32] 最小/最大发送速率钳位,0 为无限制。 /// 该值将控制带宽估计值所允许达到的 /// 最小/最大发送速率。 默认值为 0(无限制) k_ESteamNetworkingConfig_SendRateMin = 10, k_ESteamNetworkingConfig_SendRateMax = 11, /// [connection int32] Nagle 时间,以微秒为单位。 调用 SendMessage 时,如果 /// 传出消息小于 MTU 的大小,则它将排队等待一个 /// 等于 Nagle 计时器值的延迟。 这是为了确保 /// 如果应用程序快速发送了几个小消息, /// 它们会合并为一个数据包。 /// 参见历史 RFC 896。 值以微秒为单位。 /// 默认值为 5000us(5ms)。 k_ESteamNetworkingConfig_NagleTime = 12, /// [connection int32] 不要自动使没有强身份验证的 /// IP 连接失败 。 在客户端上,这意味着即使我们不知道自己的身份或 /// 无法获得证书,我们也将尝试连接。 在服务器上, /// 这意味着我们不会由于身份验证失败而自动拒绝连接。 /// (您可以检查传入的连接并决定是否接受它。) k_ESteamNetworkingConfig_IP_AllowWithoutAuth = 23, // // SDR 中继连接的设置 // /// [int32 global] 如果对端口的前 N 个 ping 操作均失败,请将该端口标记为一段时间内不可用, /// 然后尝试其他端口。 某些 ISP 和路由器可能会丢弃第一个 /// 数据包,因此将其设置为 1 可能会严重干扰通信。 k_ESteamNetworkingConfig_SDRClient_ConsecutitivePingTimeoutsFailInitial = 19, /// [int32 global] 如果在成功接收到通信后,对端口的 N 次 /// 连续 ping 失败,则将该端口标记为一段时间不可用, /// 然后尝试其他端口。 k_ESteamNetworkingConfig_SDRClient_ConsecutitivePingTimeoutsFail = 20, /// [int32 global] 在我们认为我们的估计是可靠的之前,我们在所有时间里需要发送的最小 /// ping 数。 由于NAT、路由器没有最佳路由等原因,对每个群集的 /// 第一次 ping 通常会延迟。 在我们发送足够数量的 ping 之前, /// 我们的估计通常是不准确的。 继续执行 ping 操作,直到我们收到 /// 这么多的 ping 为止。 k_ESteamNetworkingConfig_SDRClient_MinPingsBeforePingAccurate = 21, /// [int32 global] 将所有 Steam 数据报流量设置为源自同一 /// 本地端口。 默认情况下,我们会为每个中继打开一个新的 UDP 套接字 /// (在不同的本地端口上)。 这是稍微不够理想的选择,但是它可以绕过 /// 某些不能正确实现 NAT 的路由器的问题。 如果您在与 /// 中继进行对话时遇到可能和 NAT 相关的间歇性问题,请尝试切换 /// 此标记。 k_ESteamNetworkingConfig_SDRClient_SingleSocket = 22, /// [global string] 强制使用的中继集群代码。 如果不为空,我们将 /// 仅在该集群中使用中继。 如, “iad” k_ESteamNetworkingConfig_SDRClient_ForceRelayCluster = 29, /// [connection string] 为了进行调试,请使用指定的游戏服务器地址生成 /// 我们自己的(未签名)票证。 必须将路由器配置为接受未签名的 /// 票证。 k_ESteamNetworkingConfig_SDRClient_DebugTicketAddress = 30, /// [global string] 用于调式。 覆盖具有此设置的配置中的中继列表 ///(可能只有一个)。 用逗号分隔列表。 k_ESteamNetworkingConfig_SDRClient_ForceProxyAddr = 31, // // 调试信息的日志级别。 较高的优先级 // (较低的数值)将会导致更多内容被打印。 // k_ESteamNetworkingConfig_LogLevel_AckRTT = 13, // [connection int32] 内联 ping 和回复的 RTT 计算 k_ESteamNetworkingConfig_LogLevel_PacketDecode = 14, // [connection int32] 日志 SNP 数据包发送 k_ESteamNetworkingConfig_LogLevel_Message = 15, // [connection int32] 记录每条消息的发送/接收 k_ESteamNetworkingConfig_LogLevel_PacketGaps = 16, // [connection int32] 丢弃的数据包 k_ESteamNetworkingConfig_LogLevel_P2PRendezvous = 17, // [connection int32] P2P 集合消息 k_ESteamNetworkingConfig_LogLevel_SDRRelayPings = 18, // [global int32] Ping 中继 };

ESteamNetworkingGetConfigValueResult


ISteamNetworkintgUtils::GetConfigValue 的返回值
enum ESteamNetworkingGetConfigValueResult { k_ESteamNetworkingGetConfigValue_BadValue = -1, //没有这样的配置值 k_ESteamNetworkingGetConfigValue_BadScopeObj = -2, // 错误的连接句柄等 k_ESteamNetworkingGetConfigValue_BufferTooSmall = -3, // 无法将结果放入缓冲区 k_ESteamNetworkingGetConfigValue_OK = 1, k_ESteamNetworkingGetConfigValue_OKInherited = 2, //此级别未设置值,但返回了有效(继承)的值。 k_ESteamNetworkingGetConfigValueResult__Force32Bit = 0x7fffffff };

SteamNetworkingConfigValue_t


在某些地方,我们需要在侦听套接字和连接上设置配置选项,并使它们在侦听套接字或连接真正开始执行任何操作之前生效。 创建对象然后在创建后“立即”设置选项不能完全起作用,因为在创建对象时与应用选项之间可能会收到网络数据包。 为了在创建时以可靠的方式设置选项,必须将它们传给创建函数。 此结构用于传入这些选项。

有关这些字段的含义,请参见 ISteamNetworkingUtils::SetConfigValue。 基本上,在创建对象时,我们仅循环访问选项列表并调用 ISteamNetworkingUtils::SetConfigValueStruct,其中范围参数由所创建的对象提供。
struct SteamNetworkingConfigValue_t { /// 正在设置哪个选项 ESteamNetworkingConfigValue m_eValue; /// 您填写了以下哪个字段? ESteamNetworkingConfigDataType m_eDataType; /// 选项值 union { int32_t m_int32; int64_t m_int64; float m_float; const char *m_string; // 指向 '\0'-terminated 的缓冲区 void *m_functionPtr; } m_val; };

Valve 数据中心

用于描述 Valve 网络的拓扑,识别数据中心等。

typedef uint32 SteamNetworkingPOPID;
用于网络位置存在点的标识符 (例如 Valve 数据中心)。
通常,您无需对这些进行直接操作。

SteamNetworkingPOPID CalculateSteamNetworkingPOPIDFromString( const char *pszCode )
将 3 或 4 个字符的 ID 转换为 32 位整数。

template <int N> inline void GetSteamNetworkingLocationPOPStringFromID( SteamNetworkingPOPID id, char (&szCode)[N] )
将整数解压缩为字符串表示形式,包括终止'\ 0'。 您的字符串至少应为5个字节。 (若非如此,它将无法编译。)[/td]