Через некоторое время соединение прервано в поточном процессе в запросах сокета tcp (c / linux) - PullRequest
0 голосов
/ 11 мая 2009

Я пытаюсь сделать процесс, который принимает количество запросов каждую секунду, при каждом запросе создается новый поток. Затем каждый поток открывает соединение сокета с адресом (порт http), отправляет запросы HEAD, получает ответ и закрывает сокет.
Проблема у меня возникает, когда я отправляю более 3 запросов в секунду, через некоторое время я получаю сообщение об ошибке в части функции send (), я продолжаю получать отказ в соединении. Если я ввожу больше запросов в секунду, я получаю ошибки раньше. Если я помещаю только 2 запроса в секунду, я не получаю ошибок вообще. Я подозреваю, что у меня заканчивается какой-то ресурс, но я не могу найти какой.

Вот базовая структура кода

//declarations

socketfd = socket(servinfo->ai_family,servinfo->ai_socktype,servinfo->ai_protocol);

arg = fcntl(socketfd, F_GETFL, NULL)) < 0);
arg |= O_NONBLOCK;
fcntl(socketfd, F_SETFL, arg)

if((conn = connect(socketfd, servinfo->ai_addr, servinfo->ai_addrlen)) < 0)
{
    if(errno == EINPROGRESS)
    {
        do
        {
            tv.tv_sec = CONNECT_TIMEOUT;
            tv.tv_usec = 0;
            FD_ZERO(&myset);
            FD_SET(socketfd, &myset);
            if((res = select(socketfd+1, NULL, &myset, NULL, &tv) > 0)) 
            {
                if( (arg = fcntl(socketfd, F_GETFL, NULL)) < 0) { 
                    perror("fcntl get 2");
                } 
                arg &= (~O_NONBLOCK); 
                if( fcntl(socketfd, F_SETFL, arg) < 0) {
                    perror("fcntl set 2");
                }
                char szBuf[4096];

                std::string htmlreq = "HEAD / HTTP/1.1\r\nHost:";
                htmlreq += info->hostName;
                htmlreq += "\r\n\r\n";

                if((conn = send(socketfd,htmlreq.c_str(),htmlreq.size(),0)) == -1 && errno != EINTR)
                {
                    perror("send");
                    close(socketfd);
                    return;
                }

                if((conn = recv(socketfd,szBuf,sizeof(szBuf)+1,0)) < 0 && errno != EINTR)
                {
                    perror("recv");
                    close(socketfd);
                    return ;
                }

                close(socketfd);

                // do stuff with data
                break;
            }
            else
            {
                //timeout
                break;
            }
        }while(1);
    }
    else
    {
        perror("connect");
        close(socketfd);
        return; 
    }
}

Я удалил некоторую проверку ошибок с самого начала, что я получаю как вывод: «Отправить: Соединение отказано» через некоторое время. Буду признателен за некоторые указания на то, какая часть может вызывать проблемы, платформа Ubuntu Linux. Я также был бы рад опубликовать другие части кода, если это необходимо. Tnx заранее.

Ответы [ 2 ]

1 голос
/ 11 мая 2009

Ресурс, который у вас, вероятно, заканчивается, находится на сервере, к которому вы подключаетесь. Компьютер, к которому вы подключаетесь, отклоняет соединение, потому что оно либо:

  1. Настройка ограничения количества соединений в секунду (по некоторым критериям)
  2. Или сервер, к которому вы подключаетесь, по какой-то причине слишком загружен и не может больше подключаться.

Поскольку при третьем соединении вы всегда получаете сообщение об ошибке, возможно, сервер, к которому вы подключаетесь, ограничивает количество подключений для каждого IP-адреса.

Edit1

Вы пытаетесь сделать неблокирующее соединение? Теперь, когда я посмотрю на него поближе, звучит так, как будто ваша проблема с select, так как в select возвращается, что сокет доступен для чтения, прежде чем он действительно подключится, а затем вы вызываете send. При неблокирующих соединениях следует следить за тем, чтобы при ошибке сокет становился читаемым и записываемым. Это означает, что вам нужно проверить оба после выбора, иначе вы можете пропустить любую ошибку и увидеть ошибку отправки.

Это от Стивенса УНП:

FD_ZERO(&rset);
FD_SET(sockfd, &rset);
wset = rset;
tval.tv_sec = nsec;
tval.tv_usec = 0;

if ( (n = Select(sockfd+1, &rset, &wset, NULL,
                 nsec ? &tval : NULL)) == 0) {
    close(sockfd);      /* timeout */
    errno = ETIMEDOUT;
    return(-1);
}

if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
    len = sizeof(error);
    if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
        return(-1);         /* Solaris pending error */
} else
    err_quit("select error: sockfd not set");

done:
Fcntl(sockfd, F_SETFL, flags);  /* restore file status flags */

if (error) {
    close(sockfd);      /* just in case */
    errno = error;
    return(-1);
}
return(0);
0 голосов
/ 11 мая 2009

В вашем коде довольно много проблем.

Сначала вы устанавливаете сокет в неблокирующее состояние. Я не понимаю, зачем ты это делаешь. Функция подключения имеет внутреннее время ожидания и поэтому не будет блокироваться.

Другая проблема вашего кода заключается в том, что первый оператор if пропустит блок инструкций, если соединение сразу установится! Что может случиться.

Вы, очевидно, хотите сначала отправить сообщение HEAD. Нет никакой необходимости делать этот блок неблокирующим, если вы не ожидаете, что удаленный сервер или сеть будут работать очень медленно, и вам не нужно время для этого. В этом случае выбор с неблокирующим сокетом будет иметь смысл.

После того, как вы отправите сообщение HEAD, вы ожидаете получить в ответ некоторые данные, которые вы собираете с помощью функции recv. Помните, что этот вызов функции может вернуться до получения всех отправленных данных. Вам нужен независимый способ определить, что все отправленные данные были получены. Будет ли сервер закрывать соединение? Это будет обнаружено функцией recv, возвращающей 0.

Таким образом, recv должен быть заключен в цикл, в который вы добавляете полученные данные в некоторый буфер или файл и завершаете работу, когда recv возвращает 0. Используйте неблокирующий сокет, если вы хотите добавить тайм-аут в эту операцию recv, которая может действительно блок.

Но сначала попробуйте без тайм-аутов, чтобы убедиться, что он работает на полной скорости без блокировки в качестве текущей версии.

Я подозреваю, что первоначальное соединение медленное из-за разрешения имен и IP-адресов, и ускоряется при последующих вызовах, потому что данные кэшируются.

...