проблема архитектуры epoll с проблемой бутылочного горлышка - PullRequest
0 голосов
/ 23 июня 2011
while(m_severRun){

    printf("ServerManager::eventAcceptLoop, epoll_wait\n");
    int event_cnt = epoll_wait(m_epfd, m_events, EPOLL_SIZE, -1);
    if(event_cnt == -1){
        perror("epoll_wait error \n");
        break;
    }

    for(int i=0; i<event_cnt; i++){

        SocketClient *conn = reinterpret_cast<SocketClient *>(m_events[i].data.ptr);


        if(conn->getFd() == m_serverSocket->getFd()){

            printf("ServerManager::eventAcceptLoop, A Client has been connected \n");

            struct sockaddr_in clnt_adr;
            socklen_t adr_sz = sizeof(clnt_adr);
            int clnt_sock = accept(m_serverSocket->getFd(), (struct sockaddr*)&clnt_adr, &adr_sz);

            SocketClient* client = new SocketClient(clnt_sock);
            if(!addClient(client))
                break;
        }
        else{

            if(m_events[i].events & EPOLLRDHUP){
                printf("ServerManager::eventAcceptLoop, EPOLLRDHUP \n");
                removeClient(conn);
                close(conn->getFd());
                continue;
            }

            if(m_events[i].events & EPOLLIN){

                printf("ServerManager::eventAcceptLoop, EPOLLIN \n");
                int recv = conn->recv();

                if(recv <= 0){
                    removeClient(conn);
                    close(conn->getFd());
                }
                else{
                    printf("ServerManager::eventAcceptLoop, A message has been received \n");
                    vector<char> data = conn->getData();
                    addWork(conn, data);
                }
            }

            if(m_events[i].events & EPOLLERR)
                printf("ServerManager::eventAcceptLoop, EPOLLERR \n");
        }
    }//for loop end
}//while loop end

Я работаю над сетевым программированием (tcp) и у меня есть этот код. Я впервые использую epoll, поэтому я не уверен, что этот дизайн правильный. Кроме того, я использую пул потоков (5 дочерних потоков) и всякий раз, когда я читаю данные из epoll, я помещаю их в очередь в пуле потоков. Проблема в том, что в функции чтения я вижу проблему с горлышком бутылки.

В функции чтения он вызывает ObserveSocket

int SocketClient::ObserveSock(int sock, int timeout){

    printf("SocketClient::ObserveSock called\n");

    fd_set reads;
    int fd_max;
    struct timeval _timeout;

    FD_ZERO(&reads);
    FD_SET(sock, &reads);
    fd_max = sock + 1;

    _timeout.tv_sec = timeout;
    _timeout.tv_usec = 0;

    return select(fd_max, &reads, 0, 0, &_timeout);
}

Он наблюдает за сокетом, и если в течение определенного времени нет сигнала, он возвращает 0, чтобы закрыть сокет. Я подумал, что мне нужен этот код для обнаружения неожиданного отключения пользователя или повреждения данных (клиент отправил 100 байтов, но сервер получил 90 байтов, тогда сервер будет ждать последние 10 байтов, которые не будут получены).

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

Я также поищу любой хороший учебник, который использует epoll и подробно описывает обработку исключений.

Заранее спасибо.

EDIT

Внутри функции recv () она просто вызывает функцию чтения, а перед чтением я вызываю ObserveSocket

1 Ответ

2 голосов
/ 23 июня 2011

Он смотрит на розетку и, если есть нет сигнала в течение определенного времени затем возвращает 0, чтобы закрыть сокет.

Почему?

Я думал, что мне нужен этот код для обнаружения неожиданное отключение пользователя или данных повреждение (клиент отправил 100 байт, но сервер получил 90 байтов, то сервер будет ждать последние 10 байт который не прибудет).

Вы не. Вы получите еще одно событие чтения, при котором чтение вернет 0, указывая EOS, или событие ошибки.

Если вы хотите реализовать таймаут неактивности, вы должны реализовать его в главном цикле выбора . То есть следите за последним временем активности для каждого сокета и, если он слишком длинный, закройте сокет или сделайте все, что вам нужно сделать. Поместите это тестирование в конец цикла select (), прежде чем выполнять итерацию и снова вызывать select ().

В данный момент каждое событие чтения блокирует все остальные события select () на время ожидания. Таким образом, весь ваш сервер заблокирован.

...