Как сделать сокет неблокирующим?
Я знаю функцию 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");
}