我正在开发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
答案 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。请记住,这是一种本质上危险的,不好的做法,并且以权宜之计的名义永远不合理。
修改(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位,所以不必担心。