LINUX.ORG.RU

возможно, глупый вопрос про epoll

 , ,


0

2

здравствуйте, есть код простейшего эхо-сервера:

#ifndef MAX_BUF
#define MAX_BUF 1024
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = sig_usr;
    sigset_t   set;
    sigemptyset(&set);
    sigaddset(&set, SIGKILL);
    sigaddset(&set, SIGCHLD);
    act.sa_mask = set;
    sigaction(SIGKILL, &act, 0);
    sigaction(SIGCHLD, &act, 0);

    //устанавливаем параметры серверного сокета
    struct sockaddr_in server;
    char client_message[MAX_BUF];
    std::fill(client_message,
              client_message + std::size(client_message) - 1,
              0);

    int listen_desc = ::socket(AF_INET , SOCK_STREAM , 0);
    if (listen_desc == -1) {
        std::cout << "Could not create socket\n";
        ::exit(EXIT_FAILURE);
    }
    setnonblock(listen_desc);

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( 1600 );

    int yes = 1;
    ::setsockopt(listen_desc, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

    if( ::bind(listen_desc,(struct sockaddr *)&server , sizeof(server)) < 0) {
        //print the error message
        std::cout << "error in bind\n";
        ::exit(EXIT_FAILURE);
    }

    ::listen(listen_desc , 5);

    //клиентский сокет
    int cs{};

    // create epoll instance
    int epfd;
    epfd = epoll_create(5);
    if (epfd == -1) {
        std::cout << "error in epoll_create\n";
    }

    struct epoll_event evlist[count];
    struct epoll_event ev;
    ev.data.fd = listen_desc;
    ev.events = EPOLLIN | EPOLLET;
    if(::epoll_ctl (epfd, EPOLL_CTL_ADD, listen_desc, &ev) == -1) {
        std::cout << "error in epoll_ctl\n";
    }

    while (/*strstr(request, "close") == NULL*/true) {

        int ready = epoll_wait(epfd, evlist, count, -1);
        if(ready == -1) {
            std::cout << "interrupt syscall epoll_wait was occured\n";
            if(errno == EINTR) {
                std::cout << "continue epoll_wait\n";
                continue;
            }
            std::cout<< "error in epoll_wait and exit\n";
            ::exit(1);
        }
        //добавляем новый дескриптор
        for(auto i=0;i<ready; ++i) {

            if ((evlist[i].events & EPOLLERR) ||
                (evlist[i].events & EPOLLHUP) ||
                (!(evlist[i].events & EPOLLIN)))
            {
                //ошибка в epoll или сокет не готов читать
                std::cout << "epoll error\n";
                ::close (evlist[i].data.fd);
                continue;
            }

            //если событие пришло из слушающего дескриптора
            if(evlist[i].data.fd == listen_desc) {
                struct sockaddr_in in_addr;
                socklen_t in_len;
                in_len = sizeof(in_addr);
//                cs = ::accept(listen_desc, (struct sockaddr *) &client,
//                                            (socklen_t*)&c);
                cs = ::accept (listen_desc, (sockaddr*)&in_addr, &in_len);
                if(cs == -1){
                    std::cout<<"error in accept on listen descriptor\n";
                    continue;
                }

                //добавляем полученный дескриптор в ожидающие
                setnonblock(cs);
                ev.data.fd = cs;
                ev.events = EPOLLIN | EPOLLET;
                if (epoll_ctl(epfd, EPOLL_CTL_ADD, cs, &ev) == -1) {
                    std::cout << "error in add descriptor to epoll\n";
                    continue;
                }
            } else {
                char buf[MAX_BUF];
                ::memset(buf, 0, sizeof(buf));
                int byte_count = ::read(evlist[i].data.fd, buf, MAX_BUF);
                if (byte_count == -1) {
                    std::cout << "error in read\n";
                    continue;
                }
                //если клиент закрыл соединение
                else if(byte_count == 0) {
                    if (epoll_ctl(epfd, EPOLL_CTL_DEL, evlist[i].data.fd, &ev) == -1) {
                        std::cout << "error in delete descriptor from epoll\n";
                        continue;
                    }
                    std::cout << "client with descriptor "
                              << evlist[i].data.fd
                              << " close connection\n";
                    ::close(evlist[i].data.fd);
                } else {

                    //печатаем на экран и отправляем обратно пришедшее сообщение
                    std::fill(client_message,
                              client_message + std::size(client_message) - 1,
                              0);
                    ::memcpy(client_message, buf, byte_count);
                    std::cout << client_message;

                    char response[] =
                            // Заголовок.
                            "HTTP/1.1 200 OK \n Content-Type: text/xml;charset=utf-8 \n Content-Length: 256"
                            // Тело HTML страницы.
                            "<!doctype html>"
                            "<html lang=\"en\">"
                            "<head>"
                            "<meta charset=\"UTF-8\">"
                            "<title>Document</title>"
                            "</head>"
                            "<body>"
                            "ваш запрос: <strong>1</strong>"
                            "</body>"
                            "</html>\n";
                        if (::send(evlist[i].data.fd, response/*client_message*/, sizeof(response)/*byte_count*/, 0) == -1) {
                            std::cout << "error in send\n";
                        }
                    //                    //std::cout << buf;
                    //                    if (::send(evlist[i].data.fd, /*response*/client_message, /*sizeof(buf)*/byte_count, 0) == -1) {
                    //                        std::cout << "error in send\n";
                    //                    }
                }
            }
        }
    }

#else
#endif
вроде все работает, однако во многих примерах использования epoll, которые я видел, сокеты делались неблокирующими... объясните, какой смысл использовать неблокирующий сокет внутри epoll? не будет ли cpu жрать ресурсы вхолостую?

з.ы. исправил кое-что, теперь сокеты неблокирующие, возвращается html-страница как ответ

Вы неправильно используете EPOLLET.

какой смысл использовать неблокирующий сокет внутри epoll?

Неблокирующий сокет используют, чтобы не заблокироваться на i/o операциях.

не будет ли cpu жрать ресурсы вхолостую?

Нет. Хотя если читать в цикле, то да.

Sorcerer ★★★★★
()
Ответ на: комментарий от Sorcerer

хм, кажись понял: для edge-triggered нужно всегда неблокирующие сокеты использовать

xperious ★★
() автор топика

какой смысл использовать неблокирующий сокет внутри epoll?

Epoll сообщает тебе, когда состояние сокета изменилось. Данные пришли, место в буфере отправки появилось, ошибка произошла. К блокирующимся сокетам это не относится.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

хм, начал читать про неблокирующий ввод/вывод у стивенса с примером для select - чет сложновато пока... а вот в той теме где я спрашивал про мультиплексирование https://www.linux.org.ru/forum/development/13698627, когда говорили про один epoll в одном потоке, то имелся ввиду epoll с неблокирующими сокетами?

xperious ★★
() автор топика

здравствуйте, есть код простейшего эхо-сервера:

4 экрана кода

Лол.

anonymous
()
Ответ на: комментарий от xperious

очевидно неблокирующий, а иначе как он обслужит все остальное ? а вообще у вас там ссылка на хабр, а с хабра на гитхам, где куча примеров с кодом по соревнованию

anonymous
()
Ответ на: комментарий от anonymous

я тот код с хабра не понимаю чутка: зачем в обработчике событий клиентских сокетов стоит еще один while(1)?

xperious ★★
() автор топика
Ответ на: комментарий от anonymous

очевидно неблокирующий, а иначе как он обслужит все остальное ?

ну в моем примере ж блокирующий, и обслуживает все норм)

