Как изменить сокет TCP, чтобы он был неблокирующим? - PullRequest
33 голосов
/ 09 октября 2009

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

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

Ответы [ 7 ]

80 голосов
/ 11 октября 2009

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 == -1) return false;
   flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
   return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
#endif
}
45 голосов
/ 12 марта 2014

Вы дезинформированы о том, что 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().

20 голосов
/ 09 октября 2009

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

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

16 голосов
/ 09 октября 2009

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

2 голосов
/ 08 июля 2018

Я знаю, что это старый вопрос, но для всех в Google, которые в конечном итоге ищут здесь информацию о том, что делать с блокирующими и неблокирующими сокетами, здесь дано подробное объяснение различных способов работы с вводом / выводом. режимы розеток - http://dwise1.net/pgm/sockets/blocking.html.

Краткое резюме:

  • Так почему блоки розеток?

  • Каковы основные методы программирования для работы с блокирующими сокетами?

    • Имейте дизайн, который не заботится о блокировке
    • Использование выбора
    • Использование неблокирующих розеток.
    • Использование многопоточности или многозадачности
2 голосов
/ 09 октября 2009

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

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

1 голос
/ 09 октября 2009

Лучший способ установить сокет как неблокирующий в 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");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...