Webserver Anfrage, Antwort zurückgeben

Semox

Bekanntes Mitglied
Liebe Java Forum User

Ich löse gerade die folgende Hausaufgabe, in der ich von Grund auf einen Webserver bauen, der ein einfaches Suchfeld in einem Client bedienen soll, um einen in einer Datei zu findenden Eintrag ggf. an den Browser zurückzuliefern.

Ich hänge am Schluß mit der Rückgabe in den Browser, wo die Matches im Browser angezeigt werden sollen. Ich weiß nicht recht wie man das macht, da das Socket ja sofort nach der ersten Bearbeitung der Browseranfrage (dem initialen Laden der Webseite) wieder geschlossen wird. Ich poste hier meinen Code für bessere Nachvollziehbarkeit.

Wie kann ich dem Browser das Ergebnis liefern?

Ich würde mich freuen, wenn Ihr mir schreiben würdet wie das geht.

Java:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WebServer extends Thread {

	public static final String VERSION = "Ultra-Light WebServer 0.2";

	@SuppressWarnings("unused")
	private File startVerz;
	private ServerSocket serverSocket;
	private Socket clientSocket;
	private boolean running = true;
	private BufferedReader br;

	final String ae = "%E4";
	final String Ae = "%C4";
	final String oe = "%F6";
	final String Oe = "%D6";
	final String ue = "%FC";
	final String Ue = "%DC";
	final String sz = "%DF";

	/**
	 * Dieser Konstruktor erzeugt ein ServerSocket und prüft, ob auch ein
	 * Verzeichnis in dem die Startseite liegen soll übergeben wurde.
	 * Theoretisch wird &uuml;ber die Angabe des Parameters <i>File
	 * startVerz</i> die M&ouml;glichkeit bereitgehalten, da&szlig; sp&auml;ter
	 * auch eine direkte Verbindung zu einem Verzeichnis aufgebaut werden kann,
	 * um z.B. Dateien darin abzulegen oder aufzurufen.
	 * 
	 * @param startVerz
	 *            Es handelt sich dabei um das Verzeichnis, in dem der Benutzer
	 *            die <i>index.html</i> Datei abgelegt hat.
	 * @param port
	 *            In diesem Fall ist es ein fest vergebener Port 19000
	 * @throws IOException
	 *             wird in diesem Fall nicht gefangen.
	 */
	public WebServer(File startVerz, int port) throws IOException {
		startVerz = startVerz.getCanonicalFile();
		if (!startVerz.isDirectory()) {
			throw new IOException("Kein Verzeichnis erreichbar.");
		}

		/*
		 * Mindestanforderung fuer Serverobjekt
		 */
		serverSocket = new ServerSocket(port);
		start();
	}

	/**
	 * Diese Methode wird allein dazu genutzt, um den Webserver in einer
	 * unendlichen Schleife am Leben zu halten. Dazu wird das das im Konstruktor
	 * erzeugte Socket Objekt abgerufen. Au&szlig;erdem werden Umlaute ersetzt
	 * und &uuml;bergebene Parameter an eine weitere Methode weitergeleitet, die
	 * den Suchstring ausfiltert.
	 * 
	 * Hier wird auch das Dokument f&uuml;r die Browseranzeige generiert.
	 * 
	 * @see java.lang.Thread#run()
	 */
	public void run() {
		while (running) {
			try {
				Socket socks = serverSocket.accept();

				clientSocket = new Socket();
				clientSocket = socks;

				br = new BufferedReader(new InputStreamReader(
						clientSocket.getInputStream()));
				String liesMich = br.readLine();

				liesMich.replaceAll(ae, "ä");
				liesMich.replaceAll(Ae, "Ä");
				liesMich.replaceAll(oe, "ö");
				liesMich.replaceAll(Oe, "Ö");
				liesMich.replaceAll(Ue, "Ü");
				liesMich.replaceAll(ue, "ü");
				liesMich.replaceAll(sz, "ß");

				getPars(liesMich);
				System.out.println(liesMich);

				PrintWriter pW = new PrintWriter(clientSocket.getOutputStream());
				pW.println("HTTP/1.1 200 OK");
				pW.println("Connection: closed.");
				pW.println("Connection-Type: text/html");
				pW.println("\n");

				String line = null;

				BufferedReader br_Text = new BufferedReader(
						new InputStreamReader(new FileInputStream("index.html")));
				while ((line = br_Text.readLine()) != null) {
					pW.println(line);
				}
				pW.flush();
				clientSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Es werden die Parameter aus dem Suchformular ausgefiltert und mit der
	 * Teleliste.txt Datei abgeglichen, ob ein Treffer gelandet wird.
	 * 
	 * @param browserQuery
	 *            enth&auml;lt den GET String der vom Browser versendet wird.
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	private void getPars(String browserQuery) {
		String findMe = "";
		boolean search = false;
		@SuppressWarnings("rawtypes")
		ArrayList matched = new ArrayList();

		browserQuery.toLowerCase();
		browserQuery = browserQuery.substring(browserQuery.indexOf('?') + 1,
				browserQuery.length());

		// Nur Suche starten, wenn etwas im Suchfeld eingegeben wurde
		if (browserQuery.length() > 25) {
			search = true;
		}

		// Filtern des Suchstrings
		if (search) {
			Pattern namePattern = Pattern
					.compile("searchstring=[a-z%A-Z%0-9]+");
			Matcher nameMatcher = namePattern.matcher(browserQuery);

			if (nameMatcher.find()) {
				findMe = browserQuery.substring(13, nameMatcher.end());
			}

			// Datei mit den Telefonbuchdaten laden und zeilenweise vergleichen
			try {
				BufferedReader in = new BufferedReader(new FileReader(
						"/home/semo/workspace/Webserver/bin/telebuch.txt"));
				String zeile = null;

				while ((zeile = in.readLine()) != null) {
					if (zeile.contains(findMe)) {
						matched.add(zeile);
						// System.out.println(zeile);
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}

			for (int i = 0; i < matched.size(); i++) {
				System.out.println(matched.get(i));
			}

		}
	}

	@SuppressWarnings("unused")
	public static void main(String[] args) {
		try {
			WebServer server = new WebServer(new File("./"), 19000);
		} catch (IOException e) {
			System.err.println(e);
		}
	}
}

Viele Grüße,
Semo
 

XHelp

Top Contributor
Wie auch immer deine html seite aussieht, musst du ja die Formulardaten übergeben. Nach dem Aufbau vermute ich mal, dass es per GET erfolgen soll. Da wird ja auch eine Seite zurückgegeben. In dieser kannst du dann das Ergebnis einbauen.
 
S

SlaterB

Gast
was funktioniert denn genau nicht, ist nur das sofortige close() ein Problem weil dann der Empfang im Browser nicht klappt?
dann lasse doch das close() weg, der Browser wird das vielleicht machen,
oder warte 5 sec mit Thread.sleep(5000)

schon im Internet nach Beispielen gesucht?
 

Semox

Bekanntes Mitglied
@XHelp

Korrekt. Anfrage erfolgt per GET.

@SlaterB
Ich weiß einfach nicht so recht, wie ich da heran gehen soll. Ich verstehe nicht so ganz wie der Browser das GET genau verarbeitet.

Nachvollziehbar ist einen Port anzubieten, auf dem mein Browser die Seite erstmals anfordert (die dann geschlossen wird) --> warum antwortet mein Browser dem Server dann mit einer willkürlichen anderen Portnummer? Wozu braucht es zwei Ports? Das HTML ist doch zustandslos... Die Verbindung wird geschlossen, sobald die Anfrage bearbeitet wurde... Wie kann dann noch ein GET existieren...

Werde noch nicht so schlau aus der ganzen Client/Server Sache... Daher ist es schwierig die "richtig Frage" zu stellen. Viel einfacher ist es wahrscheinlich die Antwort zu geben... :-D

Vielleicht denke ich zu kompliziert.

Vielleicht fange ich ganz konkret mit diese Frage an... Wo genau sollte ich vernünftigerweise mein clientSocket.close() plazieren?

Viele Grüße,
Semo
 

XHelp

Top Contributor
Das mit dem close ist nicht dein Problem.
Zeig mal deine html datei, da kann man genauer erklären was du zu tun hast.
Beim GET wird ja ganz normal die Seite abgefragt, d.h. dein Server soll entscheiden, ob er jetzt die Seite mit dem Formular schicken soll oder ob er die Ergebnisse ausgeben soll (weil searchstring übergeben worden ist). Aber irgendwie scheinst du diesbezüglich keine Abfrage zu haben.
 

Semox

Bekanntes Mitglied
Ok. Das mit dem close ist tatsächlich auch mein Gefühl... :)

Das wiedergeben des Ergebnisses ist mein Problem... Hier ist der HTM Code:

HTML:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html> 
<head> 
<title>Telefonbuchanwendung</title> 
</head> 
 
<body> 
 
<h1>Such die Person!</h1> 
 
<p>Voraussetzung ist eine Verbindung ins Internet/LAN.
Es werden keine Daten gespeichert, der verarbeitende Server
gibt lediglich die eingelesenen Daten aus.</p> 
 
<form action="http://localhost:19000/index.html"> 
  <table border="0" cellpadding="1" cellspacing="0" bgcolor="#E0E0E0"> 
    <tr> 
      <td align="right">Sucheingabe:</td> 
      <td><input name="searchstring" type="text" size="30" maxlength="100"></td> 
    </tr> 
    <tr> 
        <td></td> 
      <td> 
        <input type="submit" value=" Absenden "> 
        <input type="reset" value=" Abbrechen"> 
      </td> 
    </tr> 
  </table> 
</form> 
 
</body> 
</html>

Dankeschön,
Semo
 

XHelp

Top Contributor
beim ersten laden würde der Browser folgendes schicken (verkürzt):
Code:
GET /index.html HTTP/1.1
...
bzw.
GET / HTTP/1.1
...
Wenn du das Formular abschickst, kommt folgendes an:
Code:
GET /index.html?searchstring=blahiertext HTTP/1.1
...
Diese Fälle musst du dann auseinander halten und je nach die Ausgabe anpassen.
Optional kannst du auch das Formular nicht an index.html schicken, sondern an bla.html
 

Semox

Bekanntes Mitglied
Viiiiielen Dank für Deine Zeit.

Ich probiere beide Möglichkeiten morgen (… ähm…heute früh… wie auch immer) aus... Morgen früh fahre ich in die Uni und werde mir noch mal den RFC 2616 zu HTML reinziehen... Schaden wird's wohl nicht...

Gute Nacht,
Semo
 

Semox

Bekanntes Mitglied
Diese Fälle musst du dann auseinander halten und je nach die Ausgabe anpassen.
Optional kannst du auch das Formular nicht an index.html schicken, sondern an bla.html

Hi XHelp

Danke für die Hilfe. Das Problem konnte ich so lösen. Ich habe die Ausgabe der Antwort auf eine separate "bla".html umgeleitet. Zudem waren noch einige Anpassungen an dem Regex nötig der bestimmte Eingaben und Suchanfragen erlaubt, bzw. ignoriert.

Viele Grüße,
Semo
 

Neue Themen


Oben