xperious ★★
() автор топика
Ответ на: комментарий от xperious

когда говорили про один epoll в одном потоке, то имелся ввиду epoll с неблокирующими сокетами?

Да.

i-rinat ★★★★★
()
Ответ на: комментарий от xperious

ну в моем примере ж блокирующий, и обслуживает все норм)

Попробуй написать клиент, который только шлёт данные, но никогда их не принимает, натрави его на свой сервер, подожди, пока он перестанет слать. Потом попробуй открыть ещё одно соединение с сервером, уже интерактивное. Будет при этом эхо работать?

И ещё. Тебе нужно использовать не struct sockaddr, а struct sockaddr_in и кастовать указатель на его в struct sockaddr *. Вот такое вот API. По факту твой код будет работать, потому что в текущих реализациях struct sockaddr дополняется байтами до 16 байт, поэтому у них с struct sockaddr_in одинаковый размер. Но не факт, что struct sockaddr хватит для любого адреса. В какой-то момент ты решишь использовать свой подход для unix domain сокета и словишь странный баг. Лучше сразу привыкать использовать API правильно.

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от xperious

а вы хотели пардон ковыряясь пальцем в носу впрыгуть с нетворк программинг ? помните лучший способ научиться плавать ? что бы вас бросили в вводу

anonymous
()

