网络 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::GetConnectionInfo,
SteamNetConnectionStatusChangedCallback_tenum 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::FlushMessagesOnConnection,ISteamNetworkingMessages::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_EP2PSendReliablePing 位置/测量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] |