Windows non block socket

socket_set_nonblock

(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8)

socket_set_nonblock — Устанавливает неблокирующий режим для файлового дескриптора fd

Описание

Функция socket_set_nonblock() устанавливает флаг O_NONBLOCK на сокете, указанном в параметре socket .

Когда операция (например, получение, отправка, соединение, принятие соединения, . ) выполняется на неблокирующем сокете, скрипт не будет приостанавливать своё исполнение до получения сигнала или возможности выполнить операцию. Если выполняемая операция должна привести к блокированию выполнения скрипта, то вместо этого вызываемая функция возвратит ошибку.

Список параметров

Экземпляр Socket, созданный при помощи функции socket_create() или socket_accept() .

Возвращаемые значения

Возвращает true в случае успешного завершения или false в случае возникновения ошибки.

Список изменений

Версия Описание
8.0.0 socket теперь экземпляр класса Socket; ранее был ресурсом ( resource ).

Примеры

Пример #1 Пример использования socket_set_nonblock()

= socket_create_listen ( 1223 );
socket_set_nonblock ( $socket );

Этот пример создаёт слушающий сокет на всех интерфейсах на порту 1223 и устанавливает сокет в режим O_NONBLOCK . socket_accept() будет немедленно возвращать ошибку, если только именно в этот момент нету ожидающего соединения.

Смотрите также

  • socket_set_block() — Устанавливает блокирующий режим на сокете
  • socket_set_option() — Устанавливает опции для сокета
  • stream_set_blocking() — Установить блокирующий/неблокирующий режим в потоке

Способ 3 — Использование неблокирующих сокетов

Третий способ основан на использовании неблокирующих сокетов (nonblocking sockets) и функции select. Сначала разберёмся, что такое неблокирующие сокеты. Сокеты, которые мы до сих пор использовали, являлись блокирующими (blocking). Это название означает, что на время выполнения операции с таким сокетом ваша программа блокируется. Например, если вы вызвали recv, а данных на вашем конце соединения нет, то в ожидании их прихода ваша программа «засыпает». Аналогичная ситуация наблюдается, когда вы вызываете accept, а очередь запросов на соединение пуста. Это поведение можно изменить, используя функцию fcntl.

sockfd = socket(AF_INET, SOCK_STREAM, 0);

fcntl(sockfd, F_SETFL, O_NONBLOCK);

Эта несложная операция превращает сокет в неблокирующий. Вызов любой функции с таким сокетом будет возвращать управление немедленно. Причём если затребованная операция не была выполнена до конца, функция вернёт -1 и запишет в errno значение EWOULDBLOCK. Чтобы дождаться завершения операции, мы можем опрашивать все наши сокеты в цикле, пока какая-то функция не вернёт значение, отличное от EWOULDBLOCK. Как только это произойдёт, мы можем запустить на выполнение следующую операцию с этим сокетом и вернуться к нашему опрашивающему циклу. Такая тактика (называемая в англоязычной литературе polling) работоспособна, но очень неэффективна, поскольку процессорное время тратится впустую на многократные (и безрезультатные) опросы.

Чтобы исправить ситуацию, используют функцию select. Эта функция позволяет отслеживать состояние нескольких файловых дескрипторов (а в Unix к ним относятся и сокеты) одновременно.

int select(int n, fd_set *readfds, fd_set *writefds,

fd_set *exceptfds, struct timeval *timeout);

FD_CLR(int fd, fd_set *set);

FD_ISSET(int fd, fd_set *set);

FD_SET(int fd, fd_set *set);

Функция select работает с тремя множествами дескрипторов, каждое из которых имеет тип fd_set. В множество readfds записываются дескрипторы сокетов, из которых нам требуется читать данные (слушающие сокеты добавляются в это же множество). Множество writefds должно содержать дескрипторы сокетов, в которые мы собираемся писать, а exceptfds — дескрипторы сокетов, которые нужно контролировать на возникновение ошибки. Если какое-то множество вас не интересуют, вы можете передать вместо указателя на него NULL. Что касается других параметров, в n нужно записать максимальное значение дескриптора по всем множествам плюс единица, а в timeout — величину таймаута. Структура timeval имеет следующий формат.

int tv_sec; // секунды

int tv_usec; // микросекунды

Поле «микросекунды» смотрится впечатляюще. Но на практике вам не добиться такой точности измерения времени при использовании select. Реальная точность окажется в районе 100 миллисекунд.

Теперь займёмся множествами дескрипторов. Для работы с ними предусмотрены функции FD_XXX, показанные выше; их использование полностью скрывает от нас детали внутреннего устройства fd_set. Рассмотрим их назначение.

FD_ZERO(fd_set *set) — очищает множество set

FD_SET(int fd, fd_set *set) — добавляет дескриптор fd в множество set

FD_CLR(int fd, fd_set *set) — удаляет дескриптор fd из множества set

FD_ISSET(int fd, fd_set *set) — проверяет, содержится ли дескриптор fd в множестве set

Если хотя бы один сокет готов к выполнению заданной операции, select возвращает ненулевое значение, а все дескрипторы, которые привели к «срабатыванию» функции, записываются в соответствующие множества. Это позволяет нам проанализировать содержащиеся в множествах дескрипторы и выполнить над ними необходимые действия. Если сработал таймаут, select возвращает ноль, а в случае ошибки -1. Расширенный код записывается в errno.

Программы, использующие неблокирующие сокеты вместе с select, получаются весьма запутанными. Если в случае с fork мы строим логику программы, как будто клиент всего один, здесь программа вынуждена отслеживать дескрипторы всех клиентов и работать с ними параллельно. Чтобы проиллюстрировать эту методику, я в очередной раз переписал код сервера с использованием select. Новая версия приведена в листинге 3. Эта программа, также написана на C++ (а не на C). В программе использовался класс set из библиотеки STL языка C++, чтобы облегчить работу с набором дескрипторов и сделать её более понятной.