Сокеты рекомендуется переводить в неблокирующий режим чтобы в

::send(evlist[i].data.fd, /*response*/buf, sizeof /*response*/buf, 0)
Весь процесс не встал колом если у текущего сокета переполнился write буффер

zaz ★★★★
()
Ответ на: комментарий от zaz

Весь процесс не встал колом если у текущего сокета переполнился write буффер

ну да, уже вычитал в доках https://www.opennet.ru/man.shtml?topic=epoll&category=4&russian=0

жалко примеров нету, а то можно совсем чирикнуться с нюансами

xperious ★★
() автор топика
Последнее исправление: xperious (всего исправлений: 1)
Ответ на: комментарий от xperious

а что ожидалось?

Вообще я ожидал, что забьётся буфер отправки, и процесс сервера повиснет на send(), перестав принимать новые соединения. Но вместо этого на send() подвисает клиент, поэтому до блокировки сервера дело не доходит.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

ну, насколько я понял, edge-triggered режим эффективней(почему правда не понял) и для него всегда надо использовать неблокирующие сокеты

еще вот другой вопрос: предположим, есть один поток с блокирующим accept и другой поток для epoll с неблокирующими сокетами... как можно передать новый дескриптор в поток с epoll, если этот поток висит в epoll_wait? нормальное ли решение при получении нового дескриптора генерировать сигнал, в обработчике сигнала добавлять дескриптор в epoll массив, а epoll_wait перезапускать по EINTR?

xperious ★★
() автор топика
Ответ на: комментарий от xperious

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

anonymous
()
Ответ на: комментарий от xperious

ну, насколько я понял, edge-triggered режим эффективней(почему правда не понял)

А я вот не понял, что значит эффективней. По-моему, он просто другой.

и для него всегда надо использовать неблокирующие сокеты

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

предположим, есть один поток с блокирующим accept и другой поток для epoll с неблокирующими сокетами... как можно передать новый дескриптор в поток с epoll, если этот поток висит в epoll_wait?

В мане написано, что можно прям из любого потока через epoll_ctl добавлять в то время, как другой поток заблокировался на epoll_wait. Для варианта с poll() я бы использовал пайп для синхронизации.

нормальное ли решение при получении нового дескриптора генерировать сигнал

Нет, не имеет смысла. Насколько я помню, если сигнал приходит, пока ты обрабатываешь сигнал, он игнорируется. То есть одинаковые сигналы, посланные подряд, могут теряться.

i-rinat ★★★★★
()
Ответ на: комментарий от xperious

значит до встречи через месяц? прийдете в роли специалиста по епул консультировать других ?

anonymous
()
Ответ на: комментарий от i-rinat

А я вот не понял, что значит эффективней. По-моему, он просто другой.

да эт я неправильно распарсил строку

By contrast, when used as a level-triggered interface (the default, when EPOLLET is not specified), epoll is simply a faster poll(2)

В мане написано, что можно прям из любого потока через epoll_ctl добавлять в то время, как другой поток заблокировался на epoll_wait

что-то не могу этого найти...

xperious ★★
() автор топика
Последнее исправление: xperious (всего исправлений: 1)
Ответ на: комментарий от xperious
$ man epoll_wait | grep -A 9 NOTES
NOTES
       While  one thread is blocked in a call to epoll_pwait(), it is possible
       for another thread to add a file descriptor to  the  waited-upon  epoll
       instance.   If the new file descriptor becomes ready, it will cause the
       epoll_wait() call to unblock.

       For a discussion of what may happen if a file descriptor  in  an  epoll
       instance  being  monitored by epoll_wait() is closed in another thread,
       see select(2).

