C ++ Pipe [如何获取连接客户端的ipaddress或主机名。]

时间:2017-07-19 02:10:35

标签: c++ windows visual-studio winapi pipe

我想获取客户端IP地址或主机名。 我正在使用VS6.0& Windows 7旗舰版VS2010。

  服务器上的

CreateNamedPipe(...)如下:

SECURITY_ATTRIBUTES sa = {0};
hPipe = CreateNamedPipe( 
    _T("\\\\.\\pipe\\AnonymousPipe"),   // pipe name 
    PIPE_ACCESS_DUPLEX,  
    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
    PIPE_UNLIMITED_INSTANCES,
    0xD000, 
    0xD000, 
    NMPWAIT_USE_DEFAULT_WAIT, 
    &sa);

if (hPipe == INVALID_HANDLE_VALUE) 
{
    _tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError()); 
    return -1;
}

fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); 

if (fConnected) 
{ 
    printf("Client connected, creating a processing thread.\n");

    // Get Information Of Connected Client.
    // Client Ip Address or Hostname.
}

如何检测连接到服务器的客户端? 我可以使用netstat cmd行查看服务器和客户端之间的连接。

netstat -an|find "445"
TCP     0.0.0.0:445     0.0.0.0:0               LISTENING
TCP     0.0.0.0:445     192.168.125.115:4124    ESTABLISHED
TCP     0.0.0.0:445     192.168.125.192:4882    ESTABLISHED

字符串“192.168.125.115”&“192.168.125.192”是我的目标。 但我找不到在VS6.0中以编程方式检测连接的客户端IP的方法。

有人告诉我,使用GetNamedPipeClientComputerName()来解决这个问题。但

GetNamedPipeClientComputerName()
Minimum supported client Windows Vista [desktop apps only]
Minimum supported server Windows Server 2008 [desktop apps only]

许多客户仍在使用Windows XP。

我已经搜索了很多来解决这个问题,但我找不到代码描述。

感谢所有观众。

1 个答案:

答案 0 :(得分:1)

评论中已经回答的问题GetNamedPipeClientComputerName可用于此。它可以从Windows Vista获得。如果想在xp上使用它也可以通过下一个方式实现:

ULONG AltGetNamedPipeClientComputerName(HANDLE Pipe, PWSTR ClientComputerName, ULONG ClientComputerNameLength)
{
    static const char AttributeName[] = "ClientComputerName";

    if (HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL))
    {
        IO_STATUS_BLOCK iosb;

        NTSTATUS status = NtFsControlFile(Pipe,
            hEvent,
            NULL,
            NULL,
            &iosb,
            FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE,
            (void*)AttributeName,
            sizeof(AttributeName),
            ClientComputerName,
            ClientComputerNameLength*sizeof(WCHAR));

        if (status == STATUS_PENDING)
        {
            WaitForSingleObject(hEvent, INFINITE);
            status = iosb.Status;
        }

        CloseHandle(hEvent);

        return status == STATUS_NOT_FOUND ? ERROR_PIPE_LOCAL : RtlNtStatusToDosError(status);
    }

    return GetLastError();
}

这是一般的GetNamedPipeClientComputerName如何实现内部的。但是如果我们使用同步管道(如此创建没有FILE_FLAG_OVERLAPPED),我们可以简化功能 - 不需要创建事件 - NtFsControlFile永远不会返回STATUS_PENDING同步句柄。

ULONG AltGetNamedPipeClientComputerNameSync(HANDLE Pipe, PWSTR ClientComputerName, ULONG ClientComputerNameLength)
{
    static const char AttributeName[] = "ClientComputerName";

    IO_STATUS_BLOCK iosb;

    NTSTATUS status = NtFsControlFile(Pipe,
            hEvent,
            NULL,
            NULL,
            &iosb,
            FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE,
            (void*)AttributeName,
            sizeof(AttributeName),
            ClientComputerName,
            ClientComputerNameLength*sizeof(WCHAR));


    return status == STATUS_NOT_FOUND ? ERROR_PIPE_LOCAL : RtlNtStatusToDosError(status);
}
如果我们使用异步管道,从另一方面

- 我们可以而不是在FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE完成时等待,但是像处理异步的其他请求一样处理异步

一个非常重要的注意事项 - 我们不能在此使用DeviceIoControl,而只能使用NtFsControlFile - 这是因为DeviceIoControl内部检查dwIoControlCode以及FILE_DEVICE_FILE_SYSTEM } NtFsControlFile另外NtDeviceIoControlFileFSCTL_PIPE_GET_CONNECTION_ATTRIBUTE(来自wdk)中定义的ntifs.h

#define FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE CTL_CODE(FILE_DEVICE_NAMED_PIPE, 12, METHOD_BUFFERED, FILE_ANY_ACCESS)

FILE_DEVICE_NAMED_PIPE DeviceIoControl致电NtDeviceIoControlFile,但我们需要NtFsControlFile

请注意,这仅适用于服务器端管道端(由CreateNamedPipe创建),以防客户端是远程的。否则STATUS_NOT_FOUND将从NtFsControlFile返回。对此代码进行GetNamedPipeClientComputerName实施特殊检查并将其转换为ERROR_PIPE_LOCAL