SDL3_API分类参考_SDL_net 核心 API 集合(CategorySDLNet)

2026-3-7 / 0 评论 / 6 阅读

SDL_net 核心 API 集合(CategorySDLNet)

SDL_net 是 SDL 官方的网络编程扩展库,它对 BSD Sockets、WinSock 等系统级网络 API 进行了轻量级封装,核心设计目标是简化网络编程复杂度、处理各类边界情况,让开发者无需关注底层协议细节即可实现网络通信。其核心设计理念包括:全异步非阻塞接口(可显式等待操作完成)、抽象化地址管理(屏蔽不同网络协议差异)、简洁优先且不失功能完整性。


核心概念与使用流程

  1. 初始化:调用 NET_Init() 初始化库,程序退出前调用 NET_Quit() 清理;
  2. 地址处理:通过 NET_ResolveHostname() 解析域名到 NET_Address 对象,或直接构造地址;
  3. 通信模式选择
    • TCP(流套接字):客户端用 NET_CreateClient() 连接服务器,服务器用 NET_CreateServer() 监听并通过 NET_AcceptClient() 接受连接,通过 NET_StreamSocket 收发数据;
    • UDP(数据报套接字):用 NET_CreateDatagramSocket() 创建套接字,通过 NET_SendDatagram()/NET_ReceiveDatagram() 收发数据包;
  4. 异步操作:所有网络操作非阻塞,可通过 NET_WaitUntil*() 系列函数显式等待操作完成;
  5. 测试辅助:通过模拟网络异常函数(如丢包)测试恶劣网络环境下的程序表现。

核心 API 列表(按功能分类)

一、基础初始化与版本管理

  • NET_Init:初始化 SDL_net 库(必须调用,加载底层网络接口)
  • NET_Quit:清理 SDL_net 所有资源(程序退出前必须调用)
  • NET_Version:获取当前 SDL_net 版本信息
  • SDL_NET_MAJOR_VERSION:SDL_net 主版本号宏(如 3 代表 SDL_net 3.x)
  • SDL_NET_MINOR_VERSION:SDL_net 次版本号宏
  • SDL_NET_MICRO_VERSION:SDL_net 修订版本号宏
  • SDL_NET_VERSION:构造版本结构体的宏
  • SDL_NET_VERSION_ATLEAST:编译期检查版本是否满足最低要求

二、网络地址(NET_Address)管理

1. 地址解析与创建

  • NET_ResolveHostname:异步解析域名/IP 到 NET_Address 对象(后台线程执行 DNS 查询)
  • NET_GetLocalAddresses:获取本地主机的所有网络地址(返回 NET_Address 数组)
  • NET_FreeLocalAddresses:释放 NET_GetLocalAddresses 返回的地址数组

2. 地址属性与操作

  • NET_GetAddressString:将 NET_Address 转换为可读的字符串(如 "192.168.1.1:8080")
  • NET_GetAddressStatus:获取地址解析状态(未解析/解析成功/解析失败)
  • NET_CompareAddresses:比较两个 NET_Address 是否指向同一网络地址
  • NET_RefAddress:增加 NET_Address 对象的引用计数(防止被提前释放)
  • NET_UnrefAddress:减少 NET_Address 对象的引用计数,计数为 0 时自动释放

三、TCP 流套接字(StreamSocket)- 客户端

  • NET_CreateClient:创建 TCP 客户端套接字,发起对指定 NET_Address 的连接
  • NET_GetConnectionStatus:获取客户端连接状态(未连接/连接中/已连接/连接失败)
  • NET_WaitUntilConnected:阻塞等待客户端连接完成(支持超时,可非阻塞查询/无限等待)
  • NET_GetStreamSocketAddress:获取流套接字对应的远端 NET_Address
  • NET_GetStreamSocketPendingWrites:获取流套接字待发送的字节数
  • NET_WriteToStreamSocket:向流套接字写入数据(非阻塞,返回实际写入字节数)
  • NET_ReadFromStreamSocket:从流套接字读取数据(非阻塞,返回实际读取字节数)
  • NET_WaitUntilInputAvailable:阻塞等待流套接字有可读数据(支持超时)
  • NET_WaitUntilStreamSocketDrained:阻塞等待套接字待发送数据全部发送完成
  • NET_DestroyStreamSocket:销毁流套接字,关闭连接

