Serveur HTTP utilisant des sockets BSD ne fermant pas la connexion ou communiquant avec le navigateur Windows

J’essaie d’utiliser un simple serveur HTTP écrit en C qui utilise des sockets UNIX BSD. Je l’ai un peu travaillé, mais j’ai les problèmes suivants.

  1. Le serveur imprime seulement “Bonjour tout le monde!” sur les navigateurs Linux et non sur les navigateurs Windows; C’est-à-dire que je peux utiliser Chrome / Firefox sous Linux et voir le texte dans le navigateur, mais pas sur une machine Windows dans les mêmes navigateurs.

  2. Plutôt que d’envoyer “hello world” avec tous les en-têtes de réponse et de fermer la connexion comme une page Web normale, ce que je vois, c’est que lorsque je teste la page du serveur (localhost: xxxx) dans le code mais la page est dans une charge continue (onglet continue de tourner). Puis, au moment où je désactive le serveur, la page “échoue à se connecter au serveur”.

Ma question est, étant donné le code ci-dessous, pourquoi cela ne s’affiche-t-il pas sur les navigateurs sous Windows et pourquoi ne ferme-t-il pas la connexion tout en laissant au client les informations envoyées?

Cela utilise des sockets BSD simples.

#include  #include  #include  #include  #include  //definitions for system variables #include  //definitions / structures for sockets #include  //const's 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's 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's 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,"\nHello World!\n",62); n = write(newsockfd,"

Hello World!

\n",28); n = write(newsockfd,"\n",15); if (n < 0) error("ERROR writing to socket"); // duh // cleaning up... //close(newsockfd); // close(sockfd); getchar(); return 0; }

Il y a beaucoup de problèmes avec votre code. Le problème principal que vous avez probablement est que vous ne dites pas au client quand la fin du corps de la réponse est atteinte, de sorte que le client ne sait pas que le corps est fait et va donc essayer de lire plus de données pour toujours et ne pas afficher a déjà. Les méthodes habituelles consistent à spécifier un Content-Length , à utiliser chunked codage de transfert en chunked (HTTP / 1.1 + uniquement) ou à fermer le socket une fois le corps terminé. Voir RFC 2616 Section 4.4 “Longueur du message” pour plus de détails.

En dehors de cela (certaines choses sont déjà signalées par EJP dans les commentaires):

  • des fins de ligne incorrectes, par exemple LF au lieu de CR LF
  • informations contradictoires dans l’en-tête de Connection
  • vous ne lisez pas correctement la demande, mais supposez simplement qu’elle sera dans le premier paquet et pas plus de 255 octets
  • pas de vérification d’erreur correcte: write() n’est pas garanti pour écrire le tampon entier

J’ai obtenu les codes de réponse basés sur les demandes de sites Web en inspectant les informations d’en-tête

S’il vous plaît ne faites pas une mise en œuvre en ne regardant que quelques échantillons de trafic sans même les comprendre correctement. Il y a déjà suffisamment de serveurs HTTP cassés et suffisamment de clients qui travaillent autour de ces serveurs et tout cela pose beaucoup de problèmes. Habituellement, ces serveurs ne sont testés que contre quelques clients sélectionnés, puis le problème commence une fois que ce type de serveurs est déployé et que, par conséquent, il est surprenant de rompre avec d’autres clients. Veuillez lire la spécification HTTP actuelle et écrivez votre serveur pour vous y conformer.