CreatePersistentTcpPortReservation 函数 (iphlpapi.h)

CreatePersistentTcpPortReservation 函数为本地计算机上的连续 TCP 端口块创建永久性 TCP 端口预留。

语法

IPHLPAPI_DLL_LINKAGE ULONG CreatePersistentTcpPortReservation(
  [in]  USHORT   StartPort,
  [in]  USHORT   NumberOfPorts,
  [out] PULONG64 Token
);

参数

[in] StartPort

按网络字节顺序排列的起始 TCP 端口号。

[in] NumberOfPorts

要保留的 TCP 端口号数。

[out] Token

指向如果函数成功,则返回的端口预留令牌的指针。

返回值

如果函数成功,则返回值NO_ERROR。

如果函数失败,则返回值为以下错误代码之一。

返回代码 说明
ERROR_ACCESS_DENIED
访问被拒绝。 此错误在以下几种情况下返回:用户在本地计算机上缺少所需的管理权限,或者应用程序没有作为内置管理员 (RunAs 管理员) 在增强的 shell 中运行。
ERROR_INVALID_PARAMETER
向该函数传递了无效参数。

如果在 StartPort 或NumberOfPorts 参数中传递零,则返回此错误。 如果 NumberOfPorts 参数的端口块太大,则也会返回此错误,具体取决于可分配的端口块的 StartPort 参数,该参数的可分配端口块将超过可分配的最大端口数。

ERROR_SHARING_VIOLATION
进程无法访问该文件,因为它正在被另一个进程使用。 如果已使用 StartPort 和NumberOfPorts 参数指定的 TCP 端口块中的 TCP 端口,则返回此错误。 如果 StartPort 和NumberOfPorts 参数指定的 TCP 端口块的永久性预留与已创建的 TCP 端口块的永久性预留匹配或重叠,则也会返回此错误。
其他
使用 FormatMessage 获取返回错误的消息字符串。

注解

CreatePersistentTcpPortReservation 函数是在 Windows Vista 及更高版本上定义的。

CreatePersistentTcpPortReservation 函数用于为 TCP 端口块添加永久性预留。

需要保留端口的应用程序和服务分为两类。 第一个类别包括需要特定端口作为其操作的一部分的组件。 此类组件通常更倾向于在应用程序清单中 (在安装时指定所需的端口,例如) 。 第二类包括运行时需要任何可用端口或端口块的组件。

这两个类别对应于特定和通配符端口预留请求。 特定的预留请求可能是永久性的,也可以是运行时,而通配符端口预留请求仅在运行时受支持。

CreatePersistentTcpPortReservation 函数使应用程序或服务能够保留 TCP 端口的永久性块。 永久性 TCP 端口预留记录在 Windows 中 TCP 模块的持久存储中。

调用方通过指定所需的端口数以及是否需要特定范围来获取永久性端口预留。 如果请求可以满足, CreatePersistentTcpPortReservation 函数将返回唯一不透明的ULONG64令牌,该令牌随后标识预留。 可以通过调用 DeletePersistentTcpPortReservation 函数释放永久性 TCP 端口预留。 请注意,每次重启系统时,给定的永久性 TCP 端口预留的令牌可能会更改。

Windows 不会为使用这些函数获取的永久性预留实现组件间安全性。 这意味着,如果向某个组件授予获取任何永久性端口预留的能力,该组件将自动获得使用授予系统上任何其他组件的任何永久性端口预留的能力。 对运行时预留强制实施进程级安全性,但此类控制不能扩展到使用 CreatePersistentTcpPortReservationCreatePersistentUdpPortReservation 函数创建的持久端口预留。

获取永久性 TCP 端口预留后,应用程序可以通过打开 TCP 套接字,然后调用 WSAIoctl 函数(指定 SIO_ASSOCIATE_PORT_RESERVATION IOCTL),并在对套接字上的 绑定 函数发出调用之前传递预留令牌,从而从 TCP 端口预留请求端口分配。

SIO_ACQUIRE_PORT_RESERVATION IOCTL 可用于请求 TCP 或 UDP 端口块的运行时预留。 对于运行时端口预留,端口池要求从向其授予预留的套接字的进程使用预留。 运行时端口预留仅持续到调用 SIO_ACQUIRE_PORT_RESERVATION IOCTL 的套接字的生存期。 相比之下,使用 CreatePersistentTcpPortReservation 函数创建的永久性端口预留可由任何能够获取永久性预留的进程使用。

