如何区分Win32套接字句柄和其他管道句柄?

问题描述 投票:0回答:4

我需要确定我的代码未创建的句柄(

GetFileType()==FILE_TYPE_PIPE
)是否是套接字。似乎没有这方面的API。

我尝试过以下方法。总体思路是使用套接字特定的函数并将失败视为非套接字。

  • getsockopt()
    ——这是我的第一次尝试。不幸的是,当同一(非套接字)句柄上的许多线程调用时,它似乎挂起。
  • WSAEnumNetworkEvents()
    ——这就是 Gnulib 所做的,但如果句柄套接字,则会产生不良副作用。
  • getpeername()
    ——这就是 cygwin 所做的,但对于某些套接字来说这也会失败。猜测错误是否意味着套接字性似乎不可靠且未来安全。

我不介意该解决方案是否仅适用于某些版本的 Windows,例如Vista,在一般情况下我总是可以回退到其他方法。

winapi winsock
4个回答
2
投票

我想也许你可以尝试在你的句柄上调用 GetNamedPipeInfo()。如果调用成功你就知道该句柄是一个管道句柄,否则它一定是一个套接字。


1
投票

你尝试过吗

WSADuplicateSocket
。然后只需检查
WSAPROTOCOL_INFO
看看它是否实际上是一个命名管道...


1
投票

您也可以使用 GetNamedPipeHandleState(),并使用 GetLastError() 评估结果。


0
投票

我最终使用了

getpeername()
,但最近我们代码的一位用户报告说
getpeername()
似乎挂起了。这是令人惊讶的,因为例如一些相关文档说“即使在阻塞套接字上,某些功能 - 例如 getpeername - 也会立即完成”(这是在实际套接字和 Windows Sockets 1.1 的上下文中,但仍然如此)。

问题是,如果某个其他线程已经在同一输入流上对

ReadFile()
进行阻塞调用,则该输入流上的
getpeername()
将挂起。

所以,我使用下面的程序做了一个实验。当另一个线程已经在同一个输入句柄上处于阻塞

ReadFile()
时,结论是:

  • getpeername()
    将会挂起。
  • GetNamedPipeInfo()
    将挂起。更糟糕的是,在某些情况下,它似乎会导致
    ReadFile()
    提前成功返回,读取零字节。
  • GetNamedPipeHandleState()
    在我的实验中没有挂起,也没有任何不需要的副作用。

据我所知,所有这些结果都没有记录,因此它们可能会随着下一个 Windows 更新而改变。另外,到目前为止我只测试了管道输入,没有使用套接字输入流进行测试(!)。

/*

Demonstrate various ways to distinguish between socket and pipe for an
input stream classified as FILE_TYPE_PIPE. Some of them hang if some
other thread is doing a blocking ReadFile() on the same input handle
at the same time.

  cl.exe getpeername_hang.c Ws2_32.lib /MD /Z7 /MD /WX  /W3

In a cmd.exe window (we use "powershell ... | " just to make a pipe as STD_INPUT_HANDLE input for getpeername_hang.exe) :

Test using getpeername(). This hangs.
  powershell -nop -c "& {sleep 30}" | .\getpeername_hang.exe getpeername

Test using GetNamedPipeInfo(). This hangs. Note: In cygwin Emacs shell the call to GetNamedPipeInfo() makes ReadFile() return success with zero bytes read, which is really scary.
  powershell -nop -c "& {sleep 30}" | .\getpeername_hang.exe getnamedpipeinfo

Test using GetNamedPipeHandleStateA(). This does not hang.
  powershell -nop -c "& {sleep 30}" | .\getpeername_hang.exe getnamedpipehandlestatea

*/

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>

static DWORD WINAPI readingThreadFunction(LPVOID lpParam)
{
  HANDLE hInput = *(HANDLE*)lpParam;
  CHAR buffer[256];
  DWORD bytesRead;

  // Read from the input (blocking read)
  if (ReadFile(hInput, buffer, sizeof(buffer), &bytesRead, NULL)) {
    fprintf(stdout, "Read %lu bytes from input\n", (unsigned long) bytesRead);
    fflush(stdout);
  } else {
    fprintf(stderr, "ReadFile failed. Error %lu\n", (unsigned long)GetLastError());
    fflush(stderr);
  }

  return 0;
}

static int isSocketGetpeername(HANDLE h)
{
  SOCKADDR_STORAGE peerAddr;
  int peerAddrLen = (int) sizeof(peerAddr);

  if (getpeername((SOCKET)h, (struct sockaddr*)&peerAddr, &peerAddrLen) == 0) {
    fprintf(stdout, "Success from getpeername()\n");
    return 1;
  } else {
    fprintf(stderr, "getpeername failed. Error %lu\n", (unsigned long)WSAGetLastError());
    return 0;
  }
}

