Подтвердить что ты не робот

Как изменить сокет TCP, чтобы он не блокировался?

Как сделать сокет неблокирующим?

Я знаю функцию fcntl(), но я слышал, что она не всегда надежна.

4b9b3361

Ответ 1

Что вы подразумеваете под "не всегда надежным"? Если системе удастся установить ваш сокет без блокировки, он будет неблокировать. Операции сокета возвращают EWOULDBLOCK, если они блокируют необходимость блокировки (например, если выходной буфер заполнен и вы слишком часто вызываете send/write).

В этой теме форума есть несколько хороших моментов при работе с неблокирующими вызовами.

Ответ 2

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
}

Ответ 3

Вы неверно проинформированы о 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().

Ответ 4

fcntl() или ioctl() используются для установки свойств для файловых потоков. Когда вы используете эту функцию для блокировки сокета, такие функции, как accept(), recv() и т.д., Которые блокируют в природе, возвращают ошибку, а errno - EWOULDBLOCK. Вы можете опросить набор дескрипторов файлов для опроса в сокетах.

Ответ 5

Как правило, вы можете добиться такого же эффекта, используя обычные блокировки IO и мультиплексирования нескольких операций ввода-вывода с использованием select(2), poll(2) или некоторых других системных вызовов, доступных на вашем система.

См. Проблема C10K для сравнения подходов к масштабируемому мультиплексированию ввода-вывода.

Ответ 6

Лучший способ установки сокета как неблокирующего в 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");
}