html текст
All interests
  • All interests
  • Design
  • Food
  • Gadgets
  • Humor
  • News
  • Photo
  • Travel
  • Video
Click to see the next recommended page
Like it
Don't like
Add to Favorites

Отличия сетевых вызовов Windows и Linux из песочницы

image
Во многом совместимые на уровне исходных кодов модели сокетов от Berkeley и Microsoft, на практике оказываются не такими уж кросплатформенными.

Рассмотрим некоторые хитрые различия в их реализации, которые обнаружились при написании кросплатформенного RPC для перенаправления сетевых вызовов некоторого процесса в одной ОС на другую ОС.


Тип сокетов
  • BSD: int
  • Win: void * // макрос SOCKET

Пока разрядность процессора 32 бита, проблем при взаимоотображении не возникает. На 64 битах у Windows тип SOCKET оказывается в 2 раза большим по размеру.

Дескриптор сокета на BSD ничем не отличается от дескриптора файла, а значит, некоторые системные вызовы принимают одновременно описатели и сокетов и файлов (например, такие-вот «редко» используемые вызовы, как close(), fcntl() и ioctl()).
Еще есть побочный эффект, проявляющий себя в довольно подлых ситуациях, заключающийся в том, что в системах с поддержкой модели Berkeley числовое значение описателя сокета — обычно маленькое число (меньше 100), и подряд создающиеся дескрипторы различаются на 1. В модели Microsoft такой описатель сразу больше 200 (примерно), а подряд создающиеся описатели различаются на sizeof(SOCKET).


Обработка ошибок
  • BSD: Вызовы возвращают -1, устанавливается глобальная перменная errno.
  • Win: Вызовы возвращают -1 (макрос SOCKET_ERROR), статус получаем с помощью WSAGetLastError().

Константы errno и коды ошибок Windows имеют абсолютно разные значения.


Создание сокетов:

socket(int af, int type, int protocol);

Константы для первого аргумента имеют абсолютно разные значения на BSD и Windows. Для второго пока совпадают.


Настройка сокетов
  • BSD:

    getsockopt(int sockfd, int level, int option_name, void *option_value, socklen_t *option_len);
    setsockopt(int sockfd, int level, int option_name, void const *option_value, socklen_t option_len)

  • Win:

    getsockopt(SOCKET sock, int level, int option_name, void *option_value, socklen_t *option_len);
    setsockopt(SOCKET sock, int level, int option_name, void const *option_value, socklen_t option_len)

Константы флагов для второго и третьего аргументов имеют абсолютно разные значения на BSD и Windows.


Настройка сокетов 2
  • BSD: fcntl(int fd, int cmd, ...);
  • Win: ioctlsocket(SOCKET sock, long cmd, long unsigned *arg);

Единственное полностью корректное отображение: fcnlt(descriptor, F_SETFL, O_NONBLOCK) -> ioctlsocket(descriptor, FIONBIO, адрес переменной со значением O_NONBLOCK). Числовые значения флагов следует воспринимать относительно целевой системы (на BSD и Windows они разные).
При этом, на запрос типа fcnlt(descriptor, F_GETFL), можно возвращать 0 или O_RDWR.


Настройка сокетов 3
  • BSD: ioctl(int fd, int cmd, ...);
  • Win: ioctlsocket(SOCKET sock, long cmd, long unsigned *arg);

Случаев реального использования ioctl() с сокетом в качестве первого аргумента пока не выявлено.


Работа с DNS

getaddrinfo(char const *node, char const *service, struct addrinfo const *hints, struct addrinfo **res)
  • BSD:

    struct addrinfo
    {
    int ai_flags;
    int ai_family;
    int ai_socktype;
    int ai_protocol;
    socklen_t ai_addrlen;
    struct sockaddr *ai_addr;
    char *ai_canonname;
    struct addrinfo *ai_next;
    };

  • Win:

    typedef struct addrinfo
    {
    int ai_flags;
    int ai_family;
    int ai_socktype;
    int ai_protocol;
    size_t ai_addrlen;
    char *ai_canonname;
    struct sockaddr_ *ai_addr;
    struct addrinfo_ *ai_next;
    } ADDRINFOA, *PADDRINFOA;

Просмотрите внимательно инварианты этих структур. ai_addr и ai_canonname имеют разные смещения относительно начала структуры. Разработчики их просто поменяли местами (перепутали?).


Пересылка данных
  • BSD:

    recv(int sockfd, void *buffer, size_t length, int flags);
    recvfrom(int sockfd, void *buffer, size_t length, int flags, struct sockaddr *from, socklen_t *fromlen);
    send(int sockfd, void const *buffer, size_t length, int flags);
    sendto(int sockfd, void const *buffer, size_t length, int flags, struct sockaddr const *to, socklen_t tolen);

  • Win:

    recv(SOCKET sock, void *buffer, size_t length, int flags);
    recvfrom(SOCKET sock, void *buffer, size_t length, int flags, struct sockaddr *from, socklen_t *fromlen);
    send(SOCKET sock, void const *buffer, size_t length, int flags);
    sendto(SOCKET sock, void const *buffer, size_t length, int flags, struct sockaddr const *to, socklen_t tolen);

