Winsock 用户态库 (ws2_32.dll) 详细分析
目录
- 概述
- 整体架构
- 核心数据结构
- [DLL 生命周期管理](#DLL 生命周期管理)
- [WSAStartup 初始化流程](#WSAStartup 初始化流程)
- [API 入口 Prolog 机制](#API 入口 Prolog 机制)
- [Socket 生命周期管理](#Socket 生命周期管理)
- [数据收发 API](#数据收发 API)
- [Socket 控制 API](#Socket 控制 API)
- 多路复用与事件
- 名称解析
- 服务提供者管理
- 工作流程图
- 数据流图
- 构建配置与导出
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(委托给服务提供者)
→ 错误处理 → 返回
这种设计的好处:
- 支持第三方协议栈:任何厂商都可以实现一个 WSP 服务提供者,无需修改 ws2_32.dll
- 协议无关性:同一个 API 可用于 TCP/IP、IPX/SPX、NetBIOS 等多种协议
- 可扩展性:通过注册表目录机制可以动态添加/删除协议提供者
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/lpWSPListenlpWSPSend/lpWSPSendTo/lpWSPRecv/lpWSPRecvFromlpWSPSelect/lpWSPEventSelect/lpWSPAsyncSelectlpWSPGetSockOpt/lpWSPSetSockOpt/lpWSPIoctllpWSPCloseSocket/lpWSPSocket/lpWSPShutdown
WSPUPCALLTABLE(上行调用表)包含 15 个函数指针,由 ws2_32 提供给提供者调用:
lpWPUCreateSocketHandle/lpWPUCloseSocketHandlelpWPUModifyIFSHandle/lpWPUQuerySocketHandleContextlpWPUCreateEvent/lpWPUSetEvent/lpWPUResetEvent/lpWPUCloseEventlpWPUPostMessage/lpWPUQueueApclpWPUOpenCurrentThread/lpWPUCloseThreadlpWPUFDIsSet/lpWPUGetProviderPathlpWPUQueryBlockingCallback/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 中间层实现