在Win64下将SOCKET转换为int是否安全?

时间:2009-12-23 16:02:11

标签: c++ winapi sockets

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

问题是标准POSIX函数如accept()或bind()期望'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的部分代码之前,这很好 事实证明,OpenSSL甚至在Win64上也使用了套接字。这看起来很奇怪,所以我开始寻找答案,但我发现的唯一一件事就是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的文件。

提前谢谢!
Ryck

3 个答案:

答案 0 :(得分:9)

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

  

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

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

答案 1 :(得分:4)

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

  

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

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

  

如果没有错误发生,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
  5. 修改(2018-01-29)

    由于这个主题似乎仍有一些兴趣,因此值得指出的是,在C ++ 11中编写可移植套接字代码非常容易,而不需要使用可疑的类型转换:

    using socket_t = decltype(socket(0, 0, 0));
    
    socket_t s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    

答案 2 :(得分:0)

/*
 * 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位,所以不必担心。