四、TCP 服务器(NET_Server)

  • NET_CreateServer:创建 TCP 服务器,监听指定 NET_Address 的端口
  • NET_AcceptClient:接受客户端连接(非阻塞,返回新的 NET_StreamSocket)
  • NET_DestroyServer:销毁服务器,停止监听端口

五、UDP 数据报套接字(DatagramSocket)

  • NET_CreateDatagramSocket:创建 UDP 数据报套接字(无连接,支持点对点通信)
  • NET_SendDatagram:发送 UDP 数据包到指定 NET_Address(非阻塞)
  • NET_ReceiveDatagram:接收 UDP 数据包(非阻塞,返回数据包和发送方地址)
  • NET_DestroyDatagram:销毁接收到的 UDP 数据包,释放内存
  • NET_DestroyDatagramSocket:销毁 UDP 套接字

六、阻塞等待函数(异步操作显式等待)

  • NET_WaitUntilResolved:阻塞等待地址解析完成(支持超时)
  • NET_WaitUntilConnected:阻塞等待客户端连接完成
  • NET_WaitUntilInputAvailable:阻塞等待套接字有可读数据
  • NET_WaitUntilStreamSocketDrained:阻塞等待套接字发送缓冲区排空

七、网络异常模拟(测试专用)

  • NET_SimulateAddressResolutionLoss:模拟地址解析失败(测试 DNS 异常)
  • NET_SimulateStreamPacketLoss:模拟 TCP 流数据包丢失(测试网络不稳定)
  • NET_SimulateDatagramPacketLoss:模拟 UDP 数据包丢失(测试丢包场景)

八、状态枚举(NET_Status)

  • 核心状态值(隐含在 NET_Status 枚举中):
    • NET_STATUS_INVALID:无效状态(未初始化/已销毁)
    • NET_STATUS_RESOLVING:地址解析中
    • NET_STATUS_RESOLVED:地址解析成功
    • NET_STATUS_RESOLVE_FAILED:地址解析失败
    • NET_STATUS_CONNECTING:连接建立中
    • NET_STATUS_CONNECTED:连接已建立
    • NET_STATUS_CONNECT_FAILED:连接建立失败

数据类型(Datatypes)

  • NET_Address:网络地址抽象对象(封装 IP 地址、端口、协议等信息)
  • NET_DatagramSocket:UDP 数据报套接字对象(无连接,用于收发数据包)
  • NET_Server:TCP 服务器对象(监听端口,接受客户端连接)
  • NET_StreamSocket:TCP 流套接字对象(面向连接,可靠的字节流传输)

结构体(Structs)

  • NET_Datagram:UDP 数据包结构体(包含数据缓冲区、数据长度、发送方地址)

宏定义(Macros)

  • SDL_NET_MAJOR_VERSION / SDL_NET_MINOR_VERSION / SDL_NET_MICRO_VERSION:版本号宏
  • SDL_NET_VERSION:构造 SDL_version 结构体的宏
  • SDL_NET_VERSION_ATLEAST:版本检查宏(如 SDL_NET_VERSION_ATLEAST(3,0,0)

FreeBASIC 示例代码

' 引入 SDL 及 SDL_net 相关声明(需链接 SDL3 和 SDL3_net 库)
#Include "SDL.bi"
#Include "SDL_net.bi"

' 补充关键常量/类型声明(FreeBASIC 绑定可能缺失)
#Define NET_PORT_ANY 0
Type NET_Status
    value As Integer
End Type
Enum NET_Status_Values
    NET_STATUS_INVALID = 0
    NET_STATUS_RESOLVING
    NET_STATUS_RESOLVED
    NET_STATUS_CONNECT_FAILED
    NET_STATUS_CONNECTING
    NET_STATUS_CONNECTED
End Enum

' 检查 SDL_net 版本
Sub CheckNetVersion()
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【检查 SDL_net 版本】")

    Dim As SDL_version ver
    NET_Version(@ver)
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SDL_net 版本:%d.%d.%d", ver.major, ver.minor, ver.patch)

    ' 编译期版本检查
    #If SDL_NET_VERSION_ATLEAST(3, 0, 0)
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "✓ 满足 SDL_net 3.0.0 最低版本要求")
    #Else
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "✗ 不满足 SDL_net 3.0.0 最低版本要求")
    #EndIf
End Sub

