确定套接字和文件描述符之间的区别。

15

在Unix中,一切皆是文件的理念支持read()write()close()函数,但在Win32上不支持。

我想模仿这种方式,但不知道如何区分WinSocks2中的socksocket还是fd

//returns 1 if `sock` is network socket, 
//        0 if `sock` is file desriptor (including stdio, stderr, stdout), ...
//       -1 in none of above
int is_net_socket(int sock)
{
    // ...?
}

这应该按照以下方式工作:

int mysock  = socket(PF_INET, SOCK_STREAM, 0);
int myfd    = _open("my_file.txt", _O_RDONLY);

printf("1: %d    2: %d    3: %d    4:%d\n",
       is_net_socket(mysock),   //1
       is_net_socket(myfd),     //0
       is_net_socket(stdin),    //0
       is_net_socket(stderr));  //0

// should print "1: 1    2: 0    3: 0    4:0"
如何实现 is_net_socket 并将其用于以下目的:
int my_close(int sock)
{
#if ON_WINDOWS
    switch( is_net_socket(sock) ) {
        case 1: return closesocket(sock);
        case 0: return _close(sock);
        default: //handle error...
    }
#else
    return close(sock);
#endif
}
4个回答

10

不确定您从何处得到Windows不允许您将SOCKET句柄用作文件的想法-正如Socket Handles页面上明确说明的那样:

Windows Sockets 2中,套接字句柄可以选择是文件句柄。 Winsock提供程序的套接字句柄可以与其他非Winsock函数(例如ReadFile、WriteFile、ReadFileEx和WriteFileEx)一起使用。

无论如何,关于在Windows上如何区分它们,可以查看函数NtQueryObject,如果传递给它的句柄是已打开的SOCKET,则它将返回一个句柄名称为\Device\Tcp。请阅读此调用返回的结构的“备注”部分。

请注意,此方法仅适用于XP及更高版本,并且在Windows 2000上会失败(我假设这已经过时,不会影响您)。


5

我想你可以使用select查询套接字的状态。

http://msdn.microsoft.com/en-us/library/ms740141%28VS.85%29.aspx

我建议将文件描述符和套接字放在一个单一的结构体中进行分组。你可以声明一个枚举来指示描述符是文件还是套接字。我知道这可能不够动态,但通常在创建可移植应用程序时,最好将这些细节抽象化处理。
例如:
enum type { SOCKET, FILE };

typedef struct
{
    unsigned int id;
    type dataType;
} descriptor_t;

int close(descriptor_t sock)
{
#if WIN32
    if (sock.dataType == SOCKET)
        return closesocket(sock.id);
    else
        return _close(sock.id);
#else
    return close(sock.id);
#endif
}

是的,这样就不用考虑操作系统对文件的表示方式了;你自己来跟踪它。 - Chris Smith

3
如果Windows的'C'库有dup()函数,您可以尝试对其进行复制,这应该对套接字失败,但对文件fd成功。因此,代码如下所示:
int is_net_socket(fd)
{
  return close(dup(fd)) != 0;
}

警告:未经测试的理论和未经测试的依赖关系;-)请注意,如果fd用尽,则此操作将返回误导性结果。另一个副作用是,如果它是文件,则会被刷新并更新其目录条目。总而言之,它可能真的很糟糕。我甚至可能会自己投反对票。


很好,但我真的希望它没有副作用。没有其他的想法吗?这是个不错的开始 :) - DinGODzilla
这个操作不应该有副作用。只有关闭打开文件的最后一个打开文件描述符,而不是任何与之相关的文件描述符,才会产生副作用。实际上,我认为这个解决方案相当不错。 - R.. GitHub STOP HELPING ICE
@R.. 它确实有副作用。我提到的那些在Windows上发生。 - user207421
哇,那真的是炸了...顺便说一下,我认为你不能对自己的答案进行踩票。 - R.. GitHub STOP HELPING ICE

3
我猜测...但不确定,在Windows上,fds和sockets使用不同的命名空间。 因此,套接字和文件的编号可能相同,当您调用is_net_socket时,就无法知道您正在谈论哪一个。
尝试打印套接字和fd编号,看它们是否在同一时间相同。

在Windows上,WinSock2(自NT4以来的标准)套接字是内核对象句柄(文件对象,可以是AFD或Tcp句柄),这就是为什么您可以在其上调用标准ReadFile和WriteFile函数的原因。所有内核对象的句柄都在同一表格中(每个进程),并且不重叠。即使它们确实具有不同的实现和不同的值,在下一个版本中也总是可以更改的。处理句柄值应视为不透明。 - Chris Smith
@ChrisSmith:我们在谈论的是SOCKET,据我所知它不是一个HANDLE - Dietrich Epp
如果你查看winsock2.h文件,你会发现这一行:typedef UINT_PTR SOCKET;。其中的UINT_PTR包含了一个实际的内核句柄。这就是为什么你可以使用它与常规的文件IO API一起使用:否则它是无法工作的。因此,在文档中有时将SOCKET称为“套接字句柄”。 - Chris Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接