$ 
i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

хм, в общем исправил чуток код, теперь новая напасть: решил протестировать через apache benchmark, однако никаких телодвижений со стороны сервера не наблюдается... если подключаться телнетом, то нормально, сервер отвечает страницей, но если попробовать через браузер, то печатается запрос браузера, а send просто игнорируется в отладчике... почему такой треш?

попробовал блокирующий accept в другом потоке - то же самое...

xperious ★★
() автор топика
Последнее исправление: xperious (всего исправлений: 1)
Ответ на: комментарий от xperious

а send просто игнорируется в отладчике

Что это значит?

Ты не проверяешь возвращаемое значение у send(). Он не обязан отсылать весь буфер, даже если сокет в блокирующем режиме. Можно, конечно, пытаться выяснить, что происходит в этом конкретном случае, но от этого уже мало пользы. Лучше напиши корректный код.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Он не обязан отсылать весь буфер, даже если сокет в блокирующем режиме

может плохо искал, но как точно отправлять так и не нашел, сварганил что-то такое:

bool send_all(int socket, const char *buffer, size_t length, int flags)
{
    int i = 0;
    while (length > 0)
    {
        char *ptr = (char*)(buffer + i);
        i = ::send(socket, ptr, length - i, flags);
        if (i < 1) return false;
        ptr += i;
        length -= i;
    }
    return true;
}

теперь отправляю так:

if(!send_all(evlist[i].data.fd, response, sizeof(response),0 ))
                        std::cout << "error in send\n";

если запустить wireshark и

ab -k -c 10 -n 1000 http://0.0.0.0:1600/

то видно, что устанавливаются 10 соединений, отправляются 10 гет запросов от ab, сервер отвечает 10 ответов 200 OK... и на этом все... висим

xperious ★★
() автор топика
Ответ на: комментарий от xperious

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

anonymous
()
Ответ на: комментарий от xperious

length - i

ptr += i;

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

устанавливаются 10 соединений, отправляются 10 гет запросов от ab, сервер отвечает 10 ответов 200 OK... и на этом все... висим

У тебя же нет реализации HTTP, даже простой. Читаешь просто кусок данных, потом сразу шлёшь ответ. Представь, что тебе клиент будет слать по одному байту за раз. Этот случай тоже должен обрабатываться. Пока нет проверок, можно только гадать, что там происходит. Всё сильно зависит от того, как пакеты разбиваются, как данные буферизуются.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Ты как будто наобум пишешь

да, согласен... походу надо нормально выспаться... чет посмотрел сам что написал и ужаснулся

Представь, что тебе клиент будет слать по одному байту за раз

вот подобное я еще хотел уточнить: в неблокирующем режиме с помощью epoll в принципе можно циклом таким как у меня(исправленным только) отправлять данные, или там нужно что-то обязательно варганить с EPOLLOUT? хочу понять я на правильном пути или нет... просто, зараза, все отправляется и создается ощущение что все нормально, вот и спрашиваю поэтому

xperious ★★
() автор топика
Ответ на: комментарий от xperious

или там нужно что-то обязательно варганить с EPOLLOUT

Если не хочешь блокироваться, нужно.

все отправляется и создается ощущение что все нормально

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

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

хм, в общем попробовал файлы разной величины качать с помощью epoll... разное поведение наблюдается и в зависимости от blocking/non-blockig режима сокетов(ну об этом речь шла уже выше) и от величины скачиваемых за раз байтов( в методе read )...

#define BUFF_DOWNLOAD_SIZE 1024 * 64

int send_get_request(int descriptor, char const* address, char const* location) {
    std::string req = "GET ";
    req += location;
    req += " HTTP/1.0\r\n";
    req += "Host: ";
    req += address;
    req += "\r\n\r\n";
    int byte_count = ::send(descriptor, req.data(), 1024, 0);
    return byte_count;
}

