web-dev-qa-db-fra.com

Un serveur HTTP simple avec Java/Socket?

Je crée actuellement un petit serveur HTTP qui renvoie une page statique <p>Hello!</p>... J'ai essayé avec des sockets avec Java:

  public static void main(String[] args) throws Exception {

        // création de la socket
        int port = 1989;
        ServerSocket serverSocket = new ServerSocket(port);
        System.err.println("Serveur lancé sur le port : " + port);

        // repeatedly wait for connections, and process
        while (true) {

            // on reste bloqué sur l'attente d'une demande client
            Socket clientSocket = serverSocket.accept();
            System.err.println("Nouveau client connecté");

            // on ouvre un flux de converation

           BufferedReader in = new BufferedReader(
                           new InputStreamReader(clientSocket.getInputStream())
                          ); 
            PrintWriter out = new PrintWriter(
                         new BufferedWriter(
                            new OutputStreamWriter(clientSocket.getOutputStream())), 
                         true);   

            // chaque fois qu'une donnée est lue sur le réseau on la renvoi sur le flux d'écriture.
            // la donnée lue est donc retournée exactement au même client.
            String s;
            while ((s = in.readLine()) != null) {
                System.out.println(s);


       out.write("HTTP/1.0 200 OK\r\n");
       out.write("Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n");
       out.write("Server: Apache/0.8.4\r\n");
       out.write("Content-Type: text/html\r\n");
       out.write("Content-Length: 59\r\n");
       out.write("Expires: Sat, 01 Jan 2000 00:59:59 GMT\r\n");
       out.write("Last-modified: Fri, 09 Aug 1996 14:21:40 GMT\r\n");
       out.write("\r\n");
       out.write("<TITLE>Exemple</TITLE>");
       out.write("<P>Ceci est une page d'exemple.</P>");
  }

            // on ferme les flux.
            System.err.println("Connexion avec le client terminée");
            out.close();
            in.close();
            clientSocket.close();
        }
    }

ce code ne contient aucune erreur et j'ai reçu une réponse du navigateur comme ceci:

GET / HTTP/1.1
Host: localhost:1989
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46 Safari/536.5 Comodo_Dragon/19.0.3.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

Mais mon problème est que je ne reçois aucune page sur le navigateur? Toute aide s'il vous plaît?

PS: J'ai déjà lu cet article: http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol , (désolé pour la langue française ...)

23
Marwen Trabelsi

En plus du\r\n après chaque ligne d’en-tête de requête, vous devez envoyer une ligne vide après l’en-tête. Exemple:

out.write("HTTP/1.0 200 OK\r\n");
// Header...
out.write("Last-modified: Fri, 09 Aug 1996 14:21:40 GMT\r\n");
out.write("\r\n"); // The content starts afters this empty line
out.write("<TITLE>Hello!</TITLE>");
// Content...