Флаги для четвертого аргумента имеют абсолютно разные значения на BSD и Windows.


Ожидание операций
  • BSD: poll(struct pollfd *fds, nfds_t nfds, int timeout);

    struct pollfd
    {
    int fd;
    short events;
    short revents;
    };

  • Win: WSAPoll(struct pollfd *fds, nfds_t nfds, int timeout);

    typedef struct pollfd
    {
    SOCKET sock;
    WORD events;
    WORD revents;
    } WSAPOLLFD, *PWSAPOLLFD;

Константы флагов для второго и третьего инвариантов структуры pollfd имеют абсолютно разные значения на BSD и Windows. WSAPoll() есть только в Windows 6й версии (Vista) и старше.


Ожидание операций 2
  • BSD: select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);

    typedef struct
    {
    long fds_bits[FD_SETSIZE / 8 * sizeof(long)];
    } fd_set;

  • Win: select(int nfds, FDSET *readfds, FDSET *writefds, FDSET *errorfds, struct timeval *timeout);

    typedef struct fd_set
    {
    unsigned fd_count;
    SOCKET fd_array[FD_SETSIZE];
    } FDSET, *PFDSET;

Проблема в select возникает при взаимоотображении структуры fd_set. Вспомним как работает select(). Данный вызов принимает три множества сокетов: для проверки на чтение, запись и ошибки на протяжении некоторого времени. Добавить свой сокет на проверку в одно из этих множеств можно макросом FD_SET(socket, set), проверить на установленость — FD_ISSET(socket, set), удалить один сокет из множества — FD_CLR(socket, set), удалить все — FD_ZERO(set). После вызова, select() оставляет в соответствующих множествах только те сокеты, которые, на протяжении указанного последним аргументом таймаута приобрели ожидаемое состояние.

Для BSD помещение некоторого сокета в некоторое множество заключается во взведении в последнем такого бита, порядковый номер которого численно равен дескриптору сокета. FD_SETSIZE обычно равно 1024. Первый аргумент select() представляет собой максимальное числовое значение дескриптора сокета, входящего в любое из трех множеств плюс один. Учитывая, что установка бита в массиве fds_bits производится исключительно без проверки диапазона, становится ясно, что при значении дескриптора сокета >= FD_SETSIZE поведение программы неопределено. Такая, несколько ненадежная реализация select — пережиток скупых на память компьютеров. Кстати, вот в каком случае важно непрямое преобразование int -> SOCKET и обратно.

Для Windows помещение некоторого сокета в некоторое множество заключается во вставке его в массив fd_array по индексу fd_count и дальнейшем увеличении последнего. FD_SETSIZE обычно равно 64. При этом, первый аргумент select() игнорируется вовсе.
Читать дальше
Twitter
Одноклассники
Мой Мир

материал с habrahabr.ru

5

      Add

      You can create thematic collections and keep, for instance, all recipes in one place so you will never lose them.

      No images found
      Previous Next 0 / 0
      500
      • Advertisement
      • Animals
      • Architecture
      • Art
      • Auto
      • Aviation
      • Books
      • Cartoons
      • Celebrities
      • Children
      • Culture
      • Design
      • Economics
      • Education
      • Entertainment
      • Fashion
      • Fitness
      • Food
      • Gadgets
      • Games
      • Health
      • History
      • Hobby
      • Humor
      • Interior
      • Moto
      • Movies
      • Music
      • Nature
      • News
      • Photo
      • Pictures
      • Politics
      • Psychology
      • Science
      • Society
      • Sport
      • Technology
      • Travel
      • Video
      • Weapons
      • Web
      • Work
        Submit
        Valid formats are JPG, PNG, GIF.
        Not more than 5 Мb, please.
        30
        surfingbird.ru/site/
        RSS format guidelines
        500
        • Advertisement
        • Animals
        • Architecture
        • Art
        • Auto
        • Aviation
        • Books
        • Cartoons
        • Celebrities
        • Children
        • Culture
        • Design
        • Economics
        • Education
        • Entertainment
        • Fashion
        • Fitness
        • Food
        • Gadgets
        • Games
        • Health
        • History
        • Hobby
        • Humor
        • Interior
        • Moto
        • Movies
        • Music
        • Nature
        • News
        • Photo
        • Pictures
        • Politics
        • Psychology
        • Science
        • Society
        • Sport
        • Technology
        • Travel
        • Video
        • Weapons
        • Web
        • Work

          Submit

          Thank you! Wait for moderation.

          Тебе это не нравится?

          You can block the domain, tag, user or channel, and we'll stop recommend it to you. You can always unblock them in your settings.

          • shoumikhin
          • домен habrahabr.ru

          Get a link

          Спасибо, твоя жалоба принята.

          Log on to Surfingbird

          Recover
          Sign up

          or

          Welcome to Surfingbird.com!

          You'll find thousands of interesting pages, photos, and videos inside.
          Join!

          • Personal
            recommendations

          • Stash
            interesting and useful stuff

          • Anywhere,
            anytime

          Do we already know you? Login or restore the password.

          Close

          Add to collection

             

            Facebook

            Ваш профиль на рассмотрении, обновите страницу через несколько секунд

            Facebook

            К сожалению, вы не попадаете под условия акции