static int isSocketGetNamedPipeInfo(HANDLE h)
{
  if (GetNamedPipeInfo(h, NULL, NULL, NULL, NULL)) {
    fprintf(stdout, "Success from GetNamedPipeInfo()\n");
    return 0;
  } else {
    fprintf(stderr, "GetNamedPipeInfo() failed. Error %lu\n", (unsigned long)GetLastError());
    return 1;
  }
}

static int isSocketGetNamedPipeHandleStateA(HANDLE h)
{
  if (GetNamedPipeHandleStateA(h, NULL, NULL, NULL, NULL, NULL, 0)) {
    fprintf(stdout, "Success from GetNamedPipeHandleStateA()\n");
    return 0;
  } else {
    fprintf(stderr, "GetNamedPipeInfo() failed. Error %lu\n", (unsigned long)GetLastError());
    return 1;
  }
}

static char *getFileType(HANDLE h)
{
  DWORD fileType = GetFileType(h);
  switch (fileType)
    {
    case FILE_TYPE_DISK:
      return "FILE_TYPE_DISK";
    case FILE_TYPE_PIPE:
      return "FILE_TYPE_PIPE";
    case FILE_TYPE_CHAR:
      return "FILE_TYPE_CHAR";
    case  FILE_TYPE_UNKNOWN:
      return "FILE_TYPE_UNKNOWN";
    case FILE_TYPE_REMOTE:
      return "FILE_TYPE_REMOTE";
    default:
      return "GetFileType_IMPOSSIBLE_RESULT";
    }
}

int main(int argc, char *argv[])
{
  HANDLE hInput;
  HANDLE hThread;
  char *how;

  if (argc > 1) {
    how = argv[1];
  } else {
    how = "getpeername";
  }

  // Initialize Winsock
  int foo = 1;
  if (foo) {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
      fprintf(stderr, "WSAStartup failed\n");
      return 1;
    }
  }

  // Get a handle to the standard input stream
  hInput = GetStdHandle(STD_INPUT_HANDLE);
  if (hInput == INVALID_HANDLE_VALUE) {
    fprintf(stderr, "Failed to get the input handle. Error %lu\n", (unsigned long)GetLastError());
    return 1;
  }
  char *fileType = getFileType(hInput);
  fprintf(stdout, "Input is a %s\n", fileType);
  fflush(stdout);

  // Create a thread that reads from the input
  hThread = CreateThread(NULL, 0, readingThreadFunction, &hInput, 0, NULL);
  if (hThread == NULL) {
    fprintf(stderr, "Failed to create thread. Error %lu\n", (unsigned long)GetLastError());
    return 1;
  }

  // Wait for a while to ensure readingThreadFunction() starts blocking in ReadFile().
  Sleep(1000);
  // At this point, the readingThreadFunction() should be blocked in ReadFile

  if (strcmp(fileType, "FILE_TYPE_PIPE") == 0) {
    fprintf(stdout, "Using method %s to determine whether input is a socket\n", how);
    fflush(stdout);

    int isSocket;
    ULONGLONG startTick = GetTickCount64();

    if (strcmp("getpeername", how) == 0) {
      isSocket = isSocketGetpeername(hInput);
    } else if (strcmp("getnamedpipeinfo", how) == 0) {
      isSocket = isSocketGetNamedPipeInfo(hInput);
    } else if (strcmp("getnamedpipehandlestatea", how) == 0) {
      isSocket = isSocketGetNamedPipeHandleStateA(hInput);
    } else {
      fprintf(stderr, "Unknown variant %s\n", how);
      return 1;
    }

    ULONGLONG elapsedSeconds = (GetTickCount64() - startTick) / 1000;
    int did_block = (elapsedSeconds > 5); // plenty

    if (isSocket) {
      fprintf(stdout, "Input is a socket %s\n", (did_block ? "BLOCKED" : "(quickly determined)"));
    } else {
      fprintf(stdout, "Input is NOT a socket %s\n", (did_block ? "BLOCKED" : "(quickly determined)"));
    }
  } else {
    fprintf(stdout, "Input is neither a pipe nor a socket (%s)\n", fileType);
  }
  fflush(stdout);

  int sleep_before_exit = 1;
  if (sleep_before_exit) {
    fprintf(stdout, "Sleeping a while before exiting\n");
    fflush(stdout);
    Sleep(5000);
  }

  int do_proper_cleanup = 1;
  if (do_proper_cleanup) {
    fprintf(stdout, "Wait for ReadFile()-thread to exit\n");
    fflush(stdout);

    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    WSACleanup();
  } else {
    (void)CancelSynchronousIo(hThread);
  }

  fprintf(stdout, "Successful exit\n");
  fflush(stdout);

  return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.