void client( char const* address, char const* location, int port) {

    int sockfd;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    char buffer[256];
    ::memset(buffer, 0, sizeof(buffer));

    sockfd = ::socket(AF_INET, SOCK_STREAM, 0);

    if (sockfd < 0) {
       std::cout << "ERROR opening socket\n";
       ::exit(1);
    }

    server = ::gethostbyname(address);

    if (server == NULL) {
       std::cout << "ERROR, no such host\n";
       ::exit(0);
    }

    ::bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    ::bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
    serv_addr.sin_port = htons(port);

    if (::connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
       std::cout << "ERROR connecting\n";
       ::exit(1);
    }

    int epfd = epoll_create(5);
    if (epfd == -1) {
        std::cout << "error in epoll_create\n";
    }

    //синхронно отправляем гет запрос
    if( send_get_request(sockfd, address, location) < 0) {
        std::cout << "error in send GET request\n";
        ::exit(1);
    }

    struct epoll_event evlist[1024];
    struct epoll_event ev;
    setnonblock(sockfd);
    ev.data.fd = sockfd;
    ev.events = EPOLLIN | EPOLLET/* | EPOLLOUT*/;
    if(::epoll_ctl (epfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {
        std::cout << "error in epoll_ctl\n";
    }

    while(true) {
        int ready = epoll_wait(epfd, evlist, 1024, -1);
        if(ready == -1) {
            std::cout << "interrupt syscall epoll_wait was occured\n";
            if(errno == EINTR) {
                std::cout << "continue epoll_wait\n";
                continue;
            }
            std::cout<< "error in epoll_wait and exit\n";
            ::exit(1);
        }

        for(auto i=0; i<ready; ++i) {
            switch(evlist[i].events) {
                case EPOLLIN  : {
                    char buf[BUFF_DOWNLOAD_SIZE];
                    ::memset(buf, 0, sizeof(buf));
                    int byte_count = ::read(evlist[i].data.fd, buf, sizeof(buf));
                    if (byte_count == -1) {
                        std::cout << "error in read\n";
                        break;
                    }
                    //если клиент закрыл соединение
                    else if(byte_count == 0) {
                        if (epoll_ctl(epfd, EPOLL_CTL_DEL, evlist[i].data.fd, &ev) == -1) {
                            std::cout << "error in delete descriptor from epoll\n";
                            break;
                        }
                        std::cout << "client with descriptor "
                                  << evlist[i].data.fd
                                  << " close connection\n";
                        ::close(evlist[i].data.fd);
                    } else {
                        std::cout << buf;
                    }
                    break;
                case EPOLLERR :
                case EPOLLHUP : break;
                default       : break;
            }
        }

    }


}
int main(int argc, char** argv) {
   //скачиваем какой-то видеофайл размером чуть больше 5мб
   client("www.mysticfractal.com","/video/cookie.avi", 80);
   return 0;
}

можете объяснить почему и при блокирующих и при неблокирующих сокетах при величине BUFF_DOWNLOAD_SIZE 1024 мы не докачиваем файл? при BUFF_DOWNLOAD_SIZE 1024 * 64 при блокирующих не докачиваем, при неблокирующих докачиваем(ну вроде так и должно быть)

xperious ★★
() автор топика
Ответ на: комментарий от xperious

почему и при блокирующих и при неблокирующих сокетах при величине BUFF_DOWNLOAD_SIZE 1024 мы не докачиваем файл?

Читаешь 1024 байта, а потом ждёшь, что epoll тебя ещё событие пришлёт, при этом запросив edge-triggered. Если на момент чтения в буфере сокета накопилось данных больше, чем 1024 байта, после одного read() данные там останутся. И epoll может тебе больше никогда не сгенерировать событие. Если ты читаешь больше байт за раз, ниже вероятность того, что данные останутся после этого одного read().

Итого: читать нужно, пока read() не вернёт тебе -1, с errno=EAGAIN или 0.

А ещё код у тебя чудной. Ты из какой кладовки bzero() и bcopy() откопал? Их ещё в 2001 году объявили устаревшими.

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от i-rinat

хм, я так понимаю вот такое в case EPOLLIN:

case EPOLLIN  : {
                    char buf[BUFF_DOWNLOAD_SIZE];
                    ::memset(buf, 0, sizeof(buf));
                    int byte_count = 1;
                    while( true ) {
                        byte_count = ::read(evlist[i].data.fd, buf, sizeof(buf));
                        if (byte_count == -1) {
                            //std::cout << "error in read\n";
                            if(errno != EAGAIN) {
                                std::cout << "not EAGAIN\n";
                            }
                            break;
                        }
                        //если клиент закрыл соединение
                        else if(byte_count == 0) {
                            if (epoll_ctl(epfd, EPOLL_CTL_DEL, evlist[i].data.fd, &ev) == -1) {
                                std::cout << "error in delete descriptor from epoll\n";
                                break;
                            }
                            std::cout << "client with descriptor "
                                      << evlist[i].data.fd
                                      << " close connection\n";
                            ::close(evlist[i].data.fd);
                        } else {
                            std::cout << buf;
                        }
                    }
                    }
                    break;
не есть нормальное решение проблемы? т.е. polling который втупую жрет ресурсы(особенно если клиент медленный)... хотя в этом случае хоть блокирующий сокет, хоть нет - все равно качает

такой цикл во многих примерчиках есть(

xperious ★★
() автор топика
Последнее исправление: xperious (всего исправлений: 2)
Ответ на: комментарий от i-rinat

А ещё код у тебя чудной. Ты из какой кладовки bzero() и bcopy() откопал?

да скопипастил для инициализации сокета с какого-то сайта... не сильно парился, epoll больше волнует

xperious ★★
() автор топика
Ответ на: комментарий от xperious

не есть нормальное решение проблемы?

Выглядит нормально. Единственно что — в std::cout ты выводишь весь буфер, а не byte_count полученных байт. В конце будет выведен мусор.

т.е. polling который втупую жрет ресурсы

Прочитаешь всё, что пришло в сокет к данному моменту, read вернёт -1 с errno==EAGAIN, дальше управление доходит до epoll_wait, где блокируется, пока новые данные в сокет не придут. Всё норм.

i-rinat ★★★★★
()
Ответ на: комментарий от xperious

да скопипастил для инициализации сокета с какого-то сайта

Не копипасти код. Перепечатывай руками.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Прочитаешь всё, что пришло в сокет к данному моменту, read вернёт -1 с errno==EAGAIN, дальше управление доходит до epoll_wait, где блокируется, пока новые данные в сокет не придут. Всё норм.

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

xperious ★★
() автор топика
Последнее исправление: xperious (всего исправлений: 1)
Ответ на: комментарий от i-rinat

хотя... если поставить после epoll_wait что-то типа std::cout << «epoll_wait\n»; то в блокирующем режиме вывод epoll_wait только один, в неблокирующем их масса... т.е. по сути, если много клиентов качается, то мы можем их рандомно кусками качать, т.е. типа асинхронно каждый из них( хотя на деле, конечно, один за другим). ну тогда профит понятен более-менее

xperious ★★
() автор топика
Ответ на: комментарий от xperious

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

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

Благодаря этому в одном потоке можно обрабатывать сразу много соединений. В случае с блокирующимся чтением ты просто подвисаешь на read(), и пока на этот сокет данных не придёт, ждёшь. Если хочется несколько соединений одновременно, нужно либо форкаться и обрабатывать соединения в отдельных процессах, либо запускать нити и обрабатывать в нитях.

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

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

хм, вопросик возник: вот одно дело эхо-сервер - скачали данные от одного EPOLLIN полностью и отправили их(и так для каждого EPOLLIN), другое дело http-сервер... получается, нужно принять все данные от клиента т.е. получить несколько EPOLLIN(http-запрос то всегда небольшой по идее, но, теоретически, может быть несколько EPOLLIN от клиента пославшего этот запрос), от каждого EPOLLIN данные положить в кучу; затем, когда их все получили, то засунуть в все данные один массив(опять же динамически аллоцированный), и только после этого анализировать заголовки, тело и т.д. чет мне кажется что все это дико медленно, прав ли я в алгоритме?

xperious ★★
() автор топика
Ответ на: комментарий от xperious

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

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.