Winsock 用户态库 (ws2_32.dll) 详细分析

Winsock 用户态库 (ws2_32.dll) 详细分析

目录

  1. 概述
  2. 整体架构
  3. 核心数据结构
  4. [DLL 生命周期管理](#DLL 生命周期管理)
  5. [WSAStartup 初始化流程](#WSAStartup 初始化流程)
  6. [API 入口 Prolog 机制](#API 入口 Prolog 机制)
  7. [Socket 生命周期管理](#Socket 生命周期管理)
  8. [数据收发 API](#数据收发 API)
  9. [Socket 控制 API](#Socket 控制 API)
  10. 多路复用与事件
  11. 名称解析
  12. 服务提供者管理
  13. 工作流程图
  14. 数据流图
  15. 构建配置与导出

1. 概述

ws2_32.dll 是 Windows Winsock 2 的 API 入口动态链接库,位于用户态,是应用程序与网络协议栈之间的第一层接口。它实现了完整的 Winsock 2 规范(支持版本 1.0 ~ 2.2),采用 WSP (Winsock Service Provider) 架构 将实际的网络操作委托给下层服务提供者。

ReactOS 的 ws2_32.dll 源码位于 d:\reactos\dll\win32\ws2_32\,共 38 个源文件。

1.1 核心职责

复制代码
┌─────────────────────────────────────────────────┐
│  ws2_32.dll                                      │
│                                                  │
│  ┌─────────────┐  ┌──────────────────────────┐   │
│  │ Winsock API │  │ WSP 架构管理              │   │
│  │ 入口函数     │  │  ├─ 协议目录管理          │   │
│  │ socket()    │  │  ├─ 命名空间目录管理       │   │
│  │ connect()   │  │  ├─ 服务提供者加载/卸载     │   │
│  │ send/recv   │  │  ├─ 套接字句柄上下文管理    │   │
│  │ select()    │  │  ├─ 进程/线程状态管理       │   │
│  │ ...         │  │  └─ 异步请求调度            │   │
│  └─────────────┘  └──────────────────────────┘   │
└─────────────────────────────────────────────────┘
          │ 调用 lpWSPXxx 函数指针
          ▼
┌─────────────────────────────────────────────────┐
│  服务提供者 DLL (如 mswsock.dll / 第三方)         │
│  通过 WSPStartup 导出 WSPPROC_TABLE              │
│  真正执行网络操作 (DeviceIoControl 等)            │
└─────────────────────────────────────────────────┘

1.2 设计思想

ws2_32.dll 的核心设计是间接调用:所有 Winsock API 都遵循同一模式------

复制代码
Winsock API → WsApiProlog(检查初始化) → WsSockGetSocket(查找句柄上下文)
           → Provider->Service.lpWSPXxx(委托给服务提供者)
           → 错误处理 → 返回

这种设计的好处:

  1. 支持第三方协议栈:任何厂商都可以实现一个 WSP 服务提供者,无需修改 ws2_32.dll
  2. 协议无关性:同一个 API 可用于 TCP/IP、IPX/SPX、NetBIOS 等多种协议
  3. 可扩展性:通过注册表目录机制可以动态添加/删除协议提供者

2. 整体架构

2.1 源码模块划分

复制代码
ws2_32/
├── CMakeLists.txt           # 构建配置
├── ws2_32.spec              # 导出定义
├── inc/
│   ├── ws2_32.h             # 主头文件
│   └── ws2_32p.h            # 内部私有数据结构
└── src/                     # 38 个源文件
    ├── dllmain.c            # DLL 入口
    ├── startup.c            # WSAStartup/WSACleanup
    │
    ├── socklife.c           # socket/bind/accept/closesocket 生命周期
    ├── sockctrl.c           # connect/listen/getsockopt/setsockopt/shutdown
    ├── send.c               # send/sendto/WSASend
    ├── recv.c               # recv/recvfrom/WSARecv
    ├── select.c             # select/WSAAsyncSelect
    ├── event.c              # WSAEventSelect/WSAEnumNetworkEvents
    ├── ioctl.c              # ioctlsocket/WSAIoctl
    ├── dupsock.c            # WSADuplicateSocket
    ├── qos.c                # WSAGetQOSByName
    │
    ├── dprocess.c           # 进程对象管理
    ├── dthread.c            # 线程对象管理
    ├── dsocket.c            # 套接字对象管理 (句柄上下文)
    ├── dprovide.c           # 传输服务提供者加载
    ├── dcatalog.c           # 协议目录管理
    ├── dcatitem.c           # 目录项管理
    ├── dcatitem.c           # 目录项枚举
    │
    ├── spinstal.c           # WSCInstallProvider 等
    ├── sputil.c             # WSP 工具函数
    ├── nsprovid.c           # 命名空间提供者
    ├── nscatalo.c           # 命名空间目录
    ├── nscatent.c           # 命名空间目录项
    ├── nspinstl.c           # 命名空间安装
    ├── nsquery.c            # 命名空间查询
    │
    ├── addrinfo.c           # getaddrinfo/freeaddrinfo
    ├── getxbyxx.c           # gethostbyname 等经典函数
    ├── getproto.c           # getprotobyname
    ├── addrconv.c           # inet_addr 等地址转换
    ├── enumprot.c           # WSAEnumProtocols
    ├── rnr.c                # WSAAddressToString 等
    ├── async.c              # WSAAsyncGetHostBy* 异步
    ├── bhook.c              # WSASetBlockingHook
    ├── rasdial.c            # RAS 自动拨号
    ├── wsautil.c            # 工具函数
    └── qshelpr.c            # QoS helper

2.2 功能模块图

复制代码
                    ┌──────────────────────────────┐
                    │  应用程序                     │
                    └──────────────┬───────────────┘
                                   │ Winsock 2 API
                    ┌──────────────▼───────────────┐
                    │  ws2_32.dll                  │
                    │                              │
                    │  ┌────────────────────┐      │
                    │  │ API 入口层          │      │
                    │  │ send/recv/socket/   │      │
                    │  │ connect/select/...  │      │
                    │  └────────┬───────────┘      │
                    │           │ WsApiProlog      │
                    │           ▼                  │
                    │  ┌────────────────────┐      │
                    │  │ 对象管理            │      │
                    │  │ WSPROCESS (进程)    │      │
                    │  │ WSTHREAD  (线程)    │      │
                    │  │ WSSOCKET  (套接字)  │      │
                    │  └────────┬───────────┘      │
                    │           │ Provider 查找    │
                    │           ▼                  │
                    │  ┌────────────────────┐      │
                    │  │ 目录管理            │      │
                    │  │ TCATALOG (协议目录) │      │
                    │  │ NSCATALOG(命名空间) │      │
                    │  └────────┬───────────┘      │
                    │           │ WSPPROC_TABLE    │
                    │           ▼                  │
                    │  ┌────────────────────┐      │
                    │  │ 服务提供者加载       │      │
                    │  │ LoadLibrary +      │      │
                    │  │ WSPStartup         │      │
                    │  └────────────────────┘      │
                    └──────────────┬───────────────┘
                                   │ lpWSPXxx 调用
                    ┌──────────────▼───────────────┐
                    │ 服务提供者 DLL                │
                    │ (AFD 提供者 / 第三方)          │
                    └──────────────┬───────────────┘
                                   │ DeviceIoControl
                    ┌──────────────▼───────────────┐
                    │ 内核 (AFD.sys → TDI → TCPIP)  │
                    └──────────────────────────────┘

3. 核心数据结构

定义在 ws2_32p.h(file:///d:/reactos/dll/win32/ws2_32/inc/ws2_32p.h)。

3.1 WSSOCKET --- 套接字上下文

c 复制代码
typedef struct _WSSOCKET
{
    LONG RefCount;              // 引用计数
    HANDLE Handle;              // 内核文件句柄 (套接字句柄)
    PWSPROCESS Process;         // 所属进程
    PTPROVIDER Provider;        // 绑定的服务提供者
    PTCATALOG_ENTRY CatalogEntry; // 协议目录项
    BOOLEAN Overlapped;         // 是否重叠 I/O (异步)
    BOOLEAN ApiSocket;          // 是否为 API 创建的 socket
    BOOLEAN IsProvider;         // 是否为提供者创建的 socket
} WSSOCKET, *PWSSOCKET;

每个 SOCKET 句柄都关联一个 WSSOCKET 上下文,用于快速查找对应的服务提供者。

3.2 WSPROCESS --- 进程上下文

c 复制代码
typedef struct _WSPROCESS
{
    LONG RefCount;              // 进程引用计数 (WSAStartup 调用次数)
    HANDLE ApcHelper, HandleHelper, NotificationHelper;
    PTCATALOG ProtocolCatalog;      // 协议目录
    PNSCATALOG NamespaceCatalog;    // 命名空间目录
    HANDLE ProtocolCatalogEvent, NamespaceCatalogEvent;
    DWORD Version;                  // Winsock 版本
    BOOLEAN LockReady;
    CRITICAL_SECTION ThreadLock;    // 线程列表锁
} WSPROCESS, *PWSPROCESS;

3.3 WSTHREAD --- 线程上下文

c 复制代码
typedef struct _WSTHREAD
{
    PWSPROCESS Process;             // 所属进程
    WSATHREADID WahThreadId;        // Winsock 线程 ID
    HANDLE AsyncHelper;             // 异步帮助器
    LPWSPCANCELBLOCKINGCALL CancelBlockingCall;
    LPBLOCKINGCALLBACK BlockingCallback;
    FARPROC BlockingHook;           // 阻塞钩子
    BOOLEAN Blocking, Cancelled;    // 阻塞状态
    CHAR Buffer[32];                // 临时缓冲区
    PCHAR Hostent, Servent;         // 解析结果缓存
    DWORD HostentSize, ServentSize;
    DWORD OpenType;                 // 打开类型 (0=默认重叠 / SO_SYNCHRONOUS_NONALERT)
    PVOID ProtocolInfo;             // 协议信息
} WSTHREAD, *PWSTHREAD;

3.4 TPROVIDER --- 传输服务提供者

c 复制代码
typedef struct _TPROVIDER
{
    LONG RefCount;                  // 引用计数
    WSPPROC_TABLE Service;          // WSP 服务函数表 (lpWSPXxx)
    HINSTANCE DllHandle;            // 提供者 DLL 句柄
    WSPUPCALLTABLE UpcallTable;     // WSP 上行调用表 (lpWPUXxx)
} TPROVIDER, *PTPROVIDER;

WSPPROC_TABLE(服务函数表)包含 30+ 个函数指针,如:

  • lpWSPAccept / lpWSPBind / lpWSPConnect / lpWSPListen
  • lpWSPSend / lpWSPSendTo / lpWSPRecv / lpWSPRecvFrom
  • lpWSPSelect / lpWSPEventSelect / lpWSPAsyncSelect
  • lpWSPGetSockOpt / lpWSPSetSockOpt / lpWSPIoctl
  • lpWSPCloseSocket / lpWSPSocket / lpWSPShutdown

WSPUPCALLTABLE(上行调用表)包含 15 个函数指针,由 ws2_32 提供给提供者调用:

  • lpWPUCreateSocketHandle / lpWPUCloseSocketHandle
  • lpWPUModifyIFSHandle / lpWPUQuerySocketHandleContext
  • lpWPUCreateEvent / lpWPUSetEvent / lpWPUResetEvent / lpWPUCloseEvent
  • lpWPUPostMessage / lpWPUQueueApc
  • lpWPUOpenCurrentThread / lpWPUCloseThread
  • lpWPUFDIsSet / lpWPUGetProviderPath
  • lpWPUQueryBlockingCallback / lpWPUCancelBlockingCall

3.5 TCATALOG / TCATALOG_ENTRY --- 协议目录

c 复制代码
typedef struct _TCATALOG_ENTRY
{
    LIST_ENTRY CatalogLink;         // 目录链表
    LONG RefCount;
    PTPROVIDER Provider;            // 对应提供者
    CHAR DllPath[MAX_PATH];         // DLL 路径
    WSAPROTOCOL_INFOW ProtocolInfo; // 协议信息 (af/type/protocol/guid...)
} TCATALOG_ENTRY, *PTCATALOG_ENTRY;

typedef struct _TCATALOG
{
    LIST_ENTRY ProtocolList;        // 协议链表头
    DWORD ItemCount;                // 项数
    DWORD UniqueId;
    DWORD NextId;
    HKEY CatalogKey;                // 注册表键
    CRITICAL_SECTION Lock;
    BOOLEAN Initialized;
} TCATALOG, *PTCATALOG;

3.6 NSPROVIDER / NSCATALOG --- 命名空间提供者

c 复制代码
typedef struct _NSPROVIDER
{
    LONG RefCount;
    DWORD NamespaceId;              // 命名空间 ID (NS_DNS, NS_SAP...)
    HINSTANCE DllHandle;
    GUID ProviderId;
    NSP_ROUTINE Service;            // NSP 服务函数表
} NSPROVIDER, *PNS_PROVIDER;

3.7 WSASYNCBLOCK --- 异步请求块

c 复制代码
typedef enum _WSASYNCOPS
{
    WsAsyncGetHostByAddr,       // 异步 gethostbyaddr
    WsAsyncGetHostByName,       // 异步 gethostbyname
    WsAsyncGetProtoByName,
    WsAsyncGetProtoByNumber,
    WsAsyncGetServByName,
    WsAsyncGetServByPort,
    WsAsyncTerminate,
} WSASYNCOPS;

typedef struct _WSASYNCBLOCK
{
    LIST_ENTRY AsyncQueue;      // 异步队列
    HANDLE TaskHandle;          // 任务线程句柄
    WSASYNCOPS Operation;       // 操作类型
    union { ... }               // GetHost/GetProto/GetServ 参数
} WSASYNCBLOCK, *PWSASYNCBLOCK;

4. DLL 生命周期管理

4.1 DllMain 初始化

dllmain.c(file:///d:/reactos/dll/win32/ws2_32/src/dllmain.c) 中:

复制代码
DllMain(DLL_PROCESS_ATTACH)
   │
   ├─ 保存模块句柄 → WsDllHandle
   ├─ 获取进程堆 → WsSockHeap
   ├─ TlsAlloc() → GlobalTlsIndex      (线程本地存储)
   ├─ WsCreateStartupSynchronization() (初始化启动临界区)
   ├─ 异步全局初始化 (WsAsyncGlobalInit)
   └─ RAS 自动拨号初始化

DllMain(DLL_THREAD_DETACH)
   └─ WsThreadDestroyCurrentThread()   (清理线程上下文)

DllMain(DLL_PROCESS_DETACH)  [仅 FreeLibrary 触发]
   ├─ WsProcDelete()                   (销毁进程结构)
   ├─ 销毁套接字/线程管理器
   └─ TlsFree()                        (释放 TLS 索引)

4.2 全局变量

变量 类型 用途
WsSockHeap HANDLE 进程堆句柄,所有内存分配使用
WsDllHandle HMODULE DLL 模块句柄
GlobalTlsIndex DWORD TLS 索引,存放 WSTHREAD 指针

5. WSAStartup 初始化流程

实现于 startup.c(file:///d:/reactos/dll/win32/ws2_32/src/startup.c#L111-L250)。

5.1 版本协商

复制代码
WSAStartup(wVersionRequested, &WSADATA)
   │
   ├─ if (!WsDllHandle) return WSASYSNOTREADY;   // DLL 未初始化
   │
   ├─ switch (LOBYTE(wVersionRequested)) {
   │     case 0: WSAVERNOTSUPPORTED; 返回 2.2
   │     case 1: 支持 1.0/1.1         // 老 Winsock 1 应用
   │     case 2: 支持 2.0/2.1/2.2     // Winsock 2 应用
   │   }
   │
   ├─ 填充 WSADATA:
   │     wVersion     = 协商后版本
   │     wHighVersion = 2.2 (支持的最高版本)
   │     szDescription = "WinSock 2.0"
   │     szSystemStatus = "Running"
   │     iMaxSockets (Winsock1: 32767)
   │     iMaxUdpDg   (Winsock1: 65467)
   │
   └─ 进入 WsStartupLock 临界区:
        ├─ WsGetProcess() 是否已存在?
        │     └─ 不存在: WsProcStartup() → WsSockStartup() → WsThreadStartup()
        ├─ WsProcSetVersion(CurrentProcess, wVersionRequested)
        └─ InterlockedIncrement(&CurrentProcess->RefCount)

5.2 WsProcStartup --- 进程对象初始化

复制代码
WsProcStartup()
   │
   ├─ 分配 WSPROCESS 结构 (WsSockHeap 堆)
   ├─ 初始化协议目录: WsTcInitialize → 读取注册表
   │     WINSOCK_ROOT = "System\CurrentControlSet\Services\WinSock2\Parameters"
   │     枚举 Protocol_Catalog9 键下的所有项
   │     每项加载一个 TPROVIDER (LoadLibrary + WSPStartup)
   ├─ 初始化命名空间目录: WsNsInitialize
   │     枚举 NameSpace_Catalog5 键下的所有项
   └─ 创建帮助器事件句柄

5.3 WSACleanup --- 清理流程

复制代码
WSACleanup()
   │
   ├─ WsStartupLock()
   ├─ WsApiProlog() 检查初始化
   ├─ InterlockedDecrement(&Process->RefCount)
   │     ├─ RefCount == 0 → WsProcDelete()   (完全清理)
   │     └─ RefCount == 1 → WsAsyncTerminateThread() (停止异步线程)
   └─ WsStartupUnlock()

6. API 入口 Prolog 机制

6.1 WsApiProlog --- 完整 Prolog

复制代码
WsApiProlog(&Process, &Thread)
   │
   ├─ 获取当前线程 TLS 数据 (TlsGetValue(GlobalTlsIndex))
   ├─ 如果线程上下文不存在 → WsThreadCreateCurrentThread()
   ├─ 通过线程上下文取进程 → Thread->Process
   ├─ 校验进程已初始化
   └─ 返回 ERROR_SUCCESS / 错误码

6.2 WsQuickProlog --- 快速 Prolog

用于不需要线程上下文的简单 API(如 select/bind 等):

复制代码
WsQuickProlog()
   └─ 检查进程是否初始化 (通过 TLS/全局状态快速判断)

6.3 典型 API 调用模式

send() 为例(send.c(file:///d:/reactos/dll/win32/ws2_32/src/send.c#L21-L75)):

c 复制代码
INT send(SOCKET s, const CHAR *buf, INT len, INT flags)
{
    PWSSOCKET Socket;
    WSABUF Buffers;

    /* 1. Prolog: 检查 WSAStartup 已调用 */
    if ((ErrorCode = WsQuickPrologTid(&ThreadId)) != ERROR_SUCCESS)
        goto error;

    /* 2. 通过 SOCKET 句柄查找 WSSOCKET 上下文 */
    if (!(Socket = WsSockGetSocket(s)))
    {
        ErrorCode = WSAENOTSOCK;   // 无效句柄
        goto error;
    }

    /* 3. 构造 WSABUF 数组 */
    Buffers.buf = (PCHAR)buf;
    Buffers.len = len;

    /* 4. 调用服务提供者的 lpWSPSend */
    Status = Socket->Provider->Service.lpWSPSend(s,
                 &Buffers, 1, &BytesSent, (DWORD)flags,
                 NULL, NULL, ThreadId, &ErrorCode);

    /* 5. 释放引用 */
    WsSockDereference(Socket);

    /* 6. 错误处理: 提供者未设置错误码时使用 WSASYSCALLFAILURE */
    if (Status == ERROR_SUCCESS) return BytesSent;
    if (ErrorCode == NO_ERROR) ErrorCode = WSASYSCALLFAILURE;

error:
    SetLastError(ErrorCode);
    return SOCKET_ERROR;
}

7. Socket 生命周期管理

7.1 socket() → WSASocketW

socket() 是一个薄包装(socklife.c(file:///d:/reactos/dll/win32/ws2_32/src/socklife.c#L141-L169)):

c 复制代码
SOCKET socket(af, type, protocol)
{
    /* Prolog */
    WsApiProlog(&Process, &Thread);

    /* 默认使用重叠 I/O */
    if (!Thread->OpenType) Flags = WSA_FLAG_OVERLAPPED;

    /* NetBIOS 特殊处理: 协议号取负 */
    if ((af == AF_NETBIOS) && (protocol > 0)) protocol *= -1;

    return WSASocketW(af, type, protocol, NULL, 0, Flags);
}

WSASocketW 是核心实现(socklife.c(file:///d:/reactos/dll/win32/ws2_32/src/socklife.c#L488+)):

复制代码
WSASocketW(af, type, protocol, lpProtocolInfo, g, dwFlags)
   │
   ├─ Prolog
   ├─ 获取协议目录 (WsProcGetTCatalog)
   │
   ├─ 情况1: 提供了 lpProtocolInfo
   │     └─ WsTcGetEntryFromCatalogEntryId(Catalog, CatalogId, &Entry)
   │
   ├─ 情况2: 未提供 → 按 (af, type, protocol) 三元组查找
   │     └─ WsTcGetEntryFromTriplet(Catalog, af, type, protocol, &Entry)
   │           ├─ 遍历协议目录
   │           ├─ 匹配 WSAPROTOCOL_INFOW 的 iAddressFamily/iSocketType/iProtocol
   │           └─ 找到最佳匹配
   │
   ├─ 获取目录项的 Provider
   ├─ 设置 OpenType (重叠/同步)
   │
   ├─ 调用提供者: Entry->Provider->Service.lpWSPSocket(af, type, protocol, ...)
   │     └─ 提供者返回真正的 SOCKET 句柄
   │
   ├─ 创建 WSSOCKET 上下文:
   │     ├─ WsSockAllocate()            (分配)
   │     ├─ WsSockInitialize(Socket, Entry) (初始化, 关联 Provider)
   │     └─ WsSockAssociateHandle(Socket, s, TRUE) (关联句柄)
   │
   └─ 返回句柄

7.2 WsSockGetSocket --- 句柄上下文查找

实现于 dsocket.c(file:///d:/reactos/dll/win32/ws2_32/src/dsocket.c#L92-L170):

复制代码
WsSockGetSocket(IN SOCKET Handle)
   │
   ├─ WsSockGetSocketNoExport(Handle)     // 通过句柄索引查找
   │     ├─ 通过 SOCKET 句柄定位 WSSOCKET 上下文
   │     └─ 如果找到, 引用计数 +1
   │
   └─ 返回 PWSSOCKET / NULL

7.3 bind() --- 绑定地址

复制代码
bind(s, name, namelen)
   ├─ WsQuickProlog()
   ├─ WsSockGetSocket(s)            // 查找上下文
   ├─ 参数检查: name && namelen >= sizeof(sockaddr)
   ├─ Socket->Provider->Service.lpWSPBind(s, name, namelen, &ErrorCode)
   └─ 成功返回 0, 失败 SetLastError + SOCKET_ERROR

7.4 accept() / WSAAccept()

复制代码
accept(s, addr, addrlen) → WSAAccept(s, addr, addrlen, NULL, 0)

WSAAccept()
   ├─ Prolog
   ├─ WsSockGetSocket(s)
   ├─ 临时切换 OpenType: Socket->Overlapped ? 0 : SO_SYNCHRONOUS_NONALERT
   ├─ Provider->Service.lpWSPAccept(s, addr, addrlen, condition, data, &ErrorCode)
   ├─ 恢复 OpenType
   ├─ 返回新句柄 ≠ s 时 → WsSockAddApiReference(新句柄)  (登记新 socket 上下文)
   └─ 返回新 SOCKET

7.5 closesocket() --- 关闭

复制代码
closesocket(s)
   ├─ WsQuickProlog()
   ├─ WsSockGetSocket(s)
   ├─ Provider->Service.lpWSPCloseSocket(s, &ErrorCode)
   ├─ 如果是提供者 socket (IsProvider):
   │     ├─ WsSockDisassociateHandle(Socket)  (解除句柄关联)
   │     └─ WsSockDereference(Socket) x2      (释放引用)
   └─ 返回

8. 数据收发 API

8.1 发送系列

函数 实现 WSP 调用
send send.c(file:///d:/reactos/dll/win32/ws2_32/src/send.c#L21-L75) lpWSPSend
sendto send.c(file:///d:/reactos/dll/win32/ws2_32/src/send.c#L80-L130) lpWSPSendTo
WSASend send.c(file:///d:/reactos/dll/win32/ws2_32/src/send.c) lpWSPSend (重叠模式)
WSASendTo send.c(file:///d:/reactos/dll/win32/ws2_32/src/send.c) lpWSPSendTo (重叠模式)
WSASendDisconnect send.c(file:///d:/reactos/dll/win32/ws2_32/src/send.c) lpWSPSendDisconnect

send() 流程

复制代码
send(s, buf, len, flags)
   ├─ 构造单个 WSABUF { buf, len }
   ├─ lpWSPSend(s, &Buffers, 1, &BytesSent, flags, NULL, NULL, ThreadId, &Err)
   ├─ 成功 → 返回 BytesSent
   └─ 失败 → SetLastError + SOCKET_ERROR

WSASend() 流程(支持多缓冲区和重叠 I/O):

复制代码
WSASend(s, lpBuffers, dwBufferCount, &dwNumberOfBytesSent,
        dwFlags, lpOverlapped, lpCompletionRoutine)
   ├─ lpWSPSend(s, lpBuffers, dwBufferCount, &dwBytesSent,
   │            dwFlags, lpOverlapped, lpCompletionRoutine,
   │            ThreadId, &ErrorCode)
   ├─ 返回 SOCKET_ERROR + WSA_IO_PENDING 表示异步进行中
   └─ 完成通过 lpOverlapped / lpCompletionRoutine 通知

8.2 接收系列

函数 实现 WSP 调用
recv recv.c(file:///d:/reactos/dll/win32/ws2_32/src/recv.c) lpWSPRecv
recvfrom recv.c(file:///d:/reactos/dll/win32/ws2_32/src/recv.c) lpWSPRecvFrom
WSARecv recv.c(file:///d:/reactos/dll/win32/ws2_32/src/recv.c) lpWSPRecv (重叠模式)
WSARecvFrom recv.c(file:///d:/reactos/dll/win32/ws2_32/src/recv.c) lpWSPRecvFrom (重叠模式)

recv() 流程

复制代码
recv(s, buf, len, flags)
   ├─ 构造单个 WSABUF
   ├─ lpWSPRecv(s, &Buffers, 1, &BytesRecv, &Flags, NULL, NULL, ThreadId, &Err)
   ├─ 成功 → 返回 BytesRecv
   └─ 失败 → SetLastError + SOCKET_ERROR

9. Socket 控制 API

实现在 sockctrl.c(file:///d:/reactos/dll/win32/ws2_32/src/sockctrl.c)。

9.1 connect()

复制代码
connect(s, name, namelen)
   ├─ Prolog
   ├─ WsSockGetSocket(s)
   ├─ 调用 Provider->Service.lpWSPConnect(s, name, namelen, NULL, NULL, NULL, NULL, &Err)
   │
   ├─ 失败检查: WSAEHOSTUNREACH / WSAENETUNREACH
   │     └─ WSAttemptAutodialAddr() 尝试 RAS 自动拨号
   │           └─ 成功 → 重试 connect (最多一次重试)
   │
   ├─ Winsock 1 兼容: WSAEALREADY → WSAEINVAL
   └─ 返回

9.2 listen() / shutdown() / getsockopt() / setsockopt()

函数 WSP 调用 说明
listen(s, backlog) lpWSPListen 进入监听状态
shutdown(s, how) lpWSPShutdown 关闭读写方向
getsockopt lpWSPGetSockOpt 获取套接字选项
setsockopt lpWSPSetSockOpt 设置套接字选项
getpeername lpWSPGetPeerName 获取对端地址
getsockname lpWSPGetSockName 获取本地地址
ioctlsocket lpWSPIoctl 套接字 IO 控制 (FIONBIO 等)

9.3 ioctlsocket()

复制代码
ioctlsocket(s, cmd, argp)   →  Provider->Service.lpWSPIoctl(...)
   ├─ FIONBIO   : 设置/清除非阻塞模式
   ├─ FIONREAD  : 查询可读字节数
   ├─ SIOCATMARK: 查询带外数据位置
   └─ 其他      : 透传给提供者

10. 多路复用与事件

10.1 select()

实现在 select.c(file:///d:/reactos/dll/win32/ws2_32/src/select.c#L39-L115):

复制代码
select(nfds, readfds, writefds, exceptfds, timeout)
   │
   ├─ WsQuickProlog()
   ├─ 从三个 fd_set 中取第一个有效句柄
   │     (优先 readfds → writefds → exceptfds)
   ├─ WsSockGetSocket(Handle)           // 获取上下文以确定提供者
   ├─ Provider->Service.lpWSPSelect(nfds, readfds, writefds, exceptfds, timeout, &Err)
   └─ 返回就绪的 socket 数量

__WSAFDIsSet :宏 FD_ISSET 的实现,遍历 fd_set 检查句柄是否存在。

10.2 WSAAsyncSelect --- 窗口消息通知

复制代码
WSAAsyncSelect(s, hWnd, wMsg, lEvent)
   ├─ Provider->Service.lpWSPAsyncSelect(s, hWnd, wMsg, lEvent, &ErrorCode)
   └─ 事件发生时 → 提供者通过 lpWPUPostMessage 发送 wMsg 到 hWnd

10.3 WSAEventSelect --- 事件对象通知

实现在 event.c(file:///d:/reactos/dll/win32/ws2_32/src/event.c):

复制代码
WSAEventSelect(s, hEventObject, lNetworkEvents)
   ├─ Provider->Service.lpWSPEventSelect(s, hEventObject, lNetworkEvents, &Err)
   └─ 事件发生时 → 提供者设置 hEventObject 信号

WSAEnumNetworkEvents(s, hEventObject, lpNetworkEvents)
   ├─ Provider->Service.lpWSPEnumNetworkEvents(s, hEventObject, lpNetworkEvents, &Err)
   └─ 返回事件掩码并重置事件

10.4 WSAWaitForMultipleEvents

复制代码
WSAWaitForMultipleEvents(cEvents, lphEvents, fWaitAll, dwTimeout, fAlertable)
   └─ 直接调用 WaitForMultipleObjectsEx (用户态事件等待)

11. 名称解析

11.1 经典函数 (getxbyxx.c)

函数 说明
gethostbyname 按主机名解析
gethostbyaddr 按 IP 地址反查
getservbyname / getservbyport 服务名/端口查询
getprotobyname / getprotobynumber 协议查询
inet_addr / inet_ntoa IP 地址字符串转换

gethostbyname 内部通过命名空间提供者查询(NSP_ROUTINE.lpNSPLookupServiceNext 等)或直接查询 DNS。

11.2 现代函数 (addrinfo.c)

函数 说明
getaddrinfo 协议无关的名称解析
freeaddrinfo 释放 getaddrinfo 结果
getnameinfo 地址到名称

getaddrinfo 流程

复制代码
getaddrinfo(pNodeName, pServiceName, pHints, ppResult)
   │
   ├─ 如果 pNodeName 是纯 IPv4 数字 → ParseV4Address() 直接解析
   ├─ 否则:
   │     ├─ 调用命名空间提供者 / DNS 查询
   │     ├─ 或者通过 gethostbyname 兼容路径
   │     └─ 构建 ADDRINFO 链表:
   │           ├─ ai_flags / ai_family / ai_socktype / ai_protocol
   │           ├─ ai_addrlen / ai_addr (sockaddr_in)
   │           └─ ai_canonname / ai_next
   │
   └─ 返回结果链表

11.3 异步解析 (async.c)

WSAAsyncGetHostByName 等通过 WSASYNCBLOCK 结构实现异步:

复制代码
WSAAsyncGetHostByName(hWnd, wMsg, name, buf, buflen)
   │
   ├─ 分配 WSASYNCBLOCK (记录 hWnd/wMsg/参数)
   ├─ 创建异步线程 (WsAsyncThread)
   ├─ 线程执行实际解析
   ├─ 完成后 → PostMessage(hWnd, wMsg) 通知窗口
   └─ 通过 WsAsyncTerminate 线程终止管理

12. 服务提供者管理

12.1 传输提供者加载 (dprovide.c)

核心函数 WsTpInitialize(file:///d:/reactos/dll/win32/ws2_32/src/dprovide.c#L36-L96):

复制代码
WsTpInitialize(Provider, DllName, ProtocolInfo)
   │
   ├─ 清空 UpcallTable 和 Service 表
   │
   ├─ 填充 WSPUPCALLTABLE (上行调用表):
   │     lpWPUCreateSocketHandle    = WPUCreateSocketHandle
   │     lpWPUCloseSocketHandle     = WPUCloseSocketHandle
   │     lpWPUModifyIFSHandle       = WPUModifyIFSHandle
   │     lpWPUCreateEvent           = WPUCreateEvent
   │     lpWPUSetEvent              = WPUSetEvent
   │     lpWPUResetEvent            = WPUResetEvent
   │     lpWPUCloseEvent            = WPUCloseEvent
   │     lpWPUPostMessage           = WPUPostMessage
   │     lpWPUQueueApc              = WPUQueueApc
   │     lpWPUQueryBlockingCallback = WPUQueryBlockingCallback
   │     lpWPUOpenCurrentThread     = WPUOpenCurrentThread
   │     lpWPUCloseThread           = WPUCloseThread
   │     ... (共 15 个)
   │
   ├─ ExpandEnvironmentStrings(DllName, ExpandedDllPath)   // 展开环境变量
   ├─ LoadLibrary(ExpandedDllPath)                         // 加载提供者 DLL
   ├─ GetProcAddress(DllHandle, "WSPStartup")              // 获取入口
   │
   └─ (*WSPStartupProc)(2.2, &WspData, ProtocolInfo,
                        Provider->UpcallTable, &Provider->Service)
         └─ 提供者初始化自身并填充 WSPPROC_TABLE

12.2 协议目录管理 (dcatalog.c)

注册表结构

复制代码
HKLM\System\CurrentControlSet\Services\WinSock2\Parameters
├── Protocol_Catalog9
│   └── Catalog_Entries
│       ├── 000000000001\ (提供者1)
│       │   ├── DisplayString
│       │   ├── Path        → DLL 路径
│       │   ├── Protocol   → WSAPROTOCOL_INFOW
│       │   └── ...
│       └── 000000000002\ (提供者2)
│           └── ...
└── NameSpace_Catalog5
    └── Catalog_Entries
        └── ... (命名空间提供者)

目录操作

  • WsTcInitialize --- 打开注册表键,读取所有协议项
  • WsTcGetEntryFromCatalogEntryId --- 按 CatalogEntryId 查找
  • WsTcGetEntryFromTriplet --- 按 (af, type, protocol) 三元组匹配
  • WsTcEntryReference/Dereference --- 引用计数

12.3 命名空间提供者 (nsquery.c / nsprovid.c)

命名空间查询流程:

复制代码
WSALookupServiceBegin → WSALookupServiceNext → WSALookupServiceEnd
   │
   ├─ 遍历命名空间目录 (NSCATALOG)
   ├─ 对每个 NSPROVIDER:
   │     ├─ lpNSPLookupServiceBegin (开始查询)
   │     ├─ lpNSPLookupServiceNext  (获取结果)
   │     └─ lpNSPLookupServiceEnd   (结束查询)
   ├─ 将结果转换为 WSAQUERYSET 格式
   └─ 返回

12.4 WPU 帮助函数

提供给服务提供者的 socket 句柄管理函数:

复制代码
WPUCreateSocketHandle(CatalogEntryId, Context, &Errno)
   ├─ 从目录中查找 Entry
   ├─ WsSockAllocate + WsSockInitialize
   ├─ 分配真正的句柄 (创建内核文件句柄)
   ├─ WsSockAssociateHandle(句柄)
   └─ 返回句柄

WPUModifyIFSHandle(CatalogEntryId, ProposedHandle, &Errno)
   ├─ 查找目录项
   ├─ 分配 WSSOCKET
   ├─ WsSockAssociateHandle(Socket, ProposedHandle, TRUE)
   └─ 返回 ProposedHandle

WPUCloseSocketHandle(s, &Errno)
   ├─ WsSockGetSocket(s)
   ├─ WsSockDisassociateHandle
   └─ WsSockDereference

13. 工作流程图

13.1 WSAStartup 初始化流程

#mermaid-svg-sxWclRmA07YcRaun{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sxWclRmA07YcRaun .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sxWclRmA07YcRaun .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sxWclRmA07YcRaun .error-icon{fill:#552222;}#mermaid-svg-sxWclRmA07YcRaun .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sxWclRmA07YcRaun .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sxWclRmA07YcRaun .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sxWclRmA07YcRaun .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sxWclRmA07YcRaun .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sxWclRmA07YcRaun .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sxWclRmA07YcRaun .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sxWclRmA07YcRaun .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sxWclRmA07YcRaun .marker.cross{stroke:#333333;}#mermaid-svg-sxWclRmA07YcRaun svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sxWclRmA07YcRaun p{margin:0;}#mermaid-svg-sxWclRmA07YcRaun .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sxWclRmA07YcRaun .cluster-label text{fill:#333;}#mermaid-svg-sxWclRmA07YcRaun .cluster-label span{color:#333;}#mermaid-svg-sxWclRmA07YcRaun .cluster-label span p{background-color:transparent;}#mermaid-svg-sxWclRmA07YcRaun .label text,#mermaid-svg-sxWclRmA07YcRaun span{fill:#333;color:#333;}#mermaid-svg-sxWclRmA07YcRaun .node rect,#mermaid-svg-sxWclRmA07YcRaun .node circle,#mermaid-svg-sxWclRmA07YcRaun .node ellipse,#mermaid-svg-sxWclRmA07YcRaun .node polygon,#mermaid-svg-sxWclRmA07YcRaun .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sxWclRmA07YcRaun .rough-node .label text,#mermaid-svg-sxWclRmA07YcRaun .node .label text,#mermaid-svg-sxWclRmA07YcRaun .image-shape .label,#mermaid-svg-sxWclRmA07YcRaun .icon-shape .label{text-anchor:middle;}#mermaid-svg-sxWclRmA07YcRaun .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sxWclRmA07YcRaun .rough-node .label,#mermaid-svg-sxWclRmA07YcRaun .node .label,#mermaid-svg-sxWclRmA07YcRaun .image-shape .label,#mermaid-svg-sxWclRmA07YcRaun .icon-shape .label{text-align:center;}#mermaid-svg-sxWclRmA07YcRaun .node.clickable{cursor:pointer;}#mermaid-svg-sxWclRmA07YcRaun .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sxWclRmA07YcRaun .arrowheadPath{fill:#333333;}#mermaid-svg-sxWclRmA07YcRaun .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sxWclRmA07YcRaun .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sxWclRmA07YcRaun .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sxWclRmA07YcRaun .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sxWclRmA07YcRaun .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sxWclRmA07YcRaun .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sxWclRmA07YcRaun .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sxWclRmA07YcRaun .cluster text{fill:#333;}#mermaid-svg-sxWclRmA07YcRaun .cluster span{color:#333;}#mermaid-svg-sxWclRmA07YcRaun div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sxWclRmA07YcRaun .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sxWclRmA07YcRaun rect.text{fill:none;stroke-width:0;}#mermaid-svg-sxWclRmA07YcRaun .icon-shape,#mermaid-svg-sxWclRmA07YcRaun .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sxWclRmA07YcRaun .icon-shape p,#mermaid-svg-sxWclRmA07YcRaun .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sxWclRmA07YcRaun .icon-shape .label rect,#mermaid-svg-sxWclRmA07YcRaun .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sxWclRmA07YcRaun .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sxWclRmA07YcRaun .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sxWclRmA07YcRaun :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} LOBYTE=0
LOBYTE=1
LOBYTE=2


DllMain DLL_PROCESS_ATTACH
保存 WsDllHandle/WsSockHeap
TlsAlloc 获取 TLS 索引
WsCreateStartupSynchronization
应用程序调用 WSAStartup
版本协商
WSAVERNOTSUPPORTED
返回 1.0/1.1
返回 2.0-2.2
填充 WSADATA
进程已初始化?
WsProcStartup
WsTcInitialize 读取协议目录注册表
对每个协议项 WsTpInitialize
LoadLibrary + WSPStartup 加载提供者
WsNsInitialize 命名空间目录
WsSockStartup 套接字管理器
WsThreadStartup 线程管理器
获取现有进程
WsProcSetVersion 设置版本
RefCount++ 增加引用计数
WSAStartup 成功返回

13.2 socket() 创建流程

#mermaid-svg-6bz0ABIhKAkQGYuS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6bz0ABIhKAkQGYuS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6bz0ABIhKAkQGYuS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6bz0ABIhKAkQGYuS .error-icon{fill:#552222;}#mermaid-svg-6bz0ABIhKAkQGYuS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6bz0ABIhKAkQGYuS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6bz0ABIhKAkQGYuS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6bz0ABIhKAkQGYuS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6bz0ABIhKAkQGYuS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6bz0ABIhKAkQGYuS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6bz0ABIhKAkQGYuS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6bz0ABIhKAkQGYuS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6bz0ABIhKAkQGYuS .marker.cross{stroke:#333333;}#mermaid-svg-6bz0ABIhKAkQGYuS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6bz0ABIhKAkQGYuS p{margin:0;}#mermaid-svg-6bz0ABIhKAkQGYuS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6bz0ABIhKAkQGYuS .cluster-label text{fill:#333;}#mermaid-svg-6bz0ABIhKAkQGYuS .cluster-label span{color:#333;}#mermaid-svg-6bz0ABIhKAkQGYuS .cluster-label span p{background-color:transparent;}#mermaid-svg-6bz0ABIhKAkQGYuS .label text,#mermaid-svg-6bz0ABIhKAkQGYuS span{fill:#333;color:#333;}#mermaid-svg-6bz0ABIhKAkQGYuS .node rect,#mermaid-svg-6bz0ABIhKAkQGYuS .node circle,#mermaid-svg-6bz0ABIhKAkQGYuS .node ellipse,#mermaid-svg-6bz0ABIhKAkQGYuS .node polygon,#mermaid-svg-6bz0ABIhKAkQGYuS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6bz0ABIhKAkQGYuS .rough-node .label text,#mermaid-svg-6bz0ABIhKAkQGYuS .node .label text,#mermaid-svg-6bz0ABIhKAkQGYuS .image-shape .label,#mermaid-svg-6bz0ABIhKAkQGYuS .icon-shape .label{text-anchor:middle;}#mermaid-svg-6bz0ABIhKAkQGYuS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6bz0ABIhKAkQGYuS .rough-node .label,#mermaid-svg-6bz0ABIhKAkQGYuS .node .label,#mermaid-svg-6bz0ABIhKAkQGYuS .image-shape .label,#mermaid-svg-6bz0ABIhKAkQGYuS .icon-shape .label{text-align:center;}#mermaid-svg-6bz0ABIhKAkQGYuS .node.clickable{cursor:pointer;}#mermaid-svg-6bz0ABIhKAkQGYuS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6bz0ABIhKAkQGYuS .arrowheadPath{fill:#333333;}#mermaid-svg-6bz0ABIhKAkQGYuS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6bz0ABIhKAkQGYuS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6bz0ABIhKAkQGYuS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6bz0ABIhKAkQGYuS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6bz0ABIhKAkQGYuS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6bz0ABIhKAkQGYuS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6bz0ABIhKAkQGYuS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6bz0ABIhKAkQGYuS .cluster text{fill:#333;}#mermaid-svg-6bz0ABIhKAkQGYuS .cluster span{color:#333;}#mermaid-svg-6bz0ABIhKAkQGYuS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6bz0ABIhKAkQGYuS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6bz0ABIhKAkQGYuS rect.text{fill:none;stroke-width:0;}#mermaid-svg-6bz0ABIhKAkQGYuS .icon-shape,#mermaid-svg-6bz0ABIhKAkQGYuS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6bz0ABIhKAkQGYuS .icon-shape p,#mermaid-svg-6bz0ABIhKAkQGYuS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6bz0ABIhKAkQGYuS .icon-shape .label rect,#mermaid-svg-6bz0ABIhKAkQGYuS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6bz0ABIhKAkQGYuS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6bz0ABIhKAkQGYuS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6bz0ABIhKAkQGYuS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是







应用调用 socket af type protocol
socket 函数
WsApiProlog 检查初始化
线程 OpenType 为空?
Flags = WSA_FLAG_OVERLAPPED
Flags = 0
AF_NETBIOS?
protocol 取负
继续
WSASocketW
WsProcGetTCatalog 获取协议目录
提供 lpProtocolInfo?
按 CatalogEntryId 查找
按 af/type/protocol 三元组匹配
获取 PTCATALOG_ENTRY
匹配成功?
WSATRY_AGAIN 或错误
设置 OpenType 重叠/同步
lpWSPSocket 调用提供者
提供者返回 SOCKET 句柄
WsSockAllocate 分配 WSSOCKET
WsSockInitialize 绑定 Provider
WsSockAssociateHandle 关联句柄
返回 SOCKET

13.3 send() 完整流程

#mermaid-svg-A6vHvcsVbe2Mv7Hu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .error-icon{fill:#552222;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .marker.cross{stroke:#333333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-A6vHvcsVbe2Mv7Hu p{margin:0;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .cluster-label text{fill:#333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .cluster-label span{color:#333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .cluster-label span p{background-color:transparent;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .label text,#mermaid-svg-A6vHvcsVbe2Mv7Hu span{fill:#333;color:#333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .node rect,#mermaid-svg-A6vHvcsVbe2Mv7Hu .node circle,#mermaid-svg-A6vHvcsVbe2Mv7Hu .node ellipse,#mermaid-svg-A6vHvcsVbe2Mv7Hu .node polygon,#mermaid-svg-A6vHvcsVbe2Mv7Hu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .rough-node .label text,#mermaid-svg-A6vHvcsVbe2Mv7Hu .node .label text,#mermaid-svg-A6vHvcsVbe2Mv7Hu .image-shape .label,#mermaid-svg-A6vHvcsVbe2Mv7Hu .icon-shape .label{text-anchor:middle;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .rough-node .label,#mermaid-svg-A6vHvcsVbe2Mv7Hu .node .label,#mermaid-svg-A6vHvcsVbe2Mv7Hu .image-shape .label,#mermaid-svg-A6vHvcsVbe2Mv7Hu .icon-shape .label{text-align:center;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .node.clickable{cursor:pointer;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .arrowheadPath{fill:#333333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-A6vHvcsVbe2Mv7Hu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A6vHvcsVbe2Mv7Hu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-A6vHvcsVbe2Mv7Hu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .cluster text{fill:#333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .cluster span{color:#333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-A6vHvcsVbe2Mv7Hu rect.text{fill:none;stroke-width:0;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .icon-shape,#mermaid-svg-A6vHvcsVbe2Mv7Hu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .icon-shape p,#mermaid-svg-A6vHvcsVbe2Mv7Hu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .icon-shape .label rect,#mermaid-svg-A6vHvcsVbe2Mv7Hu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A6vHvcsVbe2Mv7Hu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-A6vHvcsVbe2Mv7Hu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-A6vHvcsVbe2Mv7Hu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否







应用调用 send s buf len flags
WsQuickPrologTid
已初始化?
SetLastError WSANOTINITIALISED
WsSockGetSocket s
找到上下文?
SetLastError WSAENOTSOCK
构造 WSABUF 数组
调用 lpWSPSend
提供者成功?
返回 BytesSent
ErrorCode 为 0?
ErrorCode = WSASYSCALLFAILURE
保持原错误
SetLastError + SOCKET_ERROR

13.4 select() 流程

渲染错误: Mermaid 渲染失败: Parse error on line 4: ...| DHandle = readfds\[0] C -->|否| E{ -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'SQS'


14. 数据流图

14.1 API 调用数据流

复制代码
应用程序
  │
  │ send(s, buf, len, flags)
  ▼
┌─────────────────────────────────────────────┐
│ ws2_32.dll  send()                          │
│                                             │
│  1. WsQuickPrologTid → ThreadId             │
│  2. WsSockGetSocket(s) → WSSOCKET           │
│  3. 构造 WSABUF {buf, len}                  │
│  4. Socket->Provider->Service.lpWSPSend     │
└──────────────────┬──────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────┐
│ 服务提供者 (WSPPROC_TABLE)                   │
│  lpWSPSend 实现                              │
│   ├─ 参数检查/转换                           │
│   ├─ 构造 AFD_SEND_INFO                     │
│   └─ DeviceIoControl(hSocket, IOCTL_AFD_SEND)│
└──────────────────┬──────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────┐
│ 内核 (AFD.sys → TDI → tcpip.sys)            │
│                                             │
│  IRP_MJ_DEVICE_CONTROL → IOCTL_AFD_SEND     │
│    → TdiSend → TDI_SEND IRP → TCPSendData   │
│    → IP → NDIS → 网卡                       │
└─────────────────────────────────────────────┘

14.2 WSSOCKET 上下文生命周期

复制代码
socket() 创建
    │
    ▼
WsSockAllocate() ──── 分配 WSSOCKET (RefCount=1)
    │
    ▼
WsSockInitialize() ── 绑定 Provider + CatalogEntry
    │
    ▼
WsSockAssociateHandle() ── 注册句柄映射 (RefCount=2)
    │
    ├────────────┬──────────────┬──────────────┐
    ▼            ▼              ▼              ▼
 send()/recv() connect()     accept()       closesocket()
    │            │              │              │
WsSockGetSocket WsSockGetSocket WsSockGetSocket WsSockGetSocket
(RefCount 3→2)  (RefCount 3→2)  (RefCount 3→2)  (RefCount 3→2)
    │            │              │              │
    ▼            ▼              ▼              ▼
lpWSPSend...  lpWSPConnect  lpWSPAccept   lpWSPCloseSocket
    │            │              │              │
    ▼            ▼              ▼              ▼
WsSockDereference (RefCount 2→1)
    │            │              │              │
    ▼            ▼              ▼              ▼
                                 新 socket → WsSockAddApiReference
                                                 │
                                                 ▼
                                            WsSockDisassociateHandle (RefCount 2→1)
                                                 │
                                                 ▼
                                            WsSockDereference → RefCount=0 → 释放

14.3 错误处理数据流

复制代码
API 调用失败
    │
    ▼
提供者返回 ErrorCode
    │
    ├─ ErrorCode == NO_ERROR (提供者未设置)
    │     └─ 转换为 WSASYSCALLFAILURE
    │
    ├─ 特定错误:
    │     ├─ WSAENOTSOCK   (句柄无效)
    │     ├─ WSAENETUNREACH/ WSAEHOSTUNREACH (connect 时触发自动拨号)
    │     ├─ WSAEALREADY   (Winsock 1 时转为 WSAEINVAL)
    │     └─ 其他直接透传
    │
    ▼
SetLastError(ErrorCode)
    │
    ▼
返回 SOCKET_ERROR / INVALID_SOCKET
    │
    ▼
应用程序调用 WSAGetLastError() → GetLastError() → 获取错误码

15. 构建配置与导出

15.1 CMakeLists.txt

cmake 复制代码
spec2def(ws2_32.dll ws2_32.spec ADD_IMPORTLIB)

add_definitions(-DLE)                    # 定义 LE (Little Endian)

add_library(ws2_32 MODULE
    src/*.c
    inc/ws2_32.h
    ws2_32.rc
    ${CMAKE_CURRENT_BINARY_DIR}/ws2_32_stubs.c
    ${CMAKE_CURRENT_BINARY_DIR}/ws2_32.def)

set_module_type(ws2_32 win32dll)         # 用户态 DLL
target_link_libraries(ws2_32 ${PSEH_LIB})

# 高版本时链接 ws2help_static
if(DLL_EXPORT_VERSION GREATER_EQUAL 0x600)
    target_link_libraries(ws2_32 ws2help_static)
endif()

# 导入库
add_importlibs(ws2_32
    user32 advapi32 ws2help msvcrt kernel32 ntdll)

add_pch(ws2_32 inc/ws2_32.h SOURCE)      # 预编译头
add_cd_file(TARGET ws2_32 DESTINATION reactos/system32 FOR all)

15.2 导出函数 (ws2_32.spec)

主要导出(部分):

复制代码
WSAStartup              (用户态 API)
WSACleanup
socket / closesocket / bind / connect / listen / accept
send / recv / sendto / recvfrom
select / WSASelect
getsockopt / setsockopt / ioctlsocket
gethostbyname / gethostbyaddr / gethostname
getaddrinfo / freeaddrinfo / getnameinfo
inet_addr / inet_ntoa / htons / htonl
WSAGetLastError / WSASetLastError
WSASocketA / WSASocketW / WSASend / WSARecv
WSAAsyncSelect / WSAEventSelect / WSAEnumNetworkEvents
WSAAsyncGetHostByName / WSAAsyncGetHostByAddr
WSADuplicateSocket / WSAIoctl / WSAPoll
WSCEnumProtocols / WSCInstallProvider (WSP 管理)

15.3 依赖关系

复制代码
ws2_32.dll
  ├── kernel32.dll      (内存/句柄/进程管理)
  ├── ntdll.dll         (底层系统调用)
  ├── user32.dll        (PostMessage 窗口消息)
  ├── advapi32.dll      (注册表操作)
  ├── ws2help.dll       (Winsock 帮助函数)
  └── msvcrt.dll        (C 运行时)

附录 A:WSP 函数表 (WSPPROC_TABLE)

c 复制代码
typedef struct _WSPPROC_TABLE {
    LPWSPACCEPT          lpWSPAccept;
    LPWSPADDRESSTOSTRING lpWSPAddressToString;
    LPWSPASYNCSELECT     lpWSPAsyncSelect;
    LPWSPBIND            lpWSPBind;
    LPWSPCANCELBLOCKINGCALL lpWSPCancelBlockingCall;
    LPWSPCLEANUP         lpWSPCleanup;
    LPWSPCLOSESOCKET     lpWSPCloseSocket;
    LPWSPCONNECT         lpWSPConnect;
    LPWSPDUPLICATESOCKET lpWSPDuplicateSocket;
    LPWSPENUMNETWORKEVENTS lpWSPEnumNetworkEvents;
    LPWSPEVENTSELECT     lpWSPEventSelect;
    LPWSPGETOVERLAPPEDRESULT lpWSPGetOverlappedResult;
    LPWSPGETPEERNAME     lpWSPGetPeerName;
    LPWSPGETSOCKNAME     lpWSPGetSockName;
    LPWSPGETSOCKOPT      lpWSPGetSockOpt;
    LPWSPGETQOSBYNAME    lpWSPGetQOSByName;
    LPWSPIOCTL           lpWSPIoctl;
    LPWSPJOINLEAF        lpWSPJoinLeaf;
    LPWSPLISTEN          lpWSPListen;
    LPWSPRECV            lpWSPRecv;
    LPWSPRECVDISCONNECT  lpWSPRecvDisconnect;
    LPWSPRECVFROM        lpWSPRecvFrom;
    LPWSPSELECT          lpWSPSelect;
    LPWSPSEND            lpWSPSend;
    LPWSPSENDDISCONNECT  lpWSPSendDisconnect;
    LPWSPSENDTO          lpWSPSendTo;
    LPWSPSETSOCKOPT      lpWSPSetSockOpt;
    LPWSPSHUTDOWN        lpWSPShutdown;
    LPWSPSOCKET          lpWSPSocket;
    LPWSPSTARTUP         lpWSPStartup;
    LPWSPSTRINGTOADDRESS lpWSPStringToAddress;
} WSPPROC_TABLE;

附录 B:WSP 上行调用表 (WSPUPCALLTABLE)

c 复制代码
typedef struct _WSPUPCALLTABLE {
    LPWPUCLOSEEVENT      lpWPUCloseEvent;
    LPWPUCLOSESOCKETHANDLE lpWPUCloseSocketHandle;
    LPWPUCREATEEVENT     lpWPUCreateEvent;
    LPWPUCREATESOCKETHANDLE lpWPUCreateSocketHandle;
    LPWPUFDISSET         lpWPUFDIsSet;
    LPWPUGETPROVIDERPATH lpWPUGetProviderPath;
    LPWPUMODIFYIFSHANDLE lpWPUModifyIFSHandle;
    LPWPUPOSTMESSAGE     lpWPUPostMessage;
    LPWPUQUERYBLOCKINGCALLBACK lpWPUQueryBlockingCallback;
    LPWPUQUERYSOCKETHANDLECONTEXT lpWPUQuerySocketHandleContext;
    LPWPUQUEUEAPC        lpWPUQueueApc;
    LPWPURESETEVENT      lpWPUResetEvent;
    LPWPUSETEVENT        lpWPUSetEvent;
    LPWPUOPENCURRENTTHREAD lpWPUOpenCurrentThread;
    LPWPUCLOSETHREAD     lpWPUCloseThread;
} WSPUPCALLTABLE;

附录 C:关键文件索引

文件 路径 说明
ws2_32p.h inc/ws2_32p.h(file:///d:/reactos/dll/win32/ws2_32/inc/ws2_32p.h) 核心数据结构定义
dllmain.c src/dllmain.c(file:///d:/reactos/dll/win32/ws2_32/src/dllmain.c) DLL 入口
startup.c src/startup.c(file:///d:/reactos/dll/win32/ws2_32/src/startup.c) WSAStartup/WSACleanup
socklife.c src/socklife.c(file:///d:/reactos/dll/win32/ws2_32/src/socklife.c) socket 生命周期
sockctrl.c src/sockctrl.c(file:///d:/reactos/dll/win32/ws2_32/src/sockctrl.c) connect/listen/getsockopt
send.c src/send.c(file:///d:/reactos/dll/win32/ws2_32/src/send.c) send/sendto/WSASend
recv.c src/recv.c(file:///d:/reactos/dll/win32/ws2_32/src/recv.c) recv/recvfrom/WSARecv
select.c src/select.c(file:///d:/reactos/dll/win32/ws2_32/src/select.c) select/WSAAsyncSelect
event.c src/event.c(file:///d:/reactos/dll/win32/ws2_32/src/event.c) WSAEventSelect
dsocket.c src/dsocket.c(file:///d:/reactos/dll/win32/ws2_32/src/dsocket.c) WSSOCKET 句柄上下文管理
dprocess.c src/dprocess.c(file:///d:/reactos/dll/win32/ws2_32/src/dprocess.c) WSPROCESS 进程管理
dthread.c src/dthread.c(file:///d:/reactos/dll/win32/ws2_32/src/dthread.c) WSTHREAD 线程管理
dprovide.c src/dprovide.c(file:///d:/reactos/dll/win32/ws2_32/src/dprovide.c) 服务提供者加载 (WSPStartup)
dcatalog.c src/dcatalog.c(file:///d:/reactos/dll/win32/ws2_32/src/dcatalog.c) 协议目录管理
addrinfo.c src/addrinfo.c(file:///d:/reactos/dll/win32/ws2_32/src/addrinfo.c) getaddrinfo
async.c src/async.c(file:///d:/reactos/dll/win32/ws2_32/src/async.c) 异步名称解析
nsquery.c src/nsquery.c(file:///d:/reactos/dll/win32/ws2_32/src/nsquery.c) 命名空间查询

附录 D:相关文档

  • TCP/IP协议驱动分析_1.md(file:///d:/reactos/doc/TCP/IP协议驱动分析_1.md) --- Winsock → TDI → DeviceIoControl 完整调用链
  • TCPIP 驱动分析(file:///d:/reactos/doc/TCPIP_驱动分析.md) --- tcpip.sys 协议栈实现
  • NDIS 驱动分析(file:///d:/reactos/doc/NDIS_驱动分析.md) --- ndis.sys 中间层实现
相关推荐
云边云科技_云网融合1 小时前
工业制造多厂区、供应链远程数据传输网络如何优化?
运维·服务器·网络
clear sky .1 小时前
[TCP]MCU上如何编辑TCP服务端程序,思路框架
服务器·网络·tcp/ip
FungLeo2 小时前
Flutter 带 TTL 的多级缓存设计:内存+磁盘+网络三层实战
网络·flutter·缓存·性能优化
牛马也想出海2 小时前
使用Playwright被检测为机器人的原因及反检测方案
开发语言·网络·人工智能·机器人·php
暗暗别做白日梦2 小时前
CompletableFuture 并发统计
网络·数据库·oracle
倔强的小石头_2 小时前
中兴F50只能本地管理?刷入UFI-TOOLS后这样远程访问
网络
@CLoudbays_Martin113 小时前
Linux 服务器如何查看是否被植入后门?
linux·服务器·网络·安全·github·ssl
拾光Ծ3 小时前
【Linux网络】深入理解网络层:从IP协议格式,子网划分到NAT与路由机制
linux·网络·网络协议·tcp/ip·计算机网络
2301_7807896613 小时前
DDoS 攻击溯源:DNS 水印标记 + 区块链存证的双保险
运维·服务器·网络·云原生·ddos