【问题标题】:Is it safe to cast SOCKET to int under Win64?在 Win64 下将 SOCKET 转换为 int 是否安全?
【发布时间】:2010-12-29 13:06:07
【问题描述】:

我正在开发 POSIX C++ 程序的 Windows 端口。

问题在于,像 accept() 或 bind() 这样的标准 POSIX 函数需要一个“int”作为第一个参数,而它的 WinSock 对应函数使用“SOCKET”。
当编译为 32 位时,一切都很好,因为两者都是 32 位,但在 Win64 下 SOCKET 是 64 位,而 int 仍然是 32 位,它会产生很多编译器警告,如下所示:

warning C4244: '=' : conversion from 'SOCKET' to 'int', possible loss of data

我尝试使用 typedef 来解决这个问题:


#ifdef _WIN32
 typedef SOCKET sock_t;
#else
 typedef int sock_t;
#endif

并在适当的地方用 sock_t 替换“int”。

这很好,直到我到达调用 OpenSSL API 的部分代码。
事实证明,即使在 Win64 上,OpenSSL 也使用整数作为套接字。这看起来很奇怪,所以我开始寻找答案,但我发现的唯一内容是 openssl-dev 邮件列表上的一篇旧帖子,其中提到了评论 e_os.h:


/*
 * Even though sizeof(SOCKET) is 8, it's safe to cast it to int, because
 * the value constitutes an index in per-process table of limited size
 * and not a real pointer.
 */

所以我的问题是:
将 SOCKET 转换为 int 真的安全吗?

我希望看到某种文档证明 SOCKET 的值不能大于 2^32。

提前致谢!
瑞克

【问题讨论】:

  • JPeterMugaas 于 2018 年 9 月创建的 OpenSSL 错误“MINGW-W64 和 SOCKET 类型(不应为 int)#7282”似乎是同一个问题:github.com/openssl/openssl/issues/7282。截至 2021 年 1 月,它目前被忽略。
  • 还有这个 2019 版本的相同错误“windows x64 使用 SOCKET(64 位)而不是 int(32 位)作为描述符。BIO_new_socket 采用 int #8169”github.com/openssl/openssl/issues/8169。问题报告者 leleftheriades 决定几乎立即关闭该问题,因为他决定 OpenSSL 维护人员在它真正破坏某些东西之前不会修复该问题。伤心。

标签: c++ winapi sockets


【解决方案1】:

这个问题的简单答案是否定的。看看 MSDN [1] 上对 SOCKET 值的描述:

Windows 套接字句柄没有任何限制,除了值 INVALID_SOCKET 不是有效的套接字。套接字句柄可以取 0 到 INVALID_SOCKET–1 范围内的任何值。

很明显,在 64 位 Windows 上,API 允许 [0, 2^64 - 1) 范围内的所有值。如果 API 曾经返回大于 2^32 - 1 的值,将其分配给 int 将导致句柄截断。也可以看看 socket() 函数的返回值的描述[2]:

如果没有发生错误,socket返回一个引用新socket的描述符。

请注意,它最强调不承诺返回内核句柄。这使得任何关于内核句柄的可能值的讨论都没有实际意义。

话虽如此,在撰写本文时,socket() 函数确实返回了一个内核句柄(或与内核句柄无法区分的东西)[3],并且内核句柄确实被限制为 32 位 [4]。但请记住,微软明天可以在不违反接口合同的情况下更改任何这些内容。

但是,由于无疑有大量应用程序依赖于这些特定的实现细节(更重要的是,OpenSSL 也是如此),微软可能会三思而后行做出任何重大更改。因此,继续将 SOCKET 转换为 int。请记住,这是一种固有的危险、不好的做法,并且永远不能以权宜之计为名。

  1. http://msdn.microsoft.com/en-us/library/windows/desktop/ms740516(v=vs.85).aspx
  2. http://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx
  3. http://msdn.microsoft.com/en-us/library/windows/desktop/ms742295(v=vs.85).aspx
  4. http://msdn.microsoft.com/en-us/library/windows/desktop/aa384267(v=vs.85).aspx

编辑 (2018-01-29)

由于这个话题似乎仍然有些有趣,值得指出的是,在 C++11 中编写可移植套接字代码非常容易,而无需求助于有问题的类型转换:

using socket_t = decltype(socket(0, 0, 0));

socket_t s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

【讨论】:

  • OP 试图调用 OpenSSL 函数,例如 SSL_set_fd(SSL *ssl, int fd);,其中 fd 是您要用于 SSL 连接的套接字的文件描述符。我不知道没有任何方法可以在没有铸造的情况下处理这个问题。是的,如果 OpenSSL 在这里引入一个可以有条件的泛型类型,而不是强制int,那就太好了,但是......有“方便”,并且有“让它工作的唯一方法”:)。
【解决方案2】:
/*
 * Even though sizeof(SOCKET) is 8, it's safe to cast it to int, because
 * the value constitutes an index in per-process table of limited size
 * and not a real pointer.
 */

这个评论是正确的。 SOCKET = Windows NT 系列上的文件句柄。我从未见过 9x 系列 64 位,所以没什么好担心的。

【讨论】:

    【解决方案3】:

    这个post 似乎是在重复msdn 上kernel objects 上的信息:

    内核对象句柄是特定于进程的。也就是说,进程必须要么创建对象,要么打开现有对象以获取内核对象句柄。内核句柄的每个进程限制为 2^24。

    线程继续引用Windows Internals by Russinovich and Solomon 作为高位为零的来源。

    【讨论】:

    • 谢谢皮特!这正是我想要的。这是 Mark Russinovich 博客上的相关帖子:blogs.technet.com/markrussinovich/archive/2009/09/29/…
    • 这是各种错误。首先,2^24 个内核句柄的限制意味着句柄将具有 [0, 2^24-1] 范围内的值。其次,内核句柄的数量是一个内部实现细节,随时可能发生变化(因此标题为“Windows Internals”)。
    • 最后,Winsock 文档非常清楚,允许大于 2^32 的值:“Windows 套接字句柄没有任何限制,除了值 INVALID_SOCKET 不是有效的套接字。套接字句柄可以采用任何值在 0 到 INVALID_SOCKET–1" (msdn.microsoft.com/en-us/library/windows/desktop/…) 范围内。唯一的可取之处是,由于许多糟糕的程序员都假定 SOCKET == int,Microsoft 不太可能更改他们的实现,因为它会破坏所有这些程序。
    • 因为高位为零(今天),这是一个实现细节。这不是合同规定的。微软明天可能会改变这一点。另外请记住,socket() 函数确实承诺返回内核句柄,因此即使讨论内核句柄的属性也是无关紧要的。
    • @PeterRuderman 这个软件并不“坏”,只是因为它不希望破坏与所有其他操作系统的兼容性以应对理论上的 Windows 边缘情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-10
    • 2011-04-03
    • 2011-04-03
    • 2010-10-20
    相关资源
    最近更新 更多