CreatePersistentTcpPortReservation 函数只能由以 Administrators 组成员身份登录的用户调用。 如果 CreatePersistentTcpPortReservation 由不是 Administrators 组成员的用户调用,则函数调用将失败并返回 ERROR_ACCESS_DENIED 。 此函数也可能因为 Windows Vista 及更高版本上的用户帐户控制 (UAC) 而失败。 如果包含此函数的应用程序由以管理员组成员身份登录(而非内置管理员)的用户执行,则此调用将失败,除非应用程序已在清单文件中标记为 requestedExecutionLevel 设置为 requireAdministrator。 如果应用程序缺少此清单文件,则作为管理员组成员(而非内置管理员)登录的用户必须在增强的 shell 中执行应用程序,因为内置管理员 (RunAs 管理员) 才能使此功能成功。

示例

以下示例创建永久性 TCP 端口预留,然后创建套接字并从端口预留分配端口,然后关闭套接字并删除 TCP 端口预留。

此示例必须由管理员组成员的用户运行。 运行此示例的最简单方法是使用增强的 shell,因为内置管理员 (RunAs 管理员) 。

#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 <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>

// Need to link with iphlpapi.lib
#pragma comment(lib, "iphlpapi.lib")

// 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
    ULONG64 resToken = { 0 };

    unsigned long status = 0;

    WSADATA wsaData = { 0 };
    int iResult = 0;

    SOCKET sock = INVALID_SOCKET;
    int iFamily = AF_INET;
    int iType = SOCK_STREAM;
    int iProtocol = IPPROTO_TCP;

    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 != 3) {
        wprintf(L"usage: %s <Starting Port> <Number of Ports>\n",
             argv[0]);
        wprintf(L"Creates a persistent TCP port reservation\n");
        wprintf(L"Example usage:\n");
        wprintf(L"   %s 5000 20\n", argv[0]);
        wprintf(L"   where StartPort=5000 NumPorts=20");
        return 1;
    }

    startPort = _wtoi(argv[1]);
    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[2]);
    if (numPorts < 0) {
        wprintf(L"Number of ports must be a positive number\n");
        return 1;
    }

    status =
        CreatePersistentTcpPortReservation((USHORT) startPortns, (USHORT) numPorts,
                                           &resToken);
    if (status != NO_ERROR) {
        wprintf(L"CreatePersistentTcpPortReservation returned error: %ld\n", status);
        return 1;
    }

    wprintf(L"CreatePersistentTcpPortReservation call succeeded\n");
    wprintf(L"  Token = %I64d\n", resToken);

    // Comment out this block if you don't want to create a socket and associate it with the 
    // persistent reservation

    // 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());
    else {
        wprintf(L"socket function succeeded\n");

        iResult =
            WSAIoctl(sock, SIO_ASSOCIATE_PORT_RESERVATION, (LPVOID) & resToken,
                     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 = AF_INET;
            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) );
                }
            }                  
        }

        if (sock != INVALID_SOCKET) {
            iResult = closesocket(sock);
            if (iResult == SOCKET_ERROR) {
                wprintf(L"closesocket failed with error = %d\n", WSAGetLastError());
                WSACleanup();
            }
        }
    }

    // comment out this block of code if you don't want to delete the reservation just created
    status = DeletePersistentTcpPortReservation((USHORT) startPortns, (USHORT) numPorts);
    if (status != NO_ERROR) {
        wprintf(L"DeletePersistentTcpPortReservation returned error: %ld\n", status);
        return 1;
    }
    wprintf(L"DeletePersistentTcpPortReservation call succeeded\n");

    return 0;
}

要求

要求
最低受支持的客户端 Windows Vista [仅限桌面应用]
最低受支持的服务器 Windows Server 2008 [仅限桌面应用]
目标平台 Windows
标头 iphlpapi.h
Library Iphlpapi.lib
DLL Iphlpapi.dll

另请参阅

CreatePersistentUdpPortReservation

DeletePersistentTcpPortReservation

DeletePersistentUdpPortReservation

LookupPersistentTcpPortReservation

LookupPersistentUdpPortReservation

SIO_ACQUIRE_PORT_RESERVATION

SIO_ASSOCIATE_PORT_RESERVATION

SIO_RELEASE_PORT_RESERVATION

WSAIoctl

bind