HTTP-сервер, использующий сокеты BSD, не закрывает соединение или не общается с браузером Windows

0

Я пытаюсь использовать простой HTTP-сервер, написанный на C, который использует UNIX BSD-сокеты. У меня это работает, но у меня есть следующие проблемы.

  1. Сервер печатает только "привет мир!". в браузерах Linux, а не в браузерах Windows; т.е. я могу использовать Chrome/Firefox в Linux и видеть текст в браузере, но не на Windows-машине в тех же браузерах.

  2. Вместо того, чтобы посылать "привет мир" со всеми заголовками ответов и закрывать соединение, как обычная веб-страница, я вижу, что когда я тестирую страницу сервера (localhost: xxxx), мне представляется текст и ответы HTTP в коде, но страница находится в непрерывной загрузке (вкладка продолжает вращаться). Затем, как только я выключу сервер, страница вернется "не удалось подключиться к серверу".

Мой вопрос, учитывая приведенный ниже код, почему это не отображает браузеры в Windows и почему он не закрывает соединение, оставив клиента с переданной информацией?

Это использует простые сокеты BSD.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>  //definitions for system variables
#include <sys/socket.h> //definitions / structures for sockets
#include <netinet/in.h> //const and structures used by internet domain

void error(const char *msg) //takes litteral msg passed in and prints, then exits. 
{
    perror(msg); //man perror
    exit(1);
}

int main(int argc, char *argv[])
{
     int sockfd,    // socket file descriptor 1. both return values from system 
         newsockfd, // socket file descriptor 2
         portno;    // port number to associate with this socket

     socklen_t clilen; //stored address size  of each client
     char      buffer[256]; //stores chars from socket buffer
     struct    sockaddr_in serv_addr, cli_addr;
     /*sockaddr_in, structure containing an internet address. most of the magic DEFINED BELOW
            struct sockaddr_in
            {
                short   sin_family; /* must be AF_INET 
                 u_short sin_port;
                 struct  in_addr sin_addr;
                 char    sin_zero[8]; /* Not used, must be zero 
            };
     */
     int n; //stores return values of read() / write() system calls)

     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     } //self explanitory

     /*socket()  creates a new socket and takes 3 arguments, the first being the address domain of the socket
      *      Sockets in this case can use two types of domains which are both system constants 
      *             AF_UNIX - Used for parent child prc on the same machine
      *             AF_INET - Used for host to host communication, Ie internet communication
      *
      *     The second option SOCKET_STREAM is the type of stream/communication used in the socket. ie TCP/UDP, Being
      *             SOCK_STREAM - TCP - A continuous stream/connection during the life span of the sockets communication
      *             SOCK_DGRAM  - UDP - A connectionless strea/connection which uses less resources/is faster but possible byte loss
      * 
      *     The third option is the protocol used, due to specifying the type of stream this is almost always left to 0
      *     Leaving the arg at 0 implies that the operating system will make the best call and due to setting the stream it will
      *     more then likely choose to use the requested method
      * 
      *     This socket() in general returns a value for the subsequent descriptor for reference. If on failure, the socket()
      *     will return -1 and fail hence if(sockfd<0){}
   * 
   *   man socket() for information Andrew,
      */
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) 
            error("ERROR opening socket");

     /*bzero() sets all values in buffer to 0(00000000)*/
     bzero(
        (char *) &serv_addr,
         sizeof(serv_addr)
          ); //Deref &serv_addr, and ensures to 0 out all bytes in buffer(entire struct)

     portno = atoi(argv[1]);//port number to be used for communication, 

     serv_addr.sin_family = AF_INET; //set to internet. sin_family is stored in sockadr_in structure
     serv_addr.sin_addr.s_addr = INADDR_ANY; //structure in structure. s_addr holds the host address. IN_ADDR is always the systems ip
     serv_addr.sin_port = htons(portno); //convert to network byte order. Holds th eport number, places into readable format
     /*
           * checks and binds to new file descripter, socketaddr(deref) and allocates the size of the address bound to. 
           * If less then zero. it will fail(-1)
         */
     if (bind(sockfd, 
             (struct sockaddr *) &serv_addr, 
              sizeof(serv_addr)) < 0) 
            error("ERROR on binding");

     listen(sockfd,5); // listens on bound socket/descriptor and allows up to 5 connections to be waiting in queue(max allowed on system 
     clilen = sizeof(cli_addr); // assigns value based on size of bytes in clients address
     newsockfd = accept(sockfd, // new descripter to use when accepting a client
                 (struct sockaddr *) &cli_addr, //gathers client connection information for arg(deref)
                 &clilen); // Passes in clilen, for lenght of client address. not a copy, the real shiz

     if (newsockfd < 0) 
          error("ERROR on accept"); // again, don't be a fucking dick.
     bzero(buffer,256); // ensure buffer is not tampered at this point or gross

     n = read(newsockfd,buffer,255); // use sys-call read() to store data in buffer from client. use 255bytes or client input
                                     // which ever is less.
     if (n < 0) error("ERROR reading from socket"); // if failed as fuck, let cry

     n = write(newsockfd,"HTTP/1.0 200 OK\n",16); // Write back to client. to bound socket from client. they see this shit.
     n = write(newsockfd,"Connection: Keep-alive\n",23);
     n = write(newsockfd,"Connection: close\n",18);
     n = write(newsockfd,"Content-type: text/html; charset=UTF-8\n",39);
     n = write(newsockfd,"\n",1);
     n = write(newsockfd,"<!DOCTYPE HTML>\n<html><head><title>Hello World!</title></head>\n",62);
     n = write(newsockfd,"<body><h1>Hello World!</h1>\n",28);
     n = write(newsockfd,"</body></html>\n",15);




     if (n < 0) error("ERROR writing to socket"); // duh

     // cleaning up...
       //close(newsockfd);
      // close(sockfd);
getchar();
     return 0; 
}
  • 0
    Терминатором строки в HTTP является \r\n , а не \n.
  • 0
    Спасибо за информацию. Тем не менее, это, похоже, не исправляет вышеупомянутый код.
Показать ещё 2 комментария
Теги:
sockets
http

1 ответ

4

В коде есть много ошибок. Основная проблема, которую вы, вероятно, имеете в том, что вы не сообщаете клиенту, когда достигнут конец тела ответа, так что клиент не знает, что тело сделано, и поэтому попытается прочитать данные навсегда и не отобразить, что это уже есть. Обычные способы - указать Content-Length, используя chunked передачей пакетов (только HTTP/1. 1+) или закрытие сокета после завершения действия тела. См. RFC 2616 Раздел 4.4 "Длина сообщения" для получения подробной информации.

Помимо этого (некоторые из вещей, о которых уже говорилось в комментариях к EJP):

  • неправильные окончания строк, например LF вместо CR LF
  • конфликтующая информация в заголовке Connection
  • вы неправильно читаете запрос, но просто предположите, что он будет в первом пакете и не должен превышать 255 байт
  • нет правильной проверки ошибок: write() не гарантирует запись всего буфера

Я получил коды ответов на основе запросов веб-сайта путем проверки информации заголовка

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

  • 0
    Я на самом деле не планирую использовать это в производстве каким-либо образом. Это просто кое-что, чтобы узнать больше о сокетах.
  • 1
    @andrew Использование по назначению не меняет требуемого соответствия. Вы спросили, почему это не работает. Вот почему это не работает. Вы не можете реализовать сетевые протоколы путем догадок. Если вам все равно, работает ли это, зачем задавать вопрос?
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню