Linux, сокеты, неблокирующее соединение

Я хочу создать неблокирующее соединение. Вот так:

socket.connect(); // returns immediately

Для этого я использую другой поток, бесконечный цикл и Linux epoll. Подобно этому (псевдокод):

// in another thread
{
  create_non_block_socket();
  connect();

  epoll_create();
  epoll_ctl(); // subscribe socket to all events
  while (true)
  {
    epoll_wait(); // wait a small time(~100 ms)
    check_socket(); // check on EPOLLOUT event
  }
}

Если я запускаю сервер, а затем клиент, все это работает. Если я сначала запускаю клиент, подождите некоторое небольшое время, запустите сервер, а затем клиент не подключится.

Что я делаю неправильно? Может быть, это можно сделать по-другому?

Ответ 1

Для асинхронного подключения вы должны использовать следующие шаги:

  • создать сокет с socket(..., SOCK_NONBLOCK, ...)
  • начать соединение с connect(fd, ...)
  • если возвращаемое значение не равно 0 и EINPROGRESS, а затем прервать с ошибкой
  • ждать, пока fd не будет сигнализирован как готовый к выводу
  • проверить состояние сокета с помощью getsockopt(fd, SOL_SOCKET, SO_ERROR, ...)
  • сделано

Нет циклов - если вы не хотите обрабатывать EINTR.

Если клиент запускается первым, вы должны увидеть ошибку ECONNREFUSED на последнем шаге. Если это произойдет, закройте сокет и начните с начала.

Трудно сказать, что не так с вашим кодом, не видя больше деталей. Я полагаю, что вы не прерываете ошибки в вашей операции check_socket.

Ответ 2

Есть несколько способов проверить, успешно ли неблокирующее соединение.

  1. Сначала вызовите getpeername(), если произошел сбой с ошибкой ENOTCONN, соединение не удалось. затем вызовите getsockopt с SO_ERROR, чтобы получить ожидающую ошибку на сокете
  2. вызовите read длиной 0. Если чтение не удалось, соединение не удалось, и ошибка чтения указывает, почему не удалось установить соединение; чтение возвращает 0, если соединение успешно
  3. вызовите соединение снова; если errno EISCONN, соединение уже подключено, и первое соединение успешно.