' TCP 服务器示例(简单回显服务器)
Sub TCPServerExample(ByVal port As Uint16)
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【TCP 回显服务器示例】")

    ' 1. 初始化 SDL_net
    If (NET_Init() = SDL_FALSE) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_net 初始化失败:%s", SDL_GetError())
        Exit Sub
    End If

    ' 2. 构造服务器地址(监听所有网卡的指定端口)
    Dim As NET_Address Ptr serverAddr = NET_ResolveHostname(NULL, port)
    If (serverAddr = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "解析地址失败:%s", SDL_GetError())
        NET_Quit()
        Exit Sub
    End If

    ' 等待地址解析完成
    If (NET_WaitUntilResolved(serverAddr, -1) = SDL_FALSE) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "地址解析超时")
        NET_UnrefAddress(serverAddr)
        NET_Quit()
        Exit Sub
    End If

    ' 3. 创建 TCP 服务器
    Dim As NET_Server Ptr server = NET_CreateServer(serverAddr)
    If (server = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "创建服务器失败:%s", SDL_GetError())
        NET_UnrefAddress(serverAddr)
        NET_Quit()
        Exit Sub
    End If
    NET_UnrefAddress(serverAddr)  ' 释放地址引用

    Dim As ZString * 256 addrStr
    NET_GetAddressString(serverAddr, addrStr, sizeof(addrStr))
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "服务器已启动,监听地址:%s", addrStr)

    ' 4. 接受客户端连接并处理
    Dim As NET_StreamSocket Ptr clientSocket = NULL
    Dim As SDL_bool running = SDL_TRUE
    Dim As UByte buffer(1023)  ' 1KB 接收缓冲区

    While (running)
        ' 非阻塞接受客户端连接
        clientSocket = NET_AcceptClient(server)
        If (clientSocket <> NULL) Then
            SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "客户端已连接")

            ' 循环接收并回显数据
            While (running)
                ' 等待有可读数据(超时 100ms)
                If (NET_WaitUntilInputAvailable(clientSocket, 100) = SDL_TRUE) Then
                    ' 读取数据
                    Dim As Sint64 readBytes = NET_ReadFromStreamSocket(clientSocket, @buffer(0), sizeof(buffer))
                    If (readBytes > 0) Then
                        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "收到数据:%.*s", readBytes, @buffer(0))

                        ' 回显数据给客户端
                        Dim As Sint64 writeBytes = NET_WriteToStreamSocket(clientSocket, @buffer(0), readBytes)
                        If (writeBytes < 0) Then
                            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "发送数据失败:%s", SDL_GetError())
                            Exit While
                        End If
                    ElseIf (readBytes = 0) Then
                        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "客户端断开连接")
                        Exit While
                    Else
                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "读取数据失败:%s", SDL_GetError())
                        Exit While
                    End If
                End If

                ' 处理退出事件(简化示例,实际需处理 SDL 事件)
                Dim As SDL_Event evt
                If (SDL_PollEvent(@evt) AndAlso evt.type = SDL_QUIT) Then
                    running = SDL_FALSE
                End If
            Wend

            ' 关闭客户端连接
            NET_DestroyStreamSocket(clientSocket)
            clientSocket = NULL
        End If

        ' 处理退出事件
        Dim As SDL_Event evt
        If (SDL_PollEvent(@evt) AndAlso evt.type = SDL_QUIT) Then
            running = SDL_FALSE
        End If

        SDL_Delay(10)
    Wend

    ' 5. 释放资源
    If (clientSocket <> NULL) Then
        NET_DestroyStreamSocket(clientSocket)
    End If
    NET_DestroyServer(server)
    NET_Quit()

    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "服务器已停止")
End Sub

