复制 SAN 的套接字句柄
在不同进程中运行的多个应用程序可以使用 Windows 套接字开关对共享的基础套接字执行操作。 但是,一次只能有一个应用程序在该共享的基础套接字上执行操作。
若要使用共享的基础套接字,应用程序必须通过以下方式之一检索该基础套接字的重复句柄:
直接调用 Windows 套接字 WSADuplicateSocket 函数
调用是在控制进程的上下文中进行的, (创建套接字的进程) 。
间接调用 Win32 DuplicateHandle 函数
调用是在非控制进程的上下文中进行的, (创建套接字的进程) 。
使用句柄继承机制
子进程 (非控制进程) 继承在其父进程中创建的所有或部分句柄 (控制进程) 。
正常连接关闭期间
如果控制进程中的应用程序关闭套接字并退出,而某些数据仍要发送,则剩余的数据将缓冲在 Windows 套接字 DLL 中。 系统服务进程上下文中的另一个应用程序 (非控制进程) 随后发送此数据。
Windows 套接字开关与 TCP/IP 提供程序一起检测并处理上述每个条件。 开关一次只允许一个进程执行传输数据或更改基础共享套接字的状态的操作。 进程根据需要动态交换基础套接字的控制,以执行请求的操作。 开关序列化不同进程请求在共享套接字上执行的操作,并按先入先出 (FIFO) 顺序执行这些操作。 开关会等待所有正在进行的操作完成,然后再将基础套接字的控制权交换到另一个进程。 从逻辑上讲,一旦非控制进程请求限定操作,开关就会从控制进程中获取对基础套接字的控制。 失去控制后,如果原始控制进程请求限定操作,则开关会将原始控制进程视为非控制进程。 请注意,在非控制进程实际使用重复套接字句柄进行数据传输或状态更改操作之前,开关不会对重复的套接字句柄执行任何操作。
交换机和相应的 SAN 服务提供程序将加载到共享对特定基础套接字的访问权限的所有进程中。 交换机在共享套接字的所有进程中维护自己的套接字上下文和连接状态信息。 SAN 服务提供程序需要仅在任何给定时间点控制基础套接字的进程中维护其套接字上下文和连接状态信息。 每当交换机需要交换时,SAN 服务提供程序必须将其上下文和连接状态信息的控制权从当前控制进程交换到下一个控制进程,如以下顺序所述。 为了最大程度地减少交换所需的资源量,SAN 服务提供商可以在共享基础套接字的所有进程中维护其上下文和连接状态信息。
由于在应用程序调用 connect 或 listen 函数之前,交换机不会创建与应用程序套接字对应的 SAN 套接字,因此交换机无法在连接或侦听应用程序套接字之前请求 SAN 服务提供程序执行交换操作。 即使在连接应用程序套接字或侦听应用程序套接字之后,在交换机请求 SAN 服务提供商交换对套接字的控制之前,也必须满足以下条件之一:
不控制套接字的进程启动数据传输。 在控制进程启动的所有数据传输操作完成之前,SAN 服务提供程序不会交换对套接字的控制。
不控制套接字的进程调用 WSAAccept、 WSPAccept 或 AcceptEx 函数,以在侦听套接字上启动连接验收操作。 在控制进程发起的所有接受请求完成之前,SAN 服务提供商不会交换对套接字的控制。
开关执行以下步骤,将连接的 SAN 套接字的控制从控制进程交换到下一个控制进程 (有关交换过程的概述,请参阅 WSPDuplicateSocket function.) 文档的“备注”部分中的表:
开关在控制过程中暂停处理来自应用程序的新请求。 当 SAN 套接字上正在进行的所有发送和 RDMA 操作都已完成时,交换机会调用 SAN 服务提供商的 WSPSend 函数,以向连接的对等方发送消息以请求暂停会话,并调用 SAN 服务提供商的 WSPDeregisterMemory 函数来释放用于发送操作的所有本地缓冲区。 因此,对等连接处的交换机会暂停新应用程序请求的处理,等待 SAN 套接字上正在进行的所有发送和 RDMA 操作完成,并释放所有 RDMA 内存。 接下来,对等连接会发送一条答复消息,指示会话已暂停。 收到此确认消息后,本地终结点上的交换机会调用 SAN 服务提供商的 WSPDeregisterRdmaMemory 函数来释放所有 RDMA 内存。 此时,连接两个终结点上的 SAN 套接字只能有挂起的接收请求。 这些接收请求在远程对等方的 SAN 套接字上保持挂起状态,以允许重新激活会话。 控制过程中本地 SAN 套接字上的接收请求在下一步中完成。 当连接暂停时,远程对等连接处的交换机会排队新的阻止或重叠请求,缓冲新的非阻止发送到SO_SNDBUF设置,在达到缓冲区限制后使新的非阻止发送失败,并使用 WSAEWOULDBLOCK 使所有新的非阻止接收失败。 控制进程中的本地交换机处理应用程序套接字上的新请求,就好像进程没有对套接字的控制一样。
会话暂停后,交换机在控制进程中调用 SAN 服务提供商的 WSPDuplicateSocket 函数,以指示 SAN 服务提供商将套接字上下文传输到下一个控制进程的地址空间中。 开关指定 WSPDuplicateSocket 的 dwProcessId 参数中的下一个控制进程。 WSPDuplicateSocket 函数必须调用 WPUCompleteOverlappedRequest 函数,以以成功状态和零字节完成套接字上所有未完成的接收请求。 SAN 服务提供程序还必须自动释放与这些请求关联的所有缓冲区。 SAN 服务提供程序释放所有缓冲区,因为在 WSPDuplicateSocket 返回后,交换机不会在 SAN 套接字上请求更多操作。 唯一可能的例外是 WSPCloseSocket 函数调用,如下一步中所述。 WSPDuplicateSocket 返回后,开关将值保存在 lpProtocolInfo 输出参数指向的WSAPROTOCOL_INFOW结构的 dwProviderReserved 成员中。 开关使用此值来标识下一个控制进程的上下文中的基础套接字。 因此, dwProviderReserved 中的值必须唯一标识系统上所有进程中的基础套接字和该套接字的连接。 此外,此值必须仅在 WSPDuplicateSocket 的 dwProcessId 参数中指定的开关的进程上下文中有效。
将套接字上下文传输到下一个控制进程的地址空间后,交换机会在下一个控制进程的上下文中调用 SAN 服务提供程序的 WSPSocket 函数。 在此调用中,开关将 WSPDuplicateSocket 调用中返回的基础套接字的值传递给 lpProtocolInfo 输入参数指向的 WSAPROTOCOL_INFOW 结构的 dwProviderReserved 成员。 如果下一个控制进程未请求创建 SAN 套接字,则 SAN 服务提供程序必须创建一个新套接字并调用 WPUCreateSocketHandle 函数以获取任何新套接字所需的句柄。 如果 SAN 套接字是在下一个控制进程的上下文中创建的,则 SAN 服务提供程序可以重新激活以前的套接字,并为之前使用的套接字返回相同的描述符。 在这种情况下,SAN 服务提供程序不应调用 WPUCreateSocketHandle,但应继续使用交换机提供的原始套接字句柄。 或者,SAN 服务提供程序可以创建新的套接字,而不考虑进程中以前是否存在套接字。 在这种情况下,交换机必须在下一个控制进程的上下文中调用 SAN 服务提供程序的 WSPCloseSocket 函数,以释放以前的套接字描述符。
开关在下一个控制过程中重新启动处理来自应用程序的新请求。
开关以类似的方式复制侦听套接字,只不过不需要该开关来暂停会话。 在控制过程中调用 SAN 服务提供商的 WSPDuplicateSocket 函数之前,开关将等待它完成由应用程序的 accept 和 AcceptEx 调用发起的所有 WSPAccept 调用。
由于交换机在调用 SAN 服务提供商的 WSPDuplicateSocket 函数之前暂停处理 SAN 套接字上的新请求,因此 SAN 服务提供商可以在控制过程中释放与本地终结点关联的所有资源。 SAN 服务提供程序甚至可以终止基础连接。 如果 SAN 服务提供程序关闭控制进程中的基础连接,则 SAN 服务提供程序必须在交换机在下一个控制进程中调用 SAN 服务提供商的 WSPSocket 函数后重新建立连接。 WSPSocket 调用返回后,从交换机的角度来看,下一个控制进程中的 SAN 套接字必须处于相同的状态,与控制进程中的 SAN 套接字在调用 SAN 服务提供商的 WSPDuplicateSocket 函数的交换机之前处于相同状态。
如果 SAN NIC 支持在不同进程中运行的终结点之间共享资源,则 SAN 服务提供程序不必在接收 WSPDuplicateSocket 调用之前在控制进程中释放本地终结点的资源。 在这种情况下,与本地终结点关联的 SAN 套接字在以前的控制进程中保持非活动状态,直到交换机从下一个控制进程交换套接字上下文或调用 SAN 服务提供程序的 WSPCloseSocket 函数显式关闭套接字。 由于大多数应用程序在最初创建套接字的过程中执行对套接字的最终访问(通常是为了关闭连接)。如果 SAN 服务提供程序在将套接字的控制权切换到下一个控制进程后在控制进程中保留套接字上下文,则 SAN 服务提供程序可以提高性能。
请注意,在所有情况下,在交换机调用 SAN 服务提供程序的 WSPCloseSocket 函数显式关闭套接字之前,SAN 套接字描述符必须保持有效。 即使在接收 WSPDuplicateSocket 调用之前,SAN 服务提供程序在特定进程中释放套接字的所有资源,在交换机在该描述符上调用 WSPCloseSocket 之前,SAN 服务提供程序也不得重用套接字的描述符。
意外的进程退出或其他错误情况可能会中断 SAN 服务提供商的套接字重复操作。 例如,资源短缺可能会导致此类中断。 开关处理此类错误条件,就像处理任何其他错误情况一样。 如有必要,开关将关闭所有进程中与基础套接字关联的所有描述符,以强制终止套接字的连接。 如果可能,远程对等方上的 SAN 服务提供程序应完成接收传入数据的 WSPRecv 调用,并附带相应的错误代码,例如 WSAECONNRESET。 此错误代码通知远程对等方连接终止。 如果远程对等方上的交换机未收到此连接终止指示,则如果请求挂起的系统失败,远程对等机处的交换机将超时挂起的连接。