Как сделать сокет неблокирующим?
Я знаю функцию fcntl()
, но я слышал, что она не всегда надежна.
Как сделать сокет неблокирующим?
Я знаю функцию fcntl()
, но я слышал, что она не всегда надежна.
Что вы подразумеваете под "не всегда надежным"? Если системе удастся установить ваш сокет без блокировки, он будет неблокировать. Операции сокета возвращают EWOULDBLOCK
, если они блокируют необходимость блокировки (например, если выходной буфер заполнен и вы слишком часто вызываете send/write).
В этой теме форума есть несколько хороших моментов при работе с неблокирующими вызовами.
fcntl()
всегда работал надежно для меня. В любом случае, вот функция, которую я использую для включения/отключения блокировки в сокете:
#include <fcntl.h>
/** Returns true on success, or false if there was an error */
bool SetSocketBlockingEnabled(int fd, bool blocking)
{
if (fd < 0) return false;
#ifdef _WIN32
unsigned long mode = blocking ? 0 : 1;
return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false;
#else
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) return false;
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
#endif
}
Вы неверно проинформированы о fcntl()
, не всегда надежном. Это неверно.
Пометить сокет как неблокирующий код так же просто, как:
// where socketfd is the socket you want to make non-blocking
int status = fcntl(socketfd, F_SETFL, fcntl(socketfd, F_GETFL, 0) | O_NONBLOCK);
if (status == -1){
perror("calling fcntl");
// handle the error. By the way, I've never seen fcntl fail in this way
}
В Linux, на ядрах > 2.6.27, вы также можете создавать неблокирующие сокеты с самого начала, используя socket()
и accept4()
.
например.
// client side
int socketfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
// server side - see man page for accept4 under linux
int socketfd = accept4( ... , SOCK_NONBLOCK);
Это экономит немного работы, но менее портативен, поэтому я стараюсь установить его с помощью fcntl()
.
fcntl()
или ioctl()
используются для установки свойств для файловых потоков. Когда вы используете эту функцию для блокировки сокета, такие функции, как accept()
, recv()
и т.д., Которые блокируют в природе, возвращают ошибку, а errno
- EWOULDBLOCK
. Вы можете опросить набор дескрипторов файлов для опроса в сокетах.
Как правило, вы можете добиться такого же эффекта, используя обычные блокировки IO и мультиплексирования нескольких операций ввода-вывода с использованием select(2)
, poll(2)
или некоторых других системных вызовов, доступных на вашем система.
См. Проблема C10K для сравнения подходов к масштабируемому мультиплексированию ввода-вывода.
Лучший способ установки сокета как неблокирующего в C - использовать ioctl. Пример, когда принятый сокет задан как неблокирующий, следующий:
long on = 1L;
unsigned int len;
struct sockaddr_storage remoteAddress;
len = sizeof(remoteAddress);
int socket = accept(listenSocket, (struct sockaddr *)&remoteAddress, &len)
if (ioctl(socket, (int)FIONBIO, (char *)&on))
{
printf("ioctl FIONBIO call failed\n");
}