# Mit Server kommunizieren, InputStream



## Billie (16. Juni 2004)

Helas!

Ich arbeite gerade an einem kleinen Programm, mit welchem man mit einem beliebigen Server auf einem beliebigen Port kommunizieren kann. Also, es wird eine einfache ClientSocket Verbindung aufgebaut (new Socket(host, port)) und der Out- und InputStream besorgt.

Das Problem ist, Daten an den Server zu verschicken ist einfach. Dabei wird auf klick eines Buttons das ActionEvent ausgelöst und der Text aus einer eigenen TextArea an den Server geschickt.

Das Problem ist, wenn der Server antwortet/sendet soll der Text ebenfalls in einer TextArea angezeigt werden. Die Frage ist nun wie Frage ich am Besten ab, ob der Server wieder neue Daten hat? Im Moment habe ich dafür einen eigenen Thread laufen, dieser arbeitet aber nicht korrekt.

Also, kann mir jemand sagen wie ich den InputStream des Servers zur Laufzeit in einer TextArea anzeige?

Hier der Code des derzeitigen Threads:

```
class OutputThread extends Thread {

    boolean stop;
    InputStream in;
    JTextArea textArea;

    OutputThread(InputStream in, JTextArea textArea) {
        super();
        this.in = in;
        this.textArea = textArea;
        stop = false;
    }

    public void run() {
        int len;
        byte[] b = new byte[100];

        while(!stop) {
            try {
                if((len = in.read(b)) == -1)
                    break;

                textArea.append(new String(b, 0, len));
            } catch(IOException e) {

            }
            this.yield();
        }
    }

    public void requestStop(boolean stop) {
        this.stop = stop;
    }

    public boolean getStop() {
        return stop;
    }

    public void setInputStream(InputStream in) {
        this.in = in;
    }
}
```


----------



## Thomas Darimont (16. Juni 2004)

Hallo!

Verlagere das "lauschen nach Antwort" einfach in einen eigenen Listener-Thread...

dort kannst du dann eine blockierende read/readLine(BufferedReader) Methode deines InputStreams aufrufen welche dann so lang blockiert, bis sie etwas empfängt... dann kannst du mit den empfangenen Daten tun was du willst, wie etwa sie in einer JTextArea anzeigen lassen. Denke aber dran, dass du Veränderungen an der GUI ausserhalb vom GUI Thread nur durch den Einsatz von:


```
SwingUtilities.invokeLater(Runnable runnable(){
public void run(){

....
//Code der die GUI manipuliert...
.....

}
});
```
durchführen kannst ...

Gruß Tom


----------



## Billie (16. Juni 2004)

Also, du würdest also weiter beim Thread Ansatz bleiben und einen eigenen Thread laufen lassen, der überprüft ob es etwas neues gibt.

Aber eben genau mit diesem Thread gibt es Probleme, ich habe glaub ich zuwenig wissen um wirklich zu wissen wie das funktioniert. Wie dem auch sei, ich habe hier einen ganz einfachen Thread programmiert:


```
public void run() {

        int len;
        byte[] b = new byte[100];

        while(true) {
            try {
                if((len = in.read(b)) == -1)
                    break;

                textArea.append(new String(b, 0, len));
            } catch(IOException e) {

            }

            try {
                this.sleep(10L);
            } catch(InterruptedException e) {
            }
        }
    }
```

Aber der funktioniert einfach nicht richtig, irgendetwas beachte ich nicht. Denn es ist so, wenn ich die try-Schleife wo gelesen wird (len = in.read(b)) auskommentiere und das Programm starte, kann ich den Server mit get Anforderungen einfach "zumüllen". Ich trage immer eine einfache GET /index.htm HTTP/1.0 Anforderung ein und drücke auf Send. Jedoch wenn ich diese try-Schleife nicht auskommentiere, schreibt er nur beim ersten mal die empfangenen Daten in die TextArea und das auch nur in der Geschwindigkeit die in (sleep) angegeben ist (was ich eigentlich nicht möchte, sondern wenn es was zu schreiben gibt, alles sofort reinschreiben). Und beim dritten Senden der GET-Anfrage, bekomme ich folgende Fehlermeldung:

java.net.SocketException: Software caused connection abort: socket write error

Also irgendetwas Grundlegendes mache ich noch irgendwie falsch.


----------



## Thomas Darimont (17. Juni 2004)

Hallo!

Schau dir mal mein Beispiel an:


