Правильная запись в неблокирующий сокет в C ++ - PullRequest
2 голосов
/ 01 июля 2010

У меня странная проблема при попытке преобразовать блокирующий сервер сокетов в неблокирующий. Хотя сообщение было получено только один раз при отправке с блокирующими сокетами, при использовании неблокирующих сокетов сообщение кажется полученным бесконечное число раз. Вот код, который был изменен:

return ::write(client, message, size);

до

// Nonblocking socket code
int total_sent = 0, result = -1;
while( total_sent < size ) {
   // Create a temporary set of flags for use with the select function
   fd_set working_set;
   memcpy(&working_set, &master_set, sizeof(master_set));

   // Check if data is available for the socket - wait 1 second for timeout
   timeout.tv_sec = 1;
   timeout.tv_usec = 0;
   result = select(client + 1, NULL, &working_set, NULL, &timeout);

    // We are able to write - do so
   result = ::write(client, &message[total_sent], (size - total_sent));
   if (result == -1) {
      std::cerr << "An error has occured while writing to the server."
              << std::endl;
      return result;
   }
   total_sent += result;
}

return 0;

РЕДАКТИРОВАТЬ: инициализация основного набора выглядит следующим образом:

// Private member variables in header file
fd_set master_set;
int sock;

...

// Creation of socket in class constructor
sock = ::socket(PF_INET, socket_type, 0);

// Makes the socket nonblocking
fcntl(sock,F_GETFL,0);

FD_ZERO(&master_set);
FD_SET(sock, &master_set);

...

// And then when accept is called on the socket
result = ::accept(sock, NULL, NULL);
if (result > 0) {
   // A connection was made with a client - change the master file
   // descriptor to note that
   FD_SET(result, &master_set);
}

Я подтвердил, что в обоих случаях код вызывается только один раз для сообщения об ошибке. Кроме того, код на стороне клиента не изменился вообще - у кого-нибудь есть какие-либо рекомендации?

Ответы [ 2 ]

4 голосов
/ 01 июля 2010
fcntl(sock,F_GETFL,0);

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

fcntl(sock, F_SETFL, O_NONBLOCK);

Кроме того, вы не проверяете, действительно ли вы можете писать в стиль неблокирования сокета с помощью

FD_ISSET(client, &working_set);
2 голосов
/ 01 июля 2010

Я не верю, что этот код действительно вызывается только один раз в «неблокирующей» версии (кавычки, потому что он еще не является неблокирующим, как указал Мистер, посмотрите здесь ), проверьте еще раз , Если блокирующая и неблокирующая версии согласованы, неблокирующая версия должна вернуть total_sent (или размер). С return 0 вместо звонящего, скорее всего, поверит, что ничего не было отправлено. Что вызвало бы бесконечную отправку ... разве это не то, что происходит?

Также ваш «неблокирующий» код довольно странный. Кажется, вы используете select, чтобы блокировать его в любом случае ... Хорошо, с таймаутом в 1 с, но почему бы вам не сделать его действительно не блокирующим? то есть: удалите все данные select и проверьте на наличие ошибки в write() с ошибкой EWOULDBLOCK. select или poll для мультиплексирования.

Также вы должны проверить ошибки для select и использовать FD_ISSET, чтобы проверить, действительно ли сокет готов. Что если тайм-аут на 1 с действительно произойдет? Или если выбор остановлен каким-то прерыванием? И если при записи происходит ошибка, вы должны также написать, какая ошибка, это гораздо более полезно, чем ваше общее сообщение. Но я думаю, что эта часть кода еще далека от завершения.

Насколько я понимаю, ваш код, вероятно, должен выглядеть примерно так (если код выполняется в уникальном потоке или в потоке, или разветвление при принятии соединения изменит детали):

// Creation of socket in class constructor
sock = ::socket(PF_INET, socket_type, 0);
fcntl(sock, F_SETFL, O_NONBLOCK);

// And then when accept is called on the socket
result = ::accept(sock, NULL, NULL);
if (result > 0) {
   // A connection was made with a client
   client = result;
   fcntl(client, F_SETFL, O_NONBLOCK);
}

// Nonblocking socket code
result = ::write(client, &message[total_sent], (size - total_sent));
if (result == -1) {
  if (errno == EWOULDBLOCK){
      return 0;
  }
  std::cerr << "An error has occured while writing to the server."
          << std::endl;
  return result;
}
return size;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...