Листинг 3. Код сервера (неблокирующие сокеты и select).

How do I set a socket to be non-blocking?

The traditional UNIX system calls are blocking. For example:

accept() blocks the caller until a connection is present.

If no messages space is available at the socket to hold the message to be transmitted, then send() normally blocks.

If no messages are available at the socket, the recv call waits for a message to arrive.

When writing a server, we need to be ready to react to many kinds of event which could happen next: a new connection is made, or a client sends us a request, or a client drops its connection. If we make a call to, say, accept , and the call blocks, then we lose our ability to respond to other events.

The traditional answer to this problem is the select system call. We call select indicating various blocking calls we’re interested in. select then blocks until one or more of those blocking calls is ready, meaning that calling it will not block.

If our server only makes calls which select has indicated will not block, will everything be OK? No! These two operations — select followed by the hopefully non-blocking call — are non-atomic. By the time the server makes the call, the situation may have changed! A pending connection may disappear before we try to accept it. A client attempting to send data may disappear before we try to read its data. Data may be read from a socket by a different process before we get to it.

The answer to this is non-blocking I/O. We set a flag on a socket which marks that socket as non-blocking. This means that, when performing calls on that socket (such as read and write ), if the call cannot complete, then instead it will fail with an error like EWOULDBLOCK or EAGAIN .

To mark a socket as non-blocking, we use the fcntl system call. Here’s an example:

Here’s a complete example. This server opens TCP port 8080, and marks the listening socket as non-blocking. The server then loops, repeatedly asking for a new connection. If the server gets a connection, the server writes something to the connection then closes it. If the server does not get a connection (because no connections were made to the server, and the socket was marked non-blocking), then the server sleeps for a second before trying again.

Notice the calls to fcntl and the check for EWOULDBLOCK when calling accept . When running this and making connections, we see:

Определение blocking-режима TCP-сокета под Windows

Те, кто работает с TCP-сокетами, знают что сокет может работать в блокирующем или неблокирующем (nonblocking) режиме. Windows-сокеты, после создания, находятся в блокирующем режиме, но их можно перевести в неблокирующий функцией ioctlsocket().

При работе над одним проектом у меня появилась задача определить, в каком режиме работает сокет, предоставленный мне DLL-функцией. Т.е. кроме самого сокета, у меня никакой информации не было и приходили они в непредсказуемом режиме.

Под *nix blocking-режим без проблем определяется вызовом функции fcntl(), но под WinSock2 ничего подобного не обнаружилось, и на форумах ничего кроме «Windows does not offer any way to query whether a socket is currently set to blocking or non-blocking» никто не ответил.

Но способ определения все-таки существует:

Функция принимает номер сокета и возвращает 1 если сокет в nonblocking mode, 0 — blocking, -1 если произошла ошибка определения и -2 если сокет после определения режима остался с маленьким таймаутом.

Вкратце последовательность действий такова:

1. Сохраняем значение таймаута сокета (по умолчанию там 0 — «ждать вечно»).
2. Устанавливаем таймаут в 1 милисекунду.
3. Читаем из сокета 0 (ноль) байт Out of Band данных. Тут нужно пояснение: Если передаются OOB-данные, то функция может врать, но я никогда не сталкивался с OOB с тех пор, как Windows NT4 валилась в синий экран при попытке принять такое (WinNuke).
4. Получаем ошибку, которая произошла в результате чтения.
5. Восстанавливаем старое значение таймаута сокета.
6. Смотрим что за ошибка чтения у нам была: если WSAEWOULDBLOCK — то сокет находится в nonblocking mode, что и требовалось определить.

К недостаткам данного метода, кроме уже упомянутого MSG_OOB, можно отнести задержку определения в 1 миллисекунду и ненулевую вероятность испортить таймаут чтения сокета (хотя нагрузочный тест ни разу такого поведения не выявил).

How to make ‘send’ non-blocking in winsock

I am making a program which sends UDP packets to a server at a fixed interval, something like this:

However the periodicity cannot be guaranteed because send is a blocking call (e.g., when fixedInterval is 20ms, and a call to send is > 20ms ). Do you know how I can turn the send into a non-blocking operation?

4 Answers 4

The API ioctlsocket can do it.You can use it as below.But why don’t you use I/O models in winsock?

You need to use a non-blocking socket. The send/receive functions are the same functions for blocking or non-blocking operations, but you must set the socket itself to non-blocking.

Also, be aware that working with non-blocking sockets is quite different. You’ll need to make sure you handle WSAEWOULDBLOCK errors as success! 🙂

So, using non-blocking sockets may help, but still will not guarantee an exact period. You would be better to drive this from a timer, rather than this simple loop, so that any latency from calling send, even in non-blocking mode, will not affect the timing.

My memory is fuzzy here since it’s probably been 15 years since I’ve used UDP non-blocking.

However, there are some things of which you should be aware.

Send only smallish packets if you’re going over a public network. The PATH MTU can trip you up if either the client or the server is not written to take care of incomplete packets.

Make sure you check that you have sent the number of bytes you think you have to send. It can get weird when you’re expecting to see 300 bytes sent and the receiving end only gets 248. Both client side and server side have to be aware of this issue.

See here for some good advice from the Linux folks.

See here for the Unix Socket FAQ for UDP

This is a good, general network programming FAQ and example page.

Читайте также:  Mac os amd ryzen 2200u
Оцените статью