' TCP 客户端示例
Sub TCPClientExample(ByVal hostname As ZString Ptr, ByVal port As Uint16)
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【TCP 客户端示例】")

    ' 1. 初始化 SDL_net
    If (NET_Init() = SDL_FALSE) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_net 初始化失败:%s", SDL_GetError())
        Exit Sub
    End If

    ' 2. 解析服务器地址
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "解析地址:%s:%d", hostname, port)
    Dim As NET_Address Ptr serverAddr = NET_ResolveHostname(hostname, port)
    If (serverAddr = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "解析地址失败:%s", SDL_GetError())
        NET_Quit()
        Exit Sub
    End If

    ' 等待地址解析完成(无限等待)
    If (NET_WaitUntilResolved(serverAddr, -1) = SDL_FALSE) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "地址解析失败")
        NET_UnrefAddress(serverAddr)
        NET_Quit()
        Exit Sub
    End If

    ' 打印解析后的地址
    Dim As ZString * 256 addrStr
    NET_GetAddressString(serverAddr, addrStr, sizeof(addrStr))
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "解析完成:%s", addrStr)

    ' 3. 创建客户端并连接服务器
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "连接服务器...")
    Dim As NET_StreamSocket Ptr clientSocket = NET_CreateClient(serverAddr)
    If (clientSocket = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "创建客户端失败:%s", SDL_GetError())
        NET_UnrefAddress(serverAddr)
        NET_Quit()
        Exit Sub
    End If
    NET_UnrefAddress(serverAddr)

    ' 等待连接完成(超时 5 秒)
    If (NET_WaitUntilConnected(clientSocket, 5000) = SDL_FALSE) Then
        Dim As NET_Status status
        NET_GetConnectionStatus(clientSocket, @status)
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "连接失败,状态:%d", status.value)
        NET_DestroyStreamSocket(clientSocket)
        NET_Quit()
        Exit Sub
    End If
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "连接成功!")

    ' 4. 发送测试数据
    Dim As ZString * 64 sendData = "Hello SDL_net Server!"
    Dim As Sint64 writeBytes = NET_WriteToStreamSocket(clientSocket, sendData, Len(sendData))
    If (writeBytes < 0) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "发送数据失败:%s", SDL_GetError())
        NET_DestroyStreamSocket(clientSocket)
        NET_Quit()
        Exit Sub
    End If
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "发送数据:%s", sendData)

    ' 5. 接收服务器回显数据
    Dim As UByte recvBuffer(1023)
    If (NET_WaitUntilInputAvailable(clientSocket, 2000) = SDL_TRUE) Then
        Dim As Sint64 readBytes = NET_ReadFromStreamSocket(clientSocket, @recvBuffer(0), sizeof(recvBuffer))
        If (readBytes > 0) Then
            SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "收到回显:%.*s", readBytes, @recvBuffer(0))
        End If
    Else
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "接收超时")
    End If

    ' 6. 等待数据发送完成并关闭连接
    NET_WaitUntilStreamSocketDrained(clientSocket, -1)
    NET_DestroyStreamSocket(clientSocket)
    NET_Quit()

    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "客户端已断开连接")
End Sub

' UDP 通信示例(简单点对点发送)
Sub UDPCommunicationExample(ByVal localPort As Uint16, ByVal targetHost As ZString Ptr, ByVal targetPort As Uint16)
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【UDP 通信示例】")

    ' 1. 初始化 SDL_net
    If (NET_Init() = SDL_FALSE) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_net 初始化失败:%s", SDL_GetError())
        Exit Sub
    End If

    ' 2. 创建 UDP 套接字
    Dim As NET_Address Ptr localAddr = NET_ResolveHostname(NULL, localPort)
    Dim As NET_DatagramSocket Ptr udpSocket = NET_CreateDatagramSocket(localAddr)
    NET_UnrefAddress(localAddr)

    If (udpSocket = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "创建 UDP 套接字失败:%s", SDL_GetError())
        NET_Quit()
        Exit Sub
    End If
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "UDP 套接字已创建,监听端口:%d", localPort)

    ' 3. 解析目标地址
    Dim As NET_Address Ptr targetAddr = NET_ResolveHostname(targetHost, targetPort)
    If (NET_WaitUntilResolved(targetAddr, 3000) = SDL_FALSE) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "解析目标地址失败")
        NET_UnrefAddress(targetAddr)
        NET_DestroyDatagramSocket(udpSocket)
        NET_Quit()
        Exit Sub
    End If

    ' 4. 发送 UDP 数据包
    Dim As ZString * 64 sendData = "Hello UDP Peer!"
    If (NET_SendDatagram(udpSocket, targetAddr, sendData, Len(sendData)) = SDL_FALSE) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "发送 UDP 数据包失败:%s", SDL_GetError())
    Else
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "发送 UDP 数据:%s", sendData)
    End If

    ' 5. 尝试接收响应(超时 2 秒)
    Dim As NET_Datagram Ptr datagram = NULL
    Dim As UByte recvBuffer(1023)
    If (NET_ReceiveDatagram(udpSocket, @datagram, 2000) = SDL_TRUE AndAlso datagram <> NULL) Then
        ' 提取数据包数据
        Dim As Const UByte Ptr data = NET_DatagramGetData(datagram)
        Dim As Sint64 len = NET_DatagramGetLength(datagram)
        Dim As NET_Address Ptr senderAddr = NET_DatagramGetAddress(datagram)

        Dim As ZString * 256 senderStr
        NET_GetAddressString(senderAddr, senderStr, sizeof(senderStr))
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "从 %s 收到 UDP 数据:%.*s", senderStr, len, data)

        NET_DestroyDatagram(datagram)
    Else
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "未收到 UDP 响应(超时)")
    End If

    ' 6. 释放资源
    NET_UnrefAddress(targetAddr)
    NET_DestroyDatagramSocket(udpSocket)
    NET_Quit()
