Reactos 第 10 章 网络操作 — 10.3.4 AFD驱动与Winsock

第 10 章 网络操作 --- 10.3.4 AFD驱动与Winsock

本节剖析 AFD(Ancillary Function Driver,afd.sys)与 Winsock(ws2_32.dll + msafd.dll)的实现。 AFD 是 内核态 Winsock ,实现 socket 内核对象、bind/connect/accept、select/异步通知。Winsock 是 用户态 API,把 socket 调用翻译为对 AFD 设备的 IOCTL。ReactOS 的 AFD 实现在 drivers/network/afd/afd/(file:///d:/reactos/drivers/network/afd/afd/),Winsock 在 dll/win32/ws2_32/(file:///d:/reactos/dll/win32/ws2_32/) 和 dll/win32/msafd/(file:///d:/reactos/dll/win32/msafd/)。


概述

Winsock 在 Windows 网络栈中的位置:

  • 用户态 APIws2_32.dll 提供 socket()bind()connect() 等 BSD 风格 API
  • 服务提供者msafd.dll 是 Service Provider,把 Winsock 调用翻译为对 \Device\Afd 的 IOCTL
  • 内核态实现afd.sys 实现 socket 状态机、select 通知、异步操作
  • 协议栈:通过 TDI 与 TCP/IP 通信

本节内容概览

  • 10.3.4.0 框架图
  • 10.3.4.1 Winsock DLL 入口
  • 10.3.4.2 ws2_32.dll 核心 API
  • 10.3.4.3 msafd.dll Service Provider
  • 10.3.4.4 AFD 驱动初始化
  • 10.3.4.5 AFD 内部结构
  • 10.3.4.6 AFD 派发函数
  • 10.3.4.7 socket/connect/accept 实现
  • 10.3.4.8 send/recv 与异步通知
  • 10.3.4.9 总结与代码索引

学习目标

  • 理解 Winsock 用户态/内核态划分
  • 掌握 AFD 与 TCP/IP 之间的 TDI 接口
  • 知道 socket() 调用到 AFD 的路径
  • 跟踪 select 通知机制

涉及的内核子系统

子系统 头文件/源文件 核心作用
AFD 主 drivers/network/afd/afd/main.c(file:///d:/reactos/drivers/network/afd/afd/main.c) DriverEntry
AFD bind bind.c(file:///d:/reactos/drivers/network/afd/afd/bind.c) AfdBind
AFD connect connect.c(file:///d:/reactos/drivers/network/afd/afd/connect.c) AfdConnect
AFD listen listen.c(file:///d:/reactos/drivers/network/afd/afd/listen.c) AfdListen
AFD read read.c(file:///d:/reactos/drivers/network/afd/afd/read.c) AfdReceive
AFD write write.c(file:///d:/reactos/drivers/network/afd/afd/write.c) AfdSend
AFD select select.c(file:///d:/reactos/drivers/network/afd/afd/select.c) AfdSelect
AFD event event.c(file:///d:/reactos/drivers/network/afd/afd/event.c) 异步事件
AFD 头 afd.h(file:///d:/reactos/drivers/network/afd/afd/afd.h) 数据结构
Winsock DLL dll/win32/ws2_32/(file:///d:/reactos/dll/win32/ws2_32/) ws2_32.dll
MSAFD dll/win32/msafd/(file:///d:/reactos/dll/win32/msafd/) Service Provider
TCP/IP drivers/network/tcpip/tcpip/(file:///d:/reactos/drivers/network/tcpip/tcpip/) TCP/IP 协议栈

10.3.4.0 框架图

复制代码
+-------------------------------------+
| 应用 (浏览器/游戏)                   |
| socket(), bind(), connect(), ...     |
+-------------------------------------+
              |
              v
+-------------------------------------+
| ws2_32.dll (用户态 Winsock)         |
| 翻译 API 为 IOCTL                    |
+-------------------------------------+
              |
              v
+-------------------------------------+
| msafd.dll (Service Provider)        |
| 翻译 socket 调用为 IOCTL            |
+-------------------------------------+
              |
              v DeviceIoControl
+-------------------------------------+
| AFD (afd.sys) 内核态 Winsock        |  本节主题
| - socket 内核对象                    |
| - bind/connect/accept               |
| - select/异步通知                   |
+-------------------------------------+
              |
              v TDI IOCTL
+-------------------------------------+
| TCP/IP (tcpip.sys)                  |
+-------------------------------------+

10.3.4.1 Winsock DLL 入口

ws2_32/(file:///d:/reactos/dll/win32/ws2_32/) 包含:

  • socket.csocket()bind()connect()
  • ws2_32.spec:导出表
  • dllmain.cDllMain
  • async.c:异步 select

socket() 调用路径

c 复制代码
// ws2_32/socket.c
SOCKET WSAAPI socket(int af, int type, int protocol) {
    SOCKET s;
    
    // 1. 打开 \Device\Afd
    HANDLE h = CreateFileW(L"\\Device\\Afd", ...);
    
    // 2. IOCTL_AFD_CREATE 创 socket
    AFD_CREATE_REQUEST req = {af, type, protocol};
    AFD_CREATE_RESPONSE resp;
    DeviceIoControl(h, IOCTL_AFD_CREATE, &req, sizeof(req), &resp, sizeof(resp), ...);
    
    // 3. 复制句柄到调用方进程
    s = resp.Handle;
    
    return s;
}

bind() 调用路径

c 复制代码
int WSAAPI bind(SOCKET s, const struct sockaddr *name, int namelen) {
    AFD_BIND_REQUEST req = {s, name, namelen};
    DeviceIoControl(s, IOCTL_AFD_BIND, &req, sizeof(req), ...);
    return 0;
}

10.3.4.2 ws2_32.dll 核心 API

ws2_32.dll 导出的核心 API:

API 作用 IOCTL
socket 创建 socket IOCTL_AFD_CREATE
bind 绑定地址 IOCTL_AFD_BIND
connect 连接 IOCTL_AFD_CONNECT
listen 监听 IOCTL_AFD_LISTEN
accept 接受连接 IOCTL_AFD_ACCEPT
send/WSASend 发送 IOCTL_AFD_SEND
recv/WSARecv 接收 IOCTL_AFD_RECV
closesocket 关闭 IOCTL_AFD_CLOSE
WSAAsyncSelect 异步通知 IOCTL_AFD_EVENT_SELECT
select 多路复用 IOCTL_AFD_SELECT
getsockname 名字 IOCTL_AFD_GET_SOCK_NAME
ioctlsocket IOCTL IOCTL_AFD_SET_SOCK_MODE

connect() 路径

c 复制代码
int WSAAPI connect(SOCKET s, const struct sockaddr *name, int namelen) {
    AFD_CONNECT_REQUEST req = {s, name, namelen};
    DWORD bytesReturned;
    
    if (DeviceIoControl(s, IOCTL_AFD_CONNECT, &req, sizeof(req), NULL, 0, &bytesReturned, NULL)) {
        return 0;
    }
    
    // 阻塞 vs 非阻塞
    if (WSAGetLastError() == WSAEWOULDBLOCK) {
        // 等待 select 通知
    }
    
    return SOCKET_ERROR;
}

10.3.4.3 msafd.dll Service Provider

msafd.dllMicrosoft Ancillary Function Driver 的用户态代理

  • 实现 Winsock 2 Service Provider Interface (SPI)
  • 注册到 ws2_32.dll 的 SPI 表
  • 把所有 Winsock 调用翻译为对 AFD 设备的 IOCTL

SPI 表

msafd.dll 提供 WSPStartup 函数,填表如下:

c 复制代码
int WSPAPI WSPStartup(WORD VersionRequested, LPWSPDATA WSPData,
                      LPWSAPROTOCOL_INFOW ProtocolInfo,
                      WSPUPCALLTABLE UpcallTable, LPWSPPROC_TABLE ProcTable)
{
    ProcTable->lpWSPSocket = WSPSocket;
    ProcTable->lpWSPBind = WSPBind;
    ProcTable->lpWSPConnect = WSPConnect;
    ProcTable->lpWSPAccept = WSPAccept;
    ProcTable->lpWSPSend = WSPSend;
    ProcTable->lpWSPRecv = WSPRecv;
    // ...
}

应用通过 WSAStartupWSPStartup 链获取 SPI。

WSPSocket

c 复制代码
SOCKET WSPAPI WSPSocket(int af, int type, int protocol, ...) {
    // 翻译为 IOCTL_AFD_CREATE
    return WSPCommonSocket(af, type, protocol, ...);
}

SOCKET WSPCommonSocket(int af, int type, int protocol, ...) {
    HANDLE h = OpenAfdDevice();  // 打开 \Device\Afd
    AFD_CREATE_REQUEST req = {af, type, protocol};
    AFD_CREATE_RESPONSE resp;
    
    // 复制句柄
    DuplicateHandle(GetCurrentProcess(), resp.Handle, GetCurrentProcess(), &s, ...);
    
    return s;
}

10.3.4.4 AFD 驱动初始化

main.c(file:///d:/reactos/drivers/network/afd/afd/main.c):

c 复制代码
NTSTATUS NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    UNICODE_STRING DeviceName;
    PDEVICE_OBJECT DeviceObject;
    
    // 1. 创建设备对象 \Device\Afd
    RtlInitUnicodeString(&DeviceName, L"\\Device\\Afd");
    IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_AFD, 0, FALSE, &DeviceObject);
    AfdDeviceObject = DeviceObject;
    
    // 2. 注册派发
    DriverObject->MajorFunction[IRP_MJ_CREATE] = AfdCreate;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = AfdClose;
    DriverObject->MajorFunction[IRP_MJ_CLEANUP] = AfdCleanup;
    DriverObject->MajorFunction[IRP_MJ_READ] = AfdRead;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = AfdWrite;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AfdDispatchDeviceControl;
    
    // 3. 初始化全局
    InitializeListHead(&AfdEndpointListHead);
    ExInitializeFastMutex(&AfdGlobalLock);
    
    return STATUS_SUCCESS;
}

10.3.4.5 AFD 内部结构

端点(FCB)

c 复制代码
typedef struct _AFD_ENDPOINT {
    // 文件对象关联
    PFILE_OBJECT FileObject;
    
    // 状态
    LONG State;                  // 监听/连接/关闭
    LONG DisconnectMode;         // 主动/被动
    LONG EventFlags;             // FD_READ | FD_WRITE | ...
    
    // 地址
    SOCKADDR LocalAddress;
    ULONG LocalAddressLength;
    SOCKADDR RemoteAddress;
    ULONG RemoteAddressLength;
    
    // 协议
    LONG AddressFamily;          // AF_INET
    LONG SocketType;             // SOCK_STREAM/SOCK_DGRAM
    LONG Protocol;               // IPPROTO_TCP/UDP
    
    // TDI 句柄
    HANDLE TdiAddressHandle;
    HANDLE TdiConnectionHandle;
    
    // 异步 I/O
    PAFD_POLL PollList;          // select 多路复用列表
    LIST_ENTRY SelectListEntry;
    
    // 缓冲
    LIST_ENTRY ReceiveQueue;
    LIST_ENTRY SendQueue;
    
    // 工作项
    PIO_WORKITEM WorkItem;
    
    // 锁
    FAST_MUTEX Lock;
} AFD_ENDPOINT, *PAFD_ENDPOINT;

10.3.4.6 AFD 派发函数

AfdDispatchDeviceControl(主派发)

c 复制代码
NTSTATUS AfdDispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
    PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
    ULONG IoControlCode = IoStack->Parameters.DeviceIoControl.IoControlCode;
    PAFD_ENDPOINT Endpoint = IoStack->FileObject->FsContext;
    
    switch (IoControlCode) {
        case IOCTL_AFD_BIND:
            Status = AfdBindSocket(Endpoint, ...);
            break;
        case IOCTL_AFD_CONNECT:
            Status = AfdConnectSocket(Endpoint, ...);
            break;
        case IOCTL_AFD_LISTEN:
            Status = AfdListenSocket(Endpoint, ...);
            break;
        case IOCTL_AFD_ACCEPT:
            Status = AfdAcceptSocket(Endpoint, ...);
            break;
        case IOCTL_AFD_SEND:
            Status = AfdSend(Endpoint, ...);
            break;
        case IOCTL_AFD_RECV:
            Status = AfdReceive(Endpoint, ...);
            break;
        case IOCTL_AFD_SELECT:
            Status = AfdSelect(Endpoint, ...);
            break;
        case IOCTL_AFD_EVENT_SELECT:
            Status = AfdEventSelect(Endpoint, ...);
            break;
        // ...
    }
    
    Irp->IoStatus.Status = Status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return Status;
}

10.3.4.7 socket/connect/accept 实现

AfdCreate(socket)

c 复制代码
NTSTATUS AfdCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
    PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
    PAFD_CREATE_REQUEST CreateReq = IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
    PAFD_ENDPOINT Endpoint;
    
    // 1. 分配端点
    Endpoint = ExAllocatePoolWithTag(NonPagedPool, sizeof(AFD_ENDPOINT), 'pdaF');
    RtlZeroMemory(Endpoint, sizeof(AFD_ENDPOINT));
    
    // 2. 初始化
    Endpoint->AddressFamily = CreateReq->AddressFamily;
    Endpoint->SocketType = CreateReq->SocketType;
    Endpoint->Protocol = CreateReq->Protocol;
    Endpoint->State = SOCKET_STATE_CREATED;
    
    // 3. 打开协议设备
    switch (CreateReq->AddressFamily) {
        case AF_INET:
            if (CreateReq->SocketType == SOCK_STREAM) {
                Endpoint->TdiDeviceName = L"\\Device\\Tcp";
            } else {
                Endpoint->TdiDeviceName = L"\\Device\\Udp";
            }
            break;
    }
    
    ZwCreateFile(&Endpoint->TdiAddressHandle, ..., Endpoint->TdiDeviceName, ...);
    
    // 4. 关联 FileObject
    IoStack->FileObject->FsContext = Endpoint;
    
    return STATUS_SUCCESS;
}

AfdBind

bind.c(file:///d:/reactos/drivers/network/afd/afd/bind.c):

c 复制代码
NTSTATUS AfdBindSocket(PAFD_ENDPOINT Endpoint, PAFD_BIND_REQUEST BindReq) {
    // 1. 打开地址对象
    TDI_REQUEST_KERNEL_SET_ADDRESS_KERNEL_PARAMETERS SetAddr = {BindReq->Address, ...};
    ZwDeviceIoControlFile(Endpoint->TdiAddressHandle, ..., IOCTL_TDI_SET_ADDRESS, ...);
    
    // 2. 通知 TCP/IP 绑定地址
    return STATUS_SUCCESS;
}

AfdConnect

connect.c(file:///d:/reactos/drivers/network/afd/afd/connect.c):

c 复制代码
NTSTATUS AfdConnectSocket(PAFD_ENDPOINT Endpoint, PAFD_CONNECT_REQUEST ConnectReq) {
    // 1. 打开连接对象
    HANDLE TdiConn;
    ZwCreateFile(&TdiConn, ..., "\\Device\\Tcp", ...);
    
    // 2. 关联到地址
    ZwDeviceIoControlFile(TdiConn, ..., IOCTL_TDI_ASSOCIATE_ADDRESS, ...);
    
    // 3. 发起连接(TDI_CONNECT)
    IO_STATUS_BLOCK IoStatus;
    PIRP Irp = TdiBuildInternalDeviceControlIrp(TDI_CONNECT, ...);
    // ... 设置超时等
    
    NtStatus = NtIoCallDriver(TdiConn, Irp);
    
    if (NtStatus == STATUS_PENDING) {
        // 等待连接完成
    }
    
    return NtStatus;
}

AfdAccept

listen.c(file:///d:/reactos/drivers/network/afd/afd/listen.c):

c 复制代码
NTSTATUS AfdAcceptSocket(PAFD_ENDPOINT Endpoint, PAFD_ACCEPT_REQUEST AcceptReq) {
    // 服务端接收客户端连接
    // 创建新 socket 表示客户端连接
    // 等待连接完成
}

10.3.4.8 send/recv 与异步通知

AfdSend(send)

write.c(file:///d:/reactos/drivers/network/afd/afd/write.c):

c 复制代码
NTSTATUS AfdSend(PAFD_ENDPOINT Endpoint, PAFD_SEND_INFO SendInfo, ...) {
    // 1. 构造 TDI_SEND 请求
    TDI_REQUEST_KERNEL_SEND SendRequest;
    SendRequest.SendFlags = SendInfo->Flags;
    SendRequest.UserData = SendInfo->BufferArray;
    SendRequest.UserDataLength = SendInfo->BufferCount;
    
    // 2. 构造 IRP
    PIRP Irp = TdiBuildSendDatagram(Endpoint->TdiConnectionHandle, ...);
    
    // 3. 发送
    return IoCallDriver(Endpoint->TdiDeviceObject, Irp);
}

AfdReceive(recv)

read.c(file:///d:/reactos/drivers/network/afd/afd/read.c):

c 复制代码
NTSTATUS AfdReceive(PAFD_ENDPOINT Endpoint, PAFD_RECV_INFO RecvInfo, ...) {
    PIRP Irp = TdiBuildReceive(Endpoint->TdiConnectionHandle, ...);
    return IoCallDriver(Endpoint->TdiDeviceObject, Irp);
}

异步通知(select / WSAEventSelect)

select.c(file:///d:/reactos/drivers/network/afd/afd/select.c):

c 复制代码
NTSTATUS AfdSelect(PAFD_ENDPOINT Endpoint, PAFD_POLL PollInfo) {
    // 1. 注册 select
    Endpoint->PollState = PollInfo->PollState;
    Endpoint->EventFlags |= PollInfo->PollEvents;
    
    // 2. 把 Endpoint 加入全局 Poll 列表
    InsertTailList(&AfdEndpointListHead, &Endpoint->PollListEntry);
    
    // 3. 立即检查可读/可写
    if (数据已到达) {
        KeSetEvent(PollInfo->Event, 0, FALSE);
    } else {
        // 等待
        // 数据到达时 AfdReceiveComplete 唤醒
    }
    
    return STATUS_PENDING;
}

WSAEventSelect

WSAEventSelectFD_READ 等事件映射为 Windows 事件对象。当对应事件触发时,AFD 设置事件:

c 复制代码
VOID AfdEventSelectNotify(PAFD_ENDPOINT Endpoint, ULONG EventFlag) {
    if (Endpoint->EventFlags & EventFlag) {
        KeSetEvent(Endpoint->EventObject, 0, FALSE);
    }
}

10.3.4.9 总结

关键要点

  1. Winsock 三层结构ws2_32.dllmsafd.dllafd.sys
  2. ws2_32.dll 翻译 API 为 IOCTL
  3. msafd.dll 是 Service Provider:实现 WSP 表
  4. AFD 是内核 Winsock:维护 socket 状态、select 通知
  5. TDI 接口:AFD 与 TCP/IP 通过 TDI IOCTL 通信
  6. 设备对象\Device\Afd\Device\Tcp\Device\Udp
  7. 异步通知:通过事件对象或窗口消息

AFD 驱动与 Winsock 的深度技术分析

AFD(Ancillary Function Driver)和 Winsock 共同构成了 Windows 网络编程接口的基础设施。本节深入分析 AFD 驱动的内部实现机制、Winsock 的分层架构以及两者之间的协作方式。

AFD 驱动的核心职责

AFD 驱动(afd.sys)是 Windows 网络栈中的关键组件,它承担了以下核心职责:

  1. Socket 对象管理:维护 socket 端点(FCB)的生命周期,包括创建、绑定、连接、监听、关闭等状态转换。

  2. 协议无关性抽象 :AFD 通过 TDI 接口与底层协议栈通信,使得上层应用无需关心使用的是 TCP 还是 UDP。AFD 根据 socket 类型(SOCK_STREAM 或 SOCK_DGRAM)自动选择正确的 TDI 设备(\Device\Tcp\Device\Udp)。

  3. 异步 I/O 支持:AFD 实现了完整的异步 I/O 机制,包括重叠 I/O、事件选择、异步选择等。这使得应用可以在不阻塞线程的情况下进行网络通信。

  4. 多路复用(select) :AFD 实现了 BSD 风格的 select 系统调用,允许应用同时监控多个 socket 的可读、可写、异常状态。

  5. 缓冲管理:AFD 维护发送和接收缓冲区,协调应用层和协议栈之间的数据流。

AFD 端点(FCB)的详细结构

AFD 使用 AFD_FCB(File Control Block)结构表示每个 socket 端点。这个结构包含 socket 的所有状态信息:

c 复制代码
typedef struct _AFD_FCB {
    // 基础信息
    LONG ReferenceCount;           // 引用计数
    FAST_MUTEX Lock;               // 状态锁
    
    // Socket 属性
    LONG AddressFamily;            // AF_INET, AF_INET6
    LONG SocketType;               // SOCK_STREAM, SOCK_DGRAM
    LONG Protocol;                 // IPPROTO_TCP, IPPROTO_UDP
    
    // 状态机
    AFD_SOCKET_STATE State;        // 当前状态
    LONG DisconnectMode;           // 断开模式
    
    // 地址信息
    PSOCKADDR LocalAddress;        // 本地地址
    ULONG LocalAddressLength;
    PSOCKADDR RemoteAddress;       // 远程地址
    ULONG RemoteAddressLength;
    
    // TDI 句柄
    HANDLE TdiAddressHandle;       // 地址对象句柄
    HANDLE TdiConnectionHandle;    // 连接对象句柄
    PDEVICE_OBJECT TdiDeviceObject; // TDI 设备对象
    
    // I/O 队列
    LIST_ENTRY ReceiveQueue;       // 接收 IRP 队列
    LIST_ENTRY SendQueue;          // 发送 IRP 队列
    
    // 事件通知
    LONG EventFlags;               // FD_READ | FD_WRITE | ...
    PKEVENT EventObject;           // 事件对象
    
    // select 支持
    LIST_ENTRY PollList;           // select 列表
    LONG PollEvents;               // 关心的事件
    
    // 缓冲
    ULONG ReceiveBufferSize;
    ULONG SendBufferSize;
    
    // 工作项
    PIO_WORKITEM WorkItem;         // 异步工作项
} AFD_FCB, *PAFD_FCB;

这个结构通过文件对象的 FsContext 字段关联,每个 socket 调用都通过文件对象找到对应的 FCB。

AFD 状态机

AFD 端点的状态机是 socket 生命周期的核心。状态转换如下:

复制代码
CREATED → BOUND → LISTENING → CONNECTED → DISCONNECTING → CLOSED
         ↘ CONNECTING ↗

每个状态转换都有严格的前置条件:

  • bind 只能在 CREATED 状态调用
  • listen 只能在 BOUND 状态调用
  • connect 可以在 BOUND 或 CREATED 状态调用
  • send/recv 只能在 CONNECTED 状态调用
  • close 可以在任何状态调用

AFD 通过 SocketAcquireStateLock 宏保护状态转换的原子性,防止并发调用导致的状态混乱。

TDI 通信机制详解

AFD 与 TCP/IP 协议栈通过 TDI(Transport Driver Interface)通信。TDI 使用标准的 Windows I/O 机制,通过 IRP_MJ_INTERNAL_DEVICE_CONTROL 传递请求。

TDI 请求的关键 IOCTL 代码:

IOCTL 功能 参数
IOCTL_TDI_ASSOCIATE_ADDRESS 关联地址到连接 地址句柄
IOCTL_TDI_CONNECT 建立连接 远程地址
IOCTL_TDI_DISCONNECT 断开连接 超时、标志
IOCTL_TDI_SEND 发送数据(TCP) 缓冲、标志
IOCTL_TDI_RECEIVE 接收数据(TCP) 缓冲、标志
IOCTL_TDI_SEND_DATAGRAM 发送数据报(UDP) 目标地址、缓冲
IOCTL_TDI_RECEIVE_DATAGRAM 接收数据报(UDP) 缓冲、源地址
IOCTL_TDI_QUERY_INFORMATION 查询 TDI 信息 信息类型
IOCTL_TDI_SET_EVENT_HANDLER 设置事件回调 事件类型、回调

AFD 在发送 TDI 请求时,需要构造相应的 IRP 和 IO_STACK_LOCATION。例如,发送 TCP 数据的过程:

c 复制代码
NTSTATUS AfdSend(PAFD_FCB Fcb, PIRP Irp) {
    PIRP TdiIrp;
    PMDL Mdl;
    
    // 1. 锁定用户缓冲区
    Mdl = IoAllocateMdl(UserBuffer, Length, FALSE, FALSE, NULL);
    MmProbeAndLockPages(Mdl, UserMode, IoAccessMode);
    
    // 2. 构造 TDI_SEND IRP
    TdiIrp = TdiBuildInternalDeviceControlIrp(
        TDI_SEND,
        Fcb->TdiDeviceObject,
        Fcb->TdiConnectionHandle,
        NULL, NULL);
    
    TdiBuildSend(TdiIrp, Fcb->TdiConnectionHandle, Mdl, 0, Length, NULL);
    
    // 3. 发送并等待
    Status = IoCallDriver(Fcb->TdiDeviceObject, TdiIrp);
    
    return Status;
}

select 的实现机制

select 是 BSD socket API 中的多路复用机制,允许应用同时监控多个 socket 的状态变化。AFD 的 select 实现涉及复杂的同步机制:

  1. 请求收集 :应用调用 select 时,AFD 收集所有关心的 socket 和事件(可读、可写、异常)。

  2. 立即检查:AFD 首先立即检查每个 socket 的状态,如果有就绪的 socket,立即返回。

  3. 等待注册:如果没有就绪的 socket,AFD 将请求挂起,将 IRP 放入每个 socket 的等待队列。

  4. 事件触发:当数据到达或连接建立时,TCP/IP 通过 TDI 事件通知 AFD。AFD 检查等待队列,如果有匹配的等待请求,完成对应的 IRP。

  5. 超时处理:如果指定了超时时间,AFD 使用内核定时器在超时后完成 IRP。

c 复制代码
NTSTATUS AfdSelect(PAFD_FCB Fcb, PAFD_POLL_INFO PollInfo) {
    // 1. 立即检查
    if (CheckSocketReady(Fcb, PollInfo->Events)) {
        PollInfo->ReadyEvents = GetReadyEvents(Fcb);
        return STATUS_SUCCESS;
    }
    
    // 2. 注册等待
    if (PollInfo->Timeout == 0) {
        return STATUS_TIMEOUT;  // 非阻塞模式
    }
    
    // 3. 挂起 IRP
    InsertTailList(&Fcb->PollList, &PollInfo->ListEntry);
    return STATUS_PENDING;
}

WSAEventSelect 与 WSAAsyncSelect

Windows 提供了两种异步通知机制:

  1. WSAEventSelect :将 socket 事件映射到 Windows 事件对象。当指定事件发生时,AFD 设置对应的事件对象,应用通过 WaitForSingleObject 等待。

  2. WSAAsyncSelect:将 socket 事件映射到窗口消息。当指定事件发生时,AFD 向指定窗口投递消息。应用通过窗口过程处理消息。

这两种机制都依赖于 AFD 的事件通知系统。AFD 在端点结构中维护事件标志(EventFlags),当底层协议栈触发事件时,AFD 检查这些标志并触发相应的通知。

Winsock 分层架构的深层分析

Winsock 的分层架构是 Windows 网络编程的核心设计。这个架构包括四个主要层次:

  1. 应用层:使用 Winsock API 的网络应用程序。

  2. ws2_32.dll:Winsock 2 的用户态 API 层。它不直接处理 socket 操作,而是通过 SPI(Service Provider Interface)将调用转发给服务提供者。

  3. msafd.dll:Microsoft 的默认服务提供者。它将 Winsock 调用翻译为对 AFD 驱动的 IOCTL 调用。

  4. afd.sys:内核态的 socket 实现。它维护 socket 状态,处理异步 I/O,通过 TDI 与协议栈通信。

这种分层设计的好处是:

  • 可扩展性:可以插入第三方服务提供者(LSP)实现特殊功能
  • 兼容性:应用层 API 保持稳定,底层实现可以变化
  • 灵活性:可以为不同协议栈使用不同的服务提供者

socket 句柄的本质

在 Winsock 中,SOCKET 类型实际上是一个句柄值。在 ReactOS 的实现中,这个句柄指向 AFD 驱动创建的文件对象。应用通过 socket() 创建 socket 时,实际发生的是:

  1. ws2_32.dll 调用 msafd.dllWSPSocket
  2. WSPSocket 调用 NtCreateFile 打开 \Device\Afd
  3. AFD 驱动创建 AFD_FCB 结构,关联到文件对象
  4. 返回文件句柄作为 SOCKET

后续的 bindconnectsendrecv 等调用都通过这个文件句柄进行,AFD 通过文件对象的 FsContext 找到对应的 FCB。

异步 I/O 的实现细节

Windows 支持多种异步 I/O 模式,AFD 需要支持所有这些模式:

  1. 阻塞模式 :默认模式,send/recv 调用阻塞直到操作完成。

  2. 非阻塞模式 :通过 ioctlsocket(FIONBIO) 设置,send/recv 立即返回,如果没有数据则返回 WSAEWOULDBLOCK

  3. 重叠 I/O :通过 WSA_FLAG_OVERLAPPED 标志创建 socket,使用 WSARecv/WSASend 的重叠版本,通过事件或完成端口通知。

  4. 事件选择 :通过 WSAEventSelect 将 socket 事件映射到事件对象。

  5. 异步选择 :通过 WSAAsyncSelect 将 socket 事件映射到窗口消息。

AFD 通过检查文件对象的标志和 IRP 的参数来决定使用哪种模式。对于重叠 I/O,AFD 将 IRP 挂起,在操作完成时通过 IoCompleteRequest 通知 I/O 管理器。

与 Windows AFD 的对比

ReactOS 的 AFD 实现与 Windows 存在以下差异:

  1. 功能完整性:Windows 的 AFD 支持更多高级特性,如传输级别选项、条件接受、断开重用等。ReactOS 实现了核心功能,但部分高级特性尚未实现。

  2. 性能优化:Windows 的 AFD 包含大量性能优化,如零拷贝发送、接收缓冲合并等。ReactOS 的实现更注重功能正确性。

  3. 并发处理:Windows 的 AFD 使用更细粒度的锁机制,支持更高的并发连接数。ReactOS 使用全局锁,在高并发场景下可能成为瓶颈。

  4. 错误处理:Windows 的 AFD 对错误情况的处理更加完善,包括连接重置、超时、资源不足等。ReactOS 的错误处理相对简单。

AFD 的调试与诊断

ReactOS 的 AFD 包含调试支持,通过 AFD_DbgPrint 宏输出调试信息。调试级别包括:

  • MIN_TRACE:基本跟踪信息
  • MID_TRACE:中等详细程度的跟踪
  • MAX_TRACE:完整的跟踪信息
  • DEBUG_ULTRA:超详细调试信息

调试信息可以通过 DebugTraceLevel 变量控制,便于定位网络通信问题。


本章代码索引

文件 内容
afd/main.c(file:///d:/reactos/drivers/network/afd/afd/main.c) DriverEntry
afd/bind.c(file:///d:/reactos/drivers/network/afd/afd/bind.c) AfdBind
afd/connect.c(file:///d:/reactos/drivers/network/afd/afd/connect.c) AfdConnect
afd/listen.c(file:///d:/reactos/drivers/network/afd/afd/listen.c) AfdAccept
afd/read.c(file:///d:/reactos/drivers/network/afd/afd/read.c) AfdReceive
afd/write.c(file:///d:/reactos/drivers/network/afd/afd/write.c) AfdSend
afd/select.c(file:///d:/reactos/drivers/network/afd/afd/select.c) AfdSelect
afd/event.c(file:///d:/reactos/drivers/network/afd/afd/event.c) 异步事件
afd.h(file:///d:/reactos/drivers/network/afd/afd/afd.h) 数据结构
ws2_32/(file:///d:/reactos/dll/win32/ws2_32/) Winsock DLL
msafd/(file:///d:/reactos/dll/win32/msafd/) Service Provider
tcpip/(file:///d:/reactos/drivers/network/tcpip/tcpip/) TCP/IP 协议栈