Обслуживание HTML с использованием C++ – ошибка загрузки страницы

Я пытался передать файл index.html из текущего каталога (в целях тестирования я изменил его на "Hello"). Я вижу страницу на долю секунды, а потом она просто выдает мне это:

Соединение было сброшено

Соединение с сервером было сброшено во время загрузки страницы.

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

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

Вот server.cpp:

#include <tchar.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <string>

void handleClient(SOCKET acceptSocket) {
    std::string response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello";

    int bytesSent = send(acceptSocket, response.c_str(), response.size(), 0);
    if (bytesSent == SOCKET_ERROR) {
        std::cerr << "Error sending data to client: " << WSAGetLastError() << std::endl;
    } else {
        std::cout << "Served 'Hello' to client." << std::endl;
    }

    closesocket(acceptSocket);
}

int main() {
    SOCKET serverSocket, acceptSocket;
    const int PORT = 55555;
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 2);
    int wsaerr = WSAStartup(wVersionRequested, &wsaData);

    if (wsaerr != 0) {
        std::cerr << "Winsock dll not found." << std::endl;
        return 0;
    } else {
        std::cout << "Winsock dll was found!" << std::endl;
        std::cout << "Status: " << wsaData.szSystemStatus << std::endl;
    }

    serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (serverSocket == INVALID_SOCKET) {
        std::cerr << "Error at socket()." << std::endl;
        WSACleanup();
        return 0;
    } else {
        std::cout << "Socket() is OK!" << std::endl;
    }

    sockaddr_in service;
    service.sin_family = AF_INET;
    InetPton(AF_INET, _T("127.0.0.1"), &service.sin_addr.s_addr);
    service.sin_port = htons(PORT);

    if (bind(serverSocket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR) {
        std::cerr << "bind() failed." << std::endl;
        std::cerr << WSAGetLastError() << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return 0;
    } else {
        std::cout << "bind() is OK!" << std::endl;
    }

    if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
        std::cerr << "listen() error listening on socket." << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return 0;
    } else {
        std::cout << "listen() is OK!, waiting for connections.." << std::endl;
    }

    while (true) {
        acceptSocket = accept(serverSocket, NULL, NULL);
        if (acceptSocket == INVALID_SOCKET) {
            std::cerr << "accept failed." << std::endl;
            std::cerr << WSAGetLastError() << std::endl;
            closesocket(serverSocket);
            WSACleanup();
            return -1;
        }
        std::cout << "Client connected.." << std::endl;

        handleClient(acceptSocket);
    }

    closesocket(serverSocket);
    WSACleanup();

    return 0;
}

Я точно не ожидал этой ошибки после просмотра страницы на долю секунды.

Кроме того, я пробовал без closesocket, я пробовал без цикла while (только один клиент), я пробовал использовать потоки, ни один из них не работал

Pastori 14.07.2024 23:52

Для HTTP/1.1 вам необходимо отправить заголовок Connection: close, если вы хотите закрыть соединение.

Some programmer dude 15.07.2024 00:04

Кроме того, проверили ли вы, что происходит на сервере, когда вы получаете эту ошибку на клиенте?

Some programmer dude 15.07.2024 00:05

Примечание. Вы не можете сказать «Отправлено «Привет» клиенту». если bytesSent != SOCKET_ERROR. Возвращаемое значение send — это SOCKET_ERROR или количество байтов, записанных в буфер передачи. Последнее может быть, а может и не быть всем сообщением, которое вы намеревались отправить, если буфер передачи заполнится. Хуже того, данные в буфере отправки могут никогда не быть успешно отправлены по множеству причин, о которых send невозможно вам рассказать, поскольку работа send заканчивается, когда данные ставятся в очередь для отправки. Очень привередливая штука — сетевое программирование.

user4581301 15.07.2024 00:05

Серверу необходимо сообщить клиенту, как он должен узнать, что ответ полностью получен. Наиболее распространенными являются заголовок Content-Length, если сервер заранее знает размер, или Transfer-Encoding: chunked, если сервер не знает. Для совместимости с HTTP/1.0 сервер может указать заголовок Connection: close, а затем просто закрыть соединение в конце ответа; это не рекомендуется, поскольку в этом случае клиент не сможет определить, пришел ли ответ полностью или соединение прервалось на полпути, и может ошибочно интерпретировать неполный ответ как успешный.

Igor Tandetnik 15.07.2024 00:23

Спасибо, ребята, за ответы, проблема заключалась в отсутствующих заголовках длины контента и кодировки передачи, и я проверю «bytesSent» в окончательной версии, я просто не включил его во время тестирования, спасибо всем ответам 👍

Pastori 15.07.2024 14:57
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
76
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в том, что вы не сообщаете клиенту (браузеру), когда ответ будет завершен. Вы ДОЛЖНЫ отправить либо:

  • заголовок Content-Length, указывающий фактическое количество отправляемых байтов.

  • заголовок Transfer-Encoding: chucked, за которым следуют фрагменты данных, завершающиеся фрагментом длиной 0.

Кроме того, поскольку вы закрываете сокет после отправки ответа, вам следует отправить заголовок Connection: close, чтобы клиент знал, что нужно закрыть свой конец соединения после завершения ответа.

Кроме того, send() не гарантированно отправит столько байтов, сколько вы запрашиваете, он может отправить меньше, поэтому вам нужно вызывать send() в цикле, пока все байты не будут фактически отправлены.

Попробуйте еще что-нибудь вроде этого:

void handleClient(SOCKET acceptSocket) {
    std::string response = "HTTP/1.1 200 OK\r\n"
                           "Content-Type: text/plain\r\n"
                           "Content-Length: 5\r\n" // <-- ADD THIS!
                           "Connection: close\r\n" // <-- ADD THIS!
                           "\r\n"
                           "Hello";

    const char *data = response.c_str();
    size_t size = response.size();
    while (size > 0) {
        int bytesSent = send(acceptSocket, data, size, 0);
        if (bytesSent == SOCKET_ERROR) {
            int err = WSAGetLastError();
            std::cerr << "Error sending data to client: " << err << std::endl;
            break;
        }
        data += bytesSent;
        size -= bytesSent;
    }

    if (size == 0)
        std::cout << "Served 'Hello' to client." << std::endl;

    closesocket(acceptSocket);
}

Прочтите спецификацию протокола HTTP 1.1, RFC 2616 и его преемников RFC 7230 , 7231 , 7232 , 7233 , 7234 и 7235. Обратите особое внимание на RFC 2616, раздел 4.4: Длина сообщения и RFC 7230, раздел 3.3.3: Длина тела сообщения.

Отлично, да, сработало, спасибо

Pastori 15.07.2024 14:54

Другие вопросы по теме