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

avatar
Mike Croteau
9 августа 2021 в 02:52
101
1
1

Пожалуйста, помогите, я пытаюсь написать базовый http-сервер на C. Мне удалось получить результат, но результат не тот, к которому я стремился. Браузер отображает только один символ, и я не могу понять, почему. Ниже приведен код моего сервера на данный момент. Все, что вы можете сделать, чтобы помочь, будет высоко оценено. Ты лучший!

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 8080

int clients[1000];

int handle(int socket, int fd, struct sockaddr *pSockaddr, socklen_t *pInt, char *output);

int main(int argc, char const *argv[])
{
    int server_fd, new_socket, valread;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    char *hello = "Hello from server";

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
    {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR,
                   &opt, sizeof(opt)))
    {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons( PORT );

    if (bind(server_fd, (struct sockaddr *)&address,
            sizeof(address))<0)
    {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    char* httpVersion = "HTTP/1.1 200 OK\r\n";
    char* contentLength = "Content-Length: 1\r\n";
    char* server = "Server: n\r\n";
    char output[30000];
    char* contentType = "Content-Type: text/html";
    char* newLine = "\r\n";
    strncat(output, httpVersion, strlen(httpVersion));
    strncat(output, contentLength, strlen(contentLength));
    strncat(output, server, strlen(server));
    strncat(output, contentType, strlen(contentType));
    strncat(output, newLine, strlen(newLine));
    strncat(output, newLine, strlen(newLine));
    strncat(output, "abc", strlen("abc"));
    strncat(output, newLine, strlen(newLine));
    strncat(output, newLine, strlen(newLine));

    while(1){

        if (listen(server_fd, 3) < 0)
        {
            perror("listen");
            exit(EXIT_FAILURE);
        }

        struct sockaddr *sa = (struct sockaddr *)&address;
        socklen_t *sl = (socklen_t*)&addrlen;
        handle(new_socket, server_fd, sa, sl, output);
    }
}


int handle(int new_socket, int server_fd, struct sockaddr *sa, socklen_t *sl, char *output) {

    if ((new_socket = accept(server_fd, sa, sl))<0)
    {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    write(new_socket, output, strlen(output));

    return 0;
}

Источник
paddy
9 августа 2021 в 03:02
2

output не инициализирован. Попробуйте выполнить output[0] = '\0'; перед всеми вашими вызовами strncat.

Martin James
9 августа 2021 в 03:37
1

Вам нужно только один раз прослушать(). Это accept(), который необходимо зациклить для обработки нескольких подключений. Кроме того, вы ДОЛЖНЫ правильно и полностью обрабатывать результаты, возвращаемые системными вызовами, такими как write(). 'int clientfd = клиенты [новый_сокет];' клиенты не инициализированы, поэтому UB.

Mike Croteau
9 августа 2021 в 03:52
0

@MartinJames Я определил клиентов как переменную выше, что вы имеете в виду?

Mike Croteau
9 августа 2021 в 04:05
0

@MartinJames спасибо за лакомый кусочек, я думал, что там что-то не так.

Ответы (1)

avatar
Mike Croteau
9 августа 2021 в 04:48
0

Ниже приведен рабочий пример для порта 3000.

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>

#define PORT 3000

int clients[1000];
wchar_t output[3000];

int handle(int socket, int fd, struct sockaddr *pSockaddr, socklen_t *pInt, char *output);

void tostring(char *size, int length);

int main(int argc, char const *argv[])
{

    int server_fd, new_socket, valread;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
    {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR,
                   &opt, sizeof(opt)))
    {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons( PORT );

    if (bind(server_fd, (struct sockaddr *)&address,
            sizeof(address))<0)
    {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    char* content = "abc";
    char* httpVersion = "HTTP/1.1 200 OK\r\n";
    char* contentLengthTitle = "Content-Length: ";
    int length = strlen(content);

    char *contentLengthSize[3000];
    tostring(contentLengthSize, length);
    char* newLine = "\r\n";
    char contentLength[3000];
    strncat(contentLength, contentLengthTitle, strlen(contentLengthTitle));
    strncat(contentLength, contentLengthSize, strlen(contentLengthSize));
    strncat(contentLength, newLine, strlen(newLine));

    char* server = "Server: n\r\n";
    char* contentType = "Content-Type: text/html; charset=UTF-8";
    strncat(output, httpVersion, strlen(httpVersion));
    strncat(output, contentLength, strlen(contentLength));
    strncat(output, server, strlen(server));
    strncat(output, contentType, strlen(contentType));
    strncat(output, newLine, strlen(newLine));
    strncat(output, newLine, strlen(newLine));
    strncat(output, content, strlen(content));
    strncat(output, newLine, strlen(newLine));
    strncat(output, newLine, strlen(newLine));

    if (listen(server_fd, 3) < 0)
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    while(1){
        struct sockaddr *sa = (struct sockaddr *)&address;
        socklen_t *sl = (socklen_t*)&addrlen;
        handle(new_socket, server_fd, sa, sl, output);
    }
}



int handle(int new_socket, int server_fd, struct sockaddr *sa, socklen_t *sl, char *output) {

    if ((new_socket = accept(server_fd, sa, sl))<0)
    {
        perror("accept");
        exit(EXIT_FAILURE);
    }

//    char buffer[1024] = {0};
//    read(new_socket , buffer, 1024);
    write(new_socket, output, strlen(output));
    return 0;
}
void tostring(char *str, int num) {

    int i, rem, len = 0, n;

    n = num;
    while (n != 0)
    {
        len++;
        n /= 10;
    }
    for (i = 0; i < len; i++)
    {
        rem = num % 10;
        num = num / 10;
        str[len - (i + 1)] = rem + '0';
    }
    str[len] = '\0';
}


void tostring(char str[], int num);
Yunnosch
9 августа 2021 в 05:44
0

То есть вы написали функциональный минимальный сервер только для того, чтобы ответить на этот вопрос, а потом не тратили усилий на объяснения? Или ты это откуда-то скопировал?

Yunnosch
9 августа 2021 в 05:47
0

Есть ли особая причина, по которой вы снова предоставляете прототип void tostring(char str[], int num); и ПОСЛЕ определения этой функции, но не совсем идентично?

Mike Croteau
9 августа 2021 в 06:59
0

@Yunnosch Нет, я повозился. Я начал с geeksforgeeks.org/socket-programming-cc. Если вы не чувствуете, что это ценно, я уберу это. В Интернете нет статей о том, как обслуживать ответ браузера.