Ryc*_*yck 23 c++ sockets winapi
我正在开发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
Run Code Online (Sandbox Code Playgroud)
并在适当的位置用'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.
*/
Run Code Online (Sandbox Code Playgroud)
所以我的问题是:
将SOCKET转换为int是否真的安全?
我想看一些证明SOCKET值不能大于2 ^ 32的文档.
提前致谢!
Ryck
内核对象句柄是特定于进程的.也就是说,进程必须创建对象或打开现有对象以获取内核对象句柄.内核句柄的每进程限制为2 ^ 24.
该线程继续引用Russinovich和Solomon的Windows Internals作为高位为零的来源.
这个问题的简单答案是否定的。看看MSDN[1]上对SOCKET值的描述:
除了值 INVALID_SOCKET 不是有效的套接字之外,Windows 套接字句柄没有任何限制。套接字句柄可以采用 0 到 INVALID_SOCKET–1 范围内的任何值。
很明显,在 64 位 Windows 上,API 允许范围 [0, 2^64 - 1) 内的所有值。如果 API 曾经返回过大于 2^32 - 1 的值,则将其分配给 int 会导致句柄截断。还可以看看 socket() 函数 [2] 的返回值的描述:
如果没有发生错误,套接字返回一个引用新套接字的描述符。
请注意,它最强调的是不承诺返回内核句柄。这使得关于内核句柄的可能值的任何讨论都没有实际意义。
尽管如此,在撰写本文时,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);
Run Code Online (Sandbox Code Playgroud)