```
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

/*
 * Created on 16.06.2004
 *
 */

/**
 * @author Thomas Darimont
 * @company Tutorials.de
 * 16.06.2004
 **/
public class BrowserRequestTester extends JFrame {

	/* Unsere Beispiel URL mit der wird ein wenig herumspielen */
	private final String EXAMPLE_URL = "http://tutorials.de";
	/* Unser Beispiel HTTP Commando das wir an den Server schicken wollen */
	private final String EXAMPLE_HTTP_CMD = "HTTP 1.0 GET index.htm";

	//Die benötigten Felder für die Responsedaten 
	private final JLabel lblResponse;
	private final JTextArea txtAResponse;
	private final JScrollPane responseScrollPane;

	//Die benötigten Felder für die Requestdaten
	private final JLabel lblRequest;
	private final JTextArea txtARequest;
	private final JScrollPane requestScrollPane;

	//Die benötigten Felder für die URL
	private final JLabel lblUrl;
	private final JTextField txtUrl;
	private final JButton btnSubmit;

	//Sammelpanel für die URL Felder
	private final JPanel urlPanel;

	//Die URL mit der wir arbeiten
	private URL url;

	//Flag das darüber auskunft gibt, ob derzeit neue  Daten vorliegen oder nicht
	private volatile boolean dirty = false;

	/**
	 *	Unser ListenerThread der auf auf "Requestlese" Aufforderungen wartet ... 
	 */
	private final Thread listenerThread = new Thread() {
		{
			setPriority(Thread.MIN_PRIORITY);
		}
		public void run() {
			BufferedReader br = null;
			String line;

			/* 
			 * Äussere Schleife
			 * Hier wird einfach nur ein while(true) Ansatz verwendet, da der Thread
			 * wenn er einmal gestartet wurde bis zum Ende der Anwendung auf 
			 * Requestbearbeitungsanforderungen reagieren soll...
			 */
			OUTER : while (true) {

				try {

					//Wenn es nichts neues gibt schlafen wir ein wenig 
					while (!dirty) {
						Thread.sleep(150l);
					}

					final StringBuffer buffer = new StringBuffer(500);

					/*
						* Hier leeren wir die JTextArea für den Response
						* Das geschieht hier über SwingUtilities.invokeLater(...)
						* Da wir von einem "fremden" Thread GUI Manipulationen durchführen
						* wollen, was nur auf diese Weise gestattet ist.
						*/
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							txtAResponse.setText("");
						}
					});

					// Hier besorgen wir uns den InputStream der URL Connection und Umhüllen ihn
					// mit einem BufferedReader
					br =
						new BufferedReader(
							new InputStreamReader(
								url.openConnection().getInputStream()));

					//jede Zeile wird mit einem newLIne Symbol an den buffer angehängt.
					while ((line = br.readLine()) != null) {
						buffer.append(line + "\n");
					}

					//hier schlafen wir kurz um auf in diesem Zeig auf Unterbrechungen reagieren zu können---
					Thread.sleep(50l);

					System.out.println("read completed");

					//hier schreiben wir den buffer in die JTextArea von Response 
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							txtAResponse.setText(buffer.toString());
							txtAResponse.setCaretPosition(
								txtAResponse.getDocument().getLength());
						}
					});

					//Es gibt nix neues, da wir fertig sind mit dem bearbeiten der response...
					dirty = false;

				} catch (InterruptedException e) {
					System.out.println("inner");
					e.printStackTrace();
					try {
						if (br != null)
							br.close();
					} catch (IOException e1) {

						throw new RuntimeException("Could not close BufferedReader");
					}
					//wir wurden unterbrochen und fangen wieder ganz von vorne an ...
					continue OUTER;

				} catch (IOException e) {
					e.printStackTrace();

					return;
				}

			} //END Äussere Schleife
		}
	};

	public BrowserRequestTester() {
		super("BrowserRequestTester");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		lblResponse = new JLabel("Response:");

		responseScrollPane = new JScrollPane();
		responseScrollPane.setPreferredSize(new Dimension(400, 150));

		txtAResponse = new JTextArea();
		responseScrollPane.getViewport().add(txtAResponse);

		lblRequest = new JLabel("Request:");

		requestScrollPane = new JScrollPane();
		responseScrollPane.setPreferredSize(new Dimension(400, 150));

		txtARequest = new JTextArea();
		txtARequest.setText(EXAMPLE_HTTP_CMD);
		requestScrollPane.getViewport().add(txtARequest);

		urlPanel = new JPanel();

		lblUrl = new JLabel("URL:");
		txtUrl = new JTextField(25);
		txtUrl.setText(EXAMPLE_URL);
		btnSubmit = new JButton("submit");

		btnSubmit.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent evt) {

				try {
					//Wenn url == null -> "" andernfalls url als String
					final String oldUrl = url == null ? "" : url.toString();
					final String tmp = txtUrl.getText();
					//URL wurde schon vorher eingegeben?
					if (tmp.equalsIgnoreCase(oldUrl))
						return;

					url = new URL(tmp);
				} catch (MalformedURLException e) {
					//Schicke Fehlermeldung zeigen...
					JOptionPane.showMessageDialog(
						null,
						e,
						"Error",
						JOptionPane.ERROR_MESSAGE);
					e.printStackTrace();
					return;
				}

				//Neue Daten stehen für die Requestbearbeitung bereit -> dirty = true;
				//Wenn der Thread läuft bekommt er eine Unterbrechungsanforderung
				//Andernfalls wird der Thread gestartet...
				dirty = true;
				if (listenerThread.isAlive()) {
					System.out.println("Interrupt");
					listenerThread.interrupt();
				} else {
					listenerThread.start();
				}

			}
		});

		urlPanel.add(lblUrl, BorderLayout.WEST);
		urlPanel.add(txtUrl, BorderLayout.CENTER);
		urlPanel.add(btnSubmit, BorderLayout.EAST);

		Container c = getContentPane();

		c.add(responseScrollPane, BorderLayout.NORTH);
		c.add(requestScrollPane, BorderLayout.CENTER);
		c.add(urlPanel, BorderLayout.SOUTH);

		pack();
		setVisible(true);
	}

	public static void main(String[] args) {
		new BrowserRequestTester();
	}
}
```

HTH

Gruß Tom


----------



## Billie (11. August 2004)

So, nach langer Zeit habe ich mich wieder etwas mit meinem Programm beschäftigt.

Ich habe jetzt den Fehler gefunden, er liegt nur teilweise am Programm. Wie oben geschrieben sende ich immer die Anfrage "GET / HTTP/1.0" ... und soweit ich gelesen habe Unterstützt das HTTP 1.0 Protokoll keine dauerhaften Verbindungen und beendet damit automatisch nach jeder Anfrage die Verbindung - was wusste ich zB damals noch nicht.

Wenn ich eine neue Anfrage "GET / HTTP/1.1\r\nConnection: Keep-Alive" benutze, funktioniert das ganze.


----------

