Определить между сокетом и fd

В unix все в файле подход к функции read(), write(), close() не поддерживается в Win32.

Я хочу эмулировать его, но понятия не имею, как отличить, когда sock является сокет или fd на WinSocks2.

//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
}

Ответы

Ответ 1

Не уверен, когда вы получаете идею о том, что Windows не позволит вам использовать дескрипторы SOCKET в качестве файлов - как четко указано в Socket Handles страница:

Ручка сокета может быть произвольным файловым дескриптором в Windows Sockets 2. Ручка сокета от поставщика Winsock может использоваться с другими функциями, отличными от Winsock, такими как ReadFile, WriteFile, ReadFileEx и WriteFileEx.

В любом случае, как различать их в Windows, см. функцию NtQueryObject, которая вернет имя дескриптора \Device\Tcp if дескриптор, переданный ему, является открытым SOCKET. Прочтите раздел "Примечания" для структуры, возвращаемой этим вызовом.

Обратите внимание, что этот подход работает только с XP и выше, и не будет работать в Windows 2000 (который, как я предполагаю, достаточно велик, что он не влияет на вас.)

Ответ 2

Я полагаю, вы можете использовать select для запроса статуса сокета.

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

Я бы рекомендовал группировать файлы desc и сокеты в одной структуре. Вы можете объявить перечисление, чтобы указать, является ли дескриптор файлом или сокетом. Я знаю, что это может быть не так динамично, как вы хотите, но обычно при создании переносных приложений лучше всего отвлечь эти детали.

Пример:

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
}

Ответ 3

Я подозреваю... но я не уверен, что fds и сокеты в Windows используют отдельные пространства имен. Поэтому число для сокета и файла может быть одинаковым, и невозможно узнать, о котором вы говорите, когда вы вызываете is_net_socket.

Попробуйте распечатать сокеты и номера fd, чтобы увидеть, совпадают ли они друг с другом одновременно.

Ответ 4

Если в библиотеке Windows 'C' есть dup(), вы можете попытаться свернуть ее, что должно завершиться неудачно для сокета, но преуспеть для файла fd. Итак:

int is_net_socket(fd)
{
  return close(dup(fd)) != 0;
}

Предупреждение: непроверенная теория с непроверенной зависимостью;-) Обратите внимание, что это приведет к ошибочным результатам, если у вас закончится fd. Другим побочным эффектом является то, что если это файл, он будет очищен и обновлен его каталог. В общем, это, наверное, отстойно. Я мог бы даже уменьшить его сам.