После большой отправки мой вызов send () приводит к полной остановке моей программы. Как это возможно? - PullRequest
3 голосов
/ 31 декабря 2010

Так что в основном я делаю MMO-сервер на C ++, который работает на Linux.Сначала он работает нормально, но через 40 секунд с 50 клиентами он полностью остановится.Когда я отлаживаю его, я обнаруживаю, что в основном последний кадр включен до того, как он перестает отвечать, это syscall (), после чего он исчезает в ядре.Как только он исчезает в ядре, он даже не возвращает значение ... это совершенно сбивает с толку.

Каждый из 50 клиентов отправляет 23 байта каждые 250 миллисекунд.Эти 23 байта затем передаются всем остальным 49 клиентам.Этот процесс начинает замедляться, а затем в конечном итоге полностью останавливается, когда ядро ​​никогда не возвращается из системного вызова для команды send ().Каковы некоторые возможные причины здесь?Это действительно сводит меня с ума!

Один из вариантов, который я нашел, - это алгоритм Наглса, который вызывает задержки.Я пытался переключить его, но это все равно происходит.

Редактировать: программа застряла здесь.В частности, при отправке, которая, в свою очередь, вызывает syscall ()

bool EpollManager::s_send(int curFD, unsigned char buf[], int bufLen, int flag) 
//     Meant to counteract partial sends
{
    int sendRetVal = 0;
    int bytesSent = 0;
    while(bytesSent != bufLen)
    {
 print_buffer(buf, bufLen);
        sendRetVal = send(curFD, buf + bytesSent, bufLen - bytesSent, flag); 

        cout << sendRetVal << " ";
        if(sendRetVal == -1)
        {
            perror("Sending failed");
            return false;
        }
        else
            bytesSent += sendRetVal;
    }
    return true;
}

Также это метод, который вызывает s_send.

    void EpollManager::broadcast(unsigned char msg[], int bytesRead, int sender)
    {
 for(iMap = connections.begin(); iMap != connections.end(); iMap++)
 {
  if(sender != iMap->first)
  {
   if(s_send(iMap->first, msg, bytesRead, 0)) // MSG_NOSIGNAL
   {
       if(debug)
       {
                    print_buffer(msg, bytesRead);
                    cout << "sent on file descriptor " << iMap->first << '\n';
       }
   }
  }
 }
 if(connections.find(sender) != connections.end())
        connections[sender]->reset_batch();
    }

И для выяснения соединений является примером boostunordered_map.Данные, которые заполняет программа, также не являются уникальными.Он успешно транслировался на другие файловые дескрипторы, но подавляется, по крайней мере на первый взгляд, случайным.

Ответы [ 2 ]

2 голосов
/ 31 декабря 2010

Управление перегрузкой TCP, т. Е. Алгоритм Nagle, наряду с полным буфером (опция сокета SO_SNDBUF) приведет к блокировке send() и аналогичных операций.

Ленивый способ обойти это состоит в реализации отдельныхпотоки для каждого сокета, но это не слишком масштабируется.В Linux вы должны использовать неблокирующие сокеты с poll() или аналогичные, в Windows вы должны исследовать порты завершения ввода-вывода.Посмотрите на библиотеки промежуточного программного обеспечения, чтобы упростить это, libevent - это популярный кроссплатформенный пример с недавним включением поддержки Windows IOCP, в качестве альтернативы Boost: ASIO для C ++.

ПолезноЧитайте статью о масштабируемости ввода-вывода: Проблема C10K .

Обратите внимание, что вы действительно не хотите отключать Nagle для интернет-трафика, даже в локальной сети вы можете увидеть серьезные проблемы без какой-либо формыобратная связь с заторами.

1 голос
/ 31 декабря 2010

Ядро сохраняет конечный буфер для отправки данных.Если получатель не получает, этот буфер заполнится, а отправитель заблокируется.Может ли это быть проблемой?

...