J'ai corrigé votre code afin qu'il fonctionne (mais ce n'est toujours pas parfait, vous devez gérer chaque requête dans un thread séparé, par exemple avec Java.util.concurrent.ThreadPoolExecutor):

public static void main(String[] args) throws Exception {
    // création de la socket
    int port = 1989;
    ServerSocket serverSocket = new ServerSocket(port);
    System.err.println("Serveur lancé sur le port : " + port);

    // repeatedly wait for connections, and process
    while (true) {
        // on reste bloqué sur l'attente d'une demande client
        Socket clientSocket = serverSocket.accept();
        System.err.println("Nouveau client connecté");

        // on ouvre un flux de converation

        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

        // chaque fois qu'une donnée est lue sur le réseau on la renvoi sur
        // le flux d'écriture.
        // la donnée lue est donc retournée exactement au même client.
        String s;
        while ((s = in.readLine()) != null) {
            System.out.println(s);
            if (s.isEmpty()) {
                break;
            }
        }

        out.write("HTTP/1.0 200 OK\r\n");
        out.write("Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n");
        out.write("Server: Apache/0.8.4\r\n");
        out.write("Content-Type: text/html\r\n");
        out.write("Content-Length: 59\r\n");
        out.write("Expires: Sat, 01 Jan 2000 00:59:59 GMT\r\n");
        out.write("Last-modified: Fri, 09 Aug 1996 14:21:40 GMT\r\n");
        out.write("\r\n");
        out.write("<TITLE>Exemple</TITLE>");
        out.write("<P>Ceci est une page d'exemple.</P>");

        // on ferme les flux.
        System.err.println("Connexion avec le client terminée");
        out.close();
        in.close();
        clientSocket.close();
    }
}
29
Joel Richard

C’est une réponse à votre dernière question seulement et la raison pour laquelle rien n’est visible dans le navigateur est que vous avez mal calculé le nombre de caractères.

Il devrait être 57 au lieu de 59.

Mieux encore, le nombre de caractères est calculé automatiquement, mais je pense que votre échantillon n’est qu’un échantillon.

4
javabeangrinder

Quelle machine utilisez-vous? Quel OS? Si vous utilisez un ordinateur UNIX, alors println ne fonctionnera pas car il n'enverra qu'un caractère LF. HTTP nécessite CR et LF pour ses en-têtes. Essayez d'ajouter\r à la fin de vos chaînes et voyez si cela fonctionne.

Oh, aussi, votre:

  out.println("HTTP/1.0 200 OK"+
"Date: Fri, 31 Dec 1999 23:59:59 GMT"+
"Server: Apache/0.8.4"+
"Content-Type: text/html"+
"Content-Length: 59"+
"Expires: Sat, 01 Jan 2000 00:59:59 GMT"+
"Last-modified: Fri, 09 Aug 1996 14:21:40 GMT"+

Il imprime une seule longue chaîne.

Modifiez-les en println pour chaque chaîne ou ajoutez\r\n à la chaîne.

1
Will Hartung

Vous avez besoin des séparateurs de ligne corrects (\r\n) entre chaque sortie de ligne. Il ne suffit pas de les concaténer - ce que vous pouvez voir si vous imprimez la réponse.

Juste un rappel: ceci est pas un serveur HTTP, comme le titre le suggère. C'est un socket qui écrit une réponse HTTP spécifique codée en dur (en supposant qu'elle soit corrigée en fonction des suggestions formulées dans les autres réponses). Même si vous avez modifié le contenu et les en-têtes de longueur de contenu renvoyés de manière dynamique, cela ne suffit pas pour être compatible avec le protocole HTTP.

Comme j’ai appris à la dure en écrivant JLHTTP , HTTP est bien plus que cela. Ce n'est pas que c'est très compliqué, mais juste qu'il y a beaucoup de détails supplémentaires et d'exigences à traiter correctement. Vous pouvez lire les RFC (le protocole de base est défini dans la RFC 7230 ou l'ancienne RFC 2616) pour en savoir plus sur ce que cela implique.

Je peux également proposer le code source JLHTTP comme référence pour une implémentation minimale et conforme d'un serveur HTTP bien documentée - il s'agit d'un fichier, contenant actuellement environ 3 000 lignes, dont presque la moitié est de la documentation. Son but est d'être petit, mais conforme. Je pense que regarder le code peut être utile pour tous ceux qui veulent savoir ce qu’un serveur HTTP doit faire. Comme je l'ai dit - pas très compliqué, mais avec beaucoup de petits détails.

En réalité, pour être exact, JLHTTP n'est pas aussi minime que possible. Il inclut quelques fonctionnalités supplémentaires utiles, telles que la gestion de données de formulaire en plusieurs parties pour le téléchargement de fichiers ou la prise en charge de HTTPS, qui ne sont pas requises par le protocole HTTP lui-même. Mais vous pouvez facilement extraire ces parties (ou simplement les ignorer) et parvenir à une implémentation vraiment minimale du serveur HTTP Java.

0
amichair