End Sub

' 主程序
Sub Main()
    ' 初始化 SDL 核心
    If (SDL_Init(0) < 0) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError())
        Exit Sub
    End If

    ' 运行示例(按需取消注释)
    CheckNetVersion()
    ' TCPServerExample(8080)  ' 先启动服务器
    ' TCPClientExample(StrPtr("127.0.0.1"), 8080)  ' 再启动客户端
    ' UDPCommunicationExample(8081, StrPtr("127.0.0.1"), 8082)

    ' 清理 SDL
    SDL_Quit()

    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出")
End Sub

' 运行主程序
Main()

核心知识点补充

  1. 异步非阻塞核心

    • SDL_net 所有操作默认非阻塞,调用后立即返回,需通过状态查询(如 NET_GetConnectionStatus)或 NET_WaitUntil*() 等待操作完成;
    • 阻塞函数(NET_WaitUntil*)建议在后台线程使用,避免阻塞主线程(如游戏渲染线程)。
  2. TCP vs UDP 选择

    • TCP(流套接字):可靠的字节流传输,自动重传丢失数据、保证顺序,适合需要数据完整性的场景(如登录、文件传输);
    • UDP(数据报套接字):无连接、不可靠,数据包可能丢失/乱序,但延迟低、开销小,适合实时性要求高的场景(如游戏帧同步、语音)。
  3. 地址引用计数

    • NET_Address 采用引用计数管理,NET_RefAddress 增加计数,NET_UnrefAddress 减少计数;
    • 确保不再使用的地址调用 NET_UnrefAddress,避免内存泄漏。
  4. 网络异常模拟

    • NET_Simulate*Loss 系列函数用于测试恶劣网络环境,仅在开发/测试阶段使用,生产环境需禁用;
    • 可模拟 DNS 解析失败、TCP/UDP 丢包,验证程序的容错能力。
  5. 跨平台注意事项

    • Windows 系统需确保防火墙允许程序访问网络,监听端口需配置入站规则;
    • Linux/macOS 需注意端口权限(1024 以下端口需 root 权限);
    • 移动平台(Android/iOS)需申请网络权限:Android 需 INTERNET 权限,iOS 需关闭 ATS(或配置例外)。

总结

  1. 核心优势

    • 轻量级封装系统套接字 API,屏蔽跨平台差异(Windows/ Linux/macOS/移动端);
    • 全异步非阻塞设计,适配游戏/实时应用的主线程模型;
    • 抽象化地址管理,无需关注 IPv4/IPv6 底层细节;
    • 内置网络异常模拟功能,便于测试程序的鲁棒性;
    • 与 SDL 生态无缝集成,可直接结合 SDL 事件循环使用。
  2. 使用建议

    • 网络操作尽量放在后台线程,避免阻塞主线程;
    • TCP 通信需处理连接断开、超时等异常,UDP 需自行实现重传/校验逻辑;
    • 频繁使用的 NET_Address 需合理管理引用计数,避免内存泄漏;
    • 服务器监听端口时,优先使用 NET_PORT_ANY 自动分配端口,或选择 1024 以上的非特权端口;
    • 生产环境禁用网络异常模拟函数,避免影响正常通信。
  3. 关键点回顾

    • SDL_net 核心是抽象化的地址(NET_Address)、套接字(TCP 流/UDP 数据报)、服务器(NET_Server)三层结构;
    • NET_Init()/NET_Quit() 是必选的初始化/清理接口;
    • TCP 适合可靠传输,UDP 适合实时传输,需根据业务场景选择;
    • 所有操作默认非阻塞,可通过 NET_WaitUntil*() 显式等待;
    • 地址对象采用引用计数管理,需正确调用 NET_RefAddress/NET_UnrefAddress

评论一下?

OωO
取消