SIO_ACQUIRE_PORT_RESERVATION控制代码
说明
SIO_ACQUIRE_PORT_RESERVATION控制代码获取 TCP 或 UDP 端口块的运行时预留。
若要执行此操作,请使用以下参数调用 WSAIoctl 或 WSPIoctl 函数。
int WSAIoctl(
(socket) s, // descriptor identifying a socket
SIO_ACQUIRE_PORT_RESERVATION, // dwIoControlCode
(LPVOID) lpvInBuffer, // pointer to an INET_PORT_RANGE structure
(DWORD) cbInBuffer, // size, in bytes, of the input buffer
(LPVOID) lpvOutBuffer, // pointer to an INET_PORT_RESERVATION_INSTANCE structure
(DWORD) cbOutBuffer, // size, in bytes, of the output buffer
(LPDWORD) lpcbBytesReturned, // number of bytes returned
(LPWSAOVERLAPPED) lpOverlapped, // OVERLAPPED structure
(LPWSAOVERLAPPED_COMPLETION_ROUTINE) lpCompletionRoutine, // completion routine
);
int WSPIoctl(
(socket) s, // descriptor identifying a socket
SIO_ACQUIRE_PORT_RESERVATION, // dwIoControlCode
(LPVOID) lpvInBuffer, // pointer to an INET_PORT_RANGE structure
(DWORD) cbInBuffer, // size, in bytes, of the input buffer
(LPVOID) lpvOutBuffer, // pointer to a INET_PORT_RESERVATION_INSTANCE structure
(DWORD) cbOutBuffer, // size, in bytes, of the output buffer
(LPDWORD) lpcbBytesReturned, // number of bytes returned
(LPWSAOVERLAPPED) lpOverlapped, // OVERLAPPED structure
(LPWSAOVERLAPPED_COMPLETION_ROUTINE) lpCompletionRoutine, // completion routine
(LPWSATHREADID) lpThreadId, // a WSATHREADID structure
(LPINT) lpErrno // a pointer to the error code.
);
参数
S
标识套接字的描述符。
dwIoControlCode
操作的控制代码。 对此操作使用 SIO_ACQUIRE_PORT_RESERVATION 。
lpvInBuffer
指向输入缓冲区的指针。 此参数包含指向 INET_PORT_RANGE 结构的指针,该结构指定要保留的起始点编号和端口数。
cbInBuffer
输入缓冲区的大小(以字节为单位)。 此参数应为 INET_PORT_RANGE 结构的大小。
lpvOutBuffer
指向输出缓冲区的指针。 成功输出时,此参数包含指向 INET_PORT_RESERVATION_INSTANCE 结构的指针。
cbOutBuffer
输出缓冲区的大小(以字节为单位)。 此参数必须至少为 INET_PORT_RESERVATION_INSTANCE 结构的大小。
lhttpBytesReturned
指向变量的指针,该变量接收输出缓冲区中存储的数据的大小(以字节为单位)。
如果输出缓冲区太小,调用会失败, WSAGetLastError 将返回 WSAEINVAL,并且 lhttpBytesReturned 参数指向 DWORD 值零。
如果 lpOverlapped 为 NULL,则成功调用时返回的lBytesReturned 参数指向的 DWORD 值不能为零。
如果重叠套接字的 lpOverlapped 参数不为 NULL ,则将启动无法立即完成的操作,并在以后指示完成。 返回的lBytesReturned 参数指向的 DWORD 值可能为零,因为在重叠操作完成之前无法确定存储的数据的大小。 当操作完成时发出相应的完成方法信号时,可以检索最终完成状态。
lpvOverlapped
指向 WSAOVERLAPPED 结构的指针。
如果 创建的套接字 没有重叠属性,则忽略 lpOverlapped 参数。
如果使用重叠属性打开了 , 并且 lpOverlapped 参数不为 NULL,则该操作将作为重叠 (异步) 操作执行。 在这种情况下, lpOverlapped 参数必须指向有效的 WSAOVERLAPPED 结构。
对于重叠操作, WSAIoctl 或 WSPIoctl 函数将立即返回,并在操作完成时发出相应的完成方法信号。 否则,在操作完成或发生错误之前,函数不会返回 。
lpCompletionRoutine
类型:_In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINE
指向完成操作时调用的完成例程的指针, (忽略非重叠套接字) 。
lpThreadId
指向 WSATHREADID 结构的指针,供提供程序在后续调用 WPUQueueApc 时使用。 在 WPUQueueApc 函数返回之前,提供程序应存储引用的 WSATHREADID 结构 (而不是指向同一) 的指针。
注意 此参数仅适用于 WSPIoctl 函数。
lpErrno
指向错误代码的指针。
注意 此参数仅适用于 WSPIoctl 函数。
返回值
如果操作成功完成, WSAIoctl 或 WSPIoctl 函数将返回零。
如果操作失败或挂起, WSAIoctl 或 WSPIoctl 函数将返回 SOCKET_ERROR。 若要获取扩展的错误信息,请调用 WSAGetLastError。
错误代码 | 含义 |
---|---|
WSA_IO_PENDING | 重叠 I/O 操作正在进行中。 如果已成功启动重叠操作,并且稍后将指示完成,则返回此值。 |
WSA_OPERATION_ABORTED | 由于发生线程退出或应用程序请求,I/O 操作已中止。 如果由于套接字关闭或执行 SIO_FLUSH IOCTL 命令而取消了重叠的操作,则返回此错误。 |
WSAEFAULT | 系统在尝试在调用中使用指针参数时检测到指针地址无效。 此错误返回的 lpvInBuffer、 lpvoutBuffer、 lhttpBytesReturned、 lpOverlapped 或 lpCompletionRoutine 参数未完全包含在用户地址空间的有效部分。 |
WSAEINPROGRESS | 阻止操作当前正在执行。 如果在回调正在进行时调用函数,则返回此错误。 |
WSAEINTR | 对 WSACancelBlockingCall 的调用中断了阻止操作。 如果阻止操作中断,则返回此错误。 |
WSAEINVAL | 提供了无效的参数。 如果 dwIoControlCode 参数不是有效的命令,或者指定的输入参数不可接受,或者命令不适用于指定的套接字类型,则返回此错误。 |
WSAENETDOWN | 套接字操作遇到了一个已死的网络。 如果网络子系统发生故障,则返回此错误。 |
WSAENOTSOCK | 尝试对不是套接字的内容执行操作。 如果描述符 不是套接字,则返回此错误。 |
WSAEOPNOTSUPP | 所引用对象的类型不支持尝试的操作。 如果不支持指定的 IOCTL 命令,则返回此错误。 如果传输提供程序不支持 SIO_ACQUIRE_PORT_RESERVATION IOCTL,也会返回此错误。 在 UDP 或 TCP 以外的套接字上尝试使用 SIO_ACQUIRE_PORT_RESERVATION IOCTL 时,也会返回此错误。 |
备注
SIO_ACQUIRE_PORT_RESERVATION IOCTL 在 Windows Vista 和更高版本的操作系统上受支持。
需要保留端口的应用程序和服务分为两类。 第一个类别包括需要特定端口作为其操作的一部分的组件。 此类组件通常倾向于在应用程序清单中 (在安装时指定所需的端口,例如) 。 第二个类别包括在运行时需要任何可用端口或端口块的组件。 这两个类别对应于特定和通配符端口预留请求。 特定预留请求可以是永久性的或运行时的,而通配符端口预留请求仅在运行时受支持。
SIO_ACQUIRE_PORT_RESERVATION IOCTL 用于请求 TCP 或 UDP 端口块的运行时预留。 对于运行时端口预留,端口池要求从向其授予预留的套接字的进程使用预留。 运行时端口预留的持续时间仅为调用 SIO_ACQUIRE_PORT_RESERVATION IOCTL 的套接字的生存期。 相比之下,使用 CreatePersistentTcpPortReservation 或 CreatePersistentUdpPortReservation 函数创建的永久性端口预留可由能够获取永久性预留的任何进程使用。
获取运行时 TCP 或 UDP 端口预留后,应用程序可以通过打开 TCP 或 UDP 套接字,然后调用 WSAIoctl 函数以指定 SIO_ASSOCIATE_PORT_RESERVATION IOCTL 并传递预留令牌,然后再对套接字上的绑定函数发出调用,从而从端口预留请求分配。
如果 lpOverlapped 和 lpCompletionRoutine 参数均为 NULL,则此函数中的套接字将被视为非重叠套接字。 对于非重叠套接字,将忽略 lpOverlapped 和 lpCompletionRoutine 参数,但 当套接字 处于 阻止模式时,函数可能会阻止。 如果 套接字 处于 非阻止模式,此函数仍将阻止,因为此特定 IOCTL 不支持非阻止模式。
对于重叠的套接字,将启动无法立即完成的操作,并在以后指示完成。
任何 IOCTL 都可能无限期阻止,具体取决于服务提供商的实现。 如果应用程序不能容忍 WSAIoctl 或 WSPIoctl 函数调用中的阻塞,则会建议对特别可能阻止的 IOCTL 使用重叠 I/O。
在以下情况下 ,SIO_ACQUIRE_PORT_RESERVATION IOCTL 可能会失败并出现 WSAEINTR 或 WSA_OPERATION_ABORTED :
- I/O 管理器取消请求。
- 套接字已关闭。
示例
以下示例获取运行时端口预留,然后创建一个套接字,并从该套接字的运行时端口预留中分配一个端口,然后关闭该套接字并释放运行时端口预留。
#ifndef UNICODE
#define UNICODE
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h.>
#include <winsock2.h>
#include <mstcpip.h>
#include <ws2ipdef.h>
#include <stdio.h>
#include <stdlib.h>
// Need to link with Ws2_32.lib for Winsock functions
#pragma comment(lib, "ws2_32.lib")
int wmain(int argc, WCHAR ** argv)
{
// Declare and initialize variables
int startPort = 0; // host byte order
int numPorts = 0;
USHORT startPortns = 0; // Network byte order
INET_PORT_RANGE portRange = { 0 };
INET_PORT_RESERVATION_INSTANCE portRes = { 0 };
unsigned long status = 0;
WSADATA wsaData = { 0 };
int iResult = 0;
SOCKET sock = INVALID_SOCKET;
int iFamily = AF_INET;
int iType = 0;
int iProtocol = 0;
SOCKET sockRes = INVALID_SOCKET;
DWORD bytesReturned = 0;
// Note that the sockaddr_in struct works only with AF_INET not AF_INET6
// An application needs to use the sockaddr_in6 for AF_INET6
sockaddr_in service;
sockaddr_in sockName;
int nameLen = sizeof (sockName);
// Validate the parameters
if (argc != 6) {
wprintf
(L"usage: %s <addressfamily> <type> <protocol> <StartingPort> <NumberOfPorts>\n",
argv[0]);
wprintf(L"Opens a socket for the specified family, type, & protocol\n");
wprintf
(L"and then acquires a runtime port reservation for the protocol specified\n");
wprintf(L"%ws example usage\n", argv[0]);
wprintf(L" %ws 2 2 17 5000 20\n", argv[0]);
wprintf(L" where AF_INET=2 SOCK_DGRAM=2 IPPROTO_UDP=17 StartPort=5000 NumPorts=20", argv[0]);
return 1;
}
iFamily = _wtoi(argv[1]);
iType = _wtoi(argv[2]);
iProtocol = _wtoi(argv[3]);
startPort = _wtoi(argv[4]);
if (startPort < 0 || startPort > 65535) {
wprintf(L"Starting point must be either 0 or between 1 and 65,535\n");
return 1;
}
startPortns = htons((USHORT) startPort);
numPorts = _wtoi(argv[5]);
if (numPorts < 0) {
wprintf(L"Number of ports must be a positive number\n");
return 1;
}
portRange.StartPort = startPortns;
portRange.NumberOfPorts = (USHORT) numPorts;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
wprintf(L"WSAStartup failed with error = %d\n", iResult);
return 1;
}
sock = socket(iFamily, iType, iProtocol);
if (sock == INVALID_SOCKET) {
wprintf(L"socket function failed with error = %d\n", WSAGetLastError());
WSACleanup();
return 1;
} else {
wprintf(L"socket function succeeded\n");
iResult =
WSAIoctl(sock, SIO_ACQUIRE_PORT_RESERVATION, (LPVOID) & portRange,
sizeof (INET_PORT_RANGE), (LPVOID) & portRes,
sizeof (INET_PORT_RESERVATION_INSTANCE), &bytesReturned, NULL, NULL);
if (iResult != 0) {
wprintf(L"WSAIoctl(SIO_ACQUIRE_PORT_RESERVATION) failed with error = %d\n",
WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
} else {
wprintf
(L"WSAIoctl(SIO_ACQUIRE_PORT_RESERVATION) succeeded, bytesReturned = %u\n",
bytesReturned);
wprintf(L" Starting port=%d, Number of Ports=%d, Token=%I64d\n",
htons(portRes.Reservation.StartPort),
portRes.Reservation.NumberOfPorts, portRes.Token);
sockRes = socket(iFamily, iType, iProtocol);
if (sockRes == INVALID_SOCKET) {
wprintf(L"socket function for second socket failed with error = %d\n",
WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
} else {
wprintf(L"socket function for second socket succeeded\n");
iResult =
WSAIoctl(sock, SIO_ASSOCIATE_PORT_RESERVATION,
(LPVOID) & portRes.Token, sizeof (ULONG64), NULL, 0,
&bytesReturned, NULL, NULL);
if (iResult != 0) {
wprintf
(L"WSAIoctl(SIO_ASSOCIATE_PORT_RESERVATION) failed with error = %d\n",
WSAGetLastError());
} else {
wprintf
(L"WSAIoctl(SIO_ASSOCIATE_PORT_RESERVATION) succeeded, bytesReturned = %u\n",
bytesReturned);
service.sin_family = (ADDRESS_FAMILY) iFamily;
service.sin_addr.s_addr = INADDR_ANY;
service.sin_port = 0;
iResult = bind(sock, (SOCKADDR *) & service, sizeof (service));
if (iResult == SOCKET_ERROR)
wprintf(L"bind failed with error = %d\n", WSAGetLastError());
else {
wprintf(L"bind succeeded\n");
iResult = getsockname(sock, (SOCKADDR *) & sockName, &nameLen);
if (iResult == SOCKET_ERROR)
wprintf(L"getsockname failed with error = %d\n",
WSAGetLastError());
else {
wprintf(L"getsockname succeeded\n");
wprintf(L"Port number allocated = %u\n",
ntohs(sockName.sin_port));
}
}
}
}
// comment out this block of code if you don't want to delete the reservation just created
iResult =
WSAIoctl(sock, SIO_RELEASE_PORT_RESERVATION, (LPVOID) & portRes.Token,
sizeof (ULONG64), NULL, 0, &bytesReturned, NULL, NULL);
if (iResult != 0) {
wprintf
(L"WSAIoctl(SIO_RELEASE_PORT_RESERVATION) failed with error = %d\n",
WSAGetLastError());
} else {
wprintf
(L"WSAIoctl(SIO_RELEASE_PORT_RESERVATION) succeeded, bytesReturned = %u\n",
bytesReturned);
}
}
if (sockRes != INVALID_SOCKET) {
iResult = closesocket(sockRes);
if (iResult == SOCKET_ERROR) {
wprintf(L"closesocket for second socket failed with error = %d\n",
WSAGetLastError());
}
}
if (sock != INVALID_SOCKET) {
iResult = closesocket(sock);
if (iResult == SOCKET_ERROR) {
wprintf(L"closesocket for first socket failed with error = %d\n",
WSAGetLastError());
}
}
}
WSACleanup();
return 0;
}
另请参阅
CreatePersistentTcpPortReservation
CreatePersistentUdpPortReservation
DeletePersistentTcpPortReservation
DeletePersistentUdpPortReservation
INET_PORT_RESERVATION_INSTANCE
LookupPersistentTcpPortReservation
LookupPersistentUdpPortReservation