Функция Socket read () иногда никогда не возвращает 0 при обработке туннелирования - PullRequest
0 голосов
/ 31 мая 2018
if(port == 443){ // https
    strcpy(buffer, "HTTP/1.0 200 Connection established\r\n\r\n");
    write(client, buffer, strlen(buffer));
    tunnel(server, client, buffer);
    return;
}

tunnel () выглядит следующим образом

void tunnel(int server, int client, char* buffer){
    int servercon, clientcon;
    int x;
    x = fcntl(server, F_GETFL, 0);
    fcntl(server, F_SETFL, x|O_NONBLOCK);
    x = fcntl(client, F_GETFL, 0);
    fcntl(client, F_SETFL, x|O_NONBLOCK);
    while(true){
        cout << "test" << endl;
        clientcon = read(client, buffer, BUFSIZE);
        write(server, buffer, clientcon);
        servercon = read(server, buffer, BUFSIZE);
        write(client, buffer, servercon);
        if(???) // This condition
            break;
    }

    close(client);
    close(server);
}

Я пытаюсь реализовать туннелирование, когда прокси-сервер получает метод CONNECT.Этот код работает хорошо (веб-сайты HTTPS успешно загружены). Однако проблема в том, что бесконечный цикл while () не может быть экранирован, потому что функция read () иногда никогда не возвращает 0.

В качестве условия прерывания я попыталсянесколько вариантов.

1) servercon == 0 (иногда функция read () никогда не возвращает 0)

2) servercon == -1 (происходит сбой безопасного соединения. Я не знаю почему)

3) Добавить переменную i в качестве таймера и i <100000 (работает, но слишком неэффективно) </p>

Как установить условие прерывания?

(Сделать неблокирование сокета необходимо. Без него программа останавливается на функцию read () навсегда. Я не знаю причину, но устранение проблемы с неблокированием сокета решило проблему.)

1 Ответ

0 голосов
/ 31 мая 2018

Ваш цикл tunnel() полностью игнорирует возвращаемые значения read() и write().Вместо этого попробуйте что-то вроде этого:

bool writeAll(int sckt, void *buffer, size_t buflen)
{
    char *pbuffer = (char*) buffer;
    while (buflen > 0)
    {
        ssize_t numSent = write(sckt, pbuffer, buflen);
        if (numSent < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
                continue;
            return false;
        }
        pbuffer += numSent;
        buflen -= numSent;
    }
    return true;
}

...

strcpy(buffer, "HTTP/1.0 200 Connection established\r\n\r\n");
if (writeAll(client, buffer, strlen(buffer)))
    tunnel(server, client, buffer);
close(client);
close(server);
return;

...

void tunnel(int server, int client, char *buffer)
{
    int maxFD = max(server, client) + 1;
    ssize_t numRead;
    FD_SET fd;

    int x = fcntl(server, F_GETFL, 0);
    fcntl(server, F_SETFL, x | O_NONBLOCK);

    x = fcntl(client, F_GETFL, 0);
    fcntl(client, F_SETFL, x | O_NONBLOCK);

    do
    {
        cout << "test" << endl;

        FD_ZERO(&fd);
        FD_SET(&fd, server);
        FD_SET(&fd, client);

        x = select(maxFD, &fd, NULL, NULL, NULL);
        if (x < 0) break;

        if (FD_ISSET(&fd, client))
        {
            numRead = read(client, buffer, BUFSIZE);
            if (numRead <= 0) break;

            if (!writeAll(server, buffer, numRead))
                break;
        }

        if (FD_ISSET(&fd, server))
        {
            numRead = read(server, buffer, BUFSIZE);
            if (numRead <= 0) break;

            if (!writeAll(client, buffer, numRead))
                break;
        }
    }
    while (true);
}

Что касается того, почему ваши чтения не всегда возвращают 0, когда вы ожидаете, это, скорее всего, из-за того, что клиент и сервер используют HTTP-keep-alive, чтобы сохранитьтуннельное соединение открывается после того, как сервер отправил свой ответ, поэтому клиент может отправлять последующие запросы на тот же сервер, используя то же TCP-соединение.Установление нового соединения TCP и даже нового сеанса HTTPS для каждого HTTP / S-запроса занимает много времени и тратит пропускную способность, поскольку включает в себя многократные обходы для квитирования TCP и TLS.Таким образом, стандартное поведение HTTP 1.1 и более поздних версий заключается в том, чтобы поддерживать соединение открытым, если ни одна из сторон явно не заявит, что закрытие требуется через заголовок Connection: close.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...