Serverproblem

vR34kSH0w

Mitglied
Hi,
ich versuche gerade, einen kleinen chat server zu programmieren, funktioniert auch ziemlich gut, aber nur unter einer meiner meinung nach etwas merkwürdigen bedingung.

folgender programmteil soll (wenn dann ausgebaut) alle nachrichten speichern:
Java:
import java.io.*;
import java.util.ArrayList;

public class MessageThreadTest extends Thread {
	public ArrayList <BufferedReader> br = new ArrayList <BufferedReader>();
	
	private ArrayList <String> messages = new ArrayList <String>();
	
	public void run() {
		while (
				
		true) {
			
		for (
				
		int i=0;
				
		i<br.size();
				
		i++) {
		
		try {
			
		if (
				
		br.get(i).ready()) {
			
		messages.add(br.get(i).readLine());
		}
		} catch (IOException ioe) {
			System.err.println(ioe);
		}
		}
		
		for (
				
		int i=0;
				
		i<messages.size();
				
		i++) {
		
		System.out.println("Message " + (i+1) + ": " + messages.get(i));
		}
		}
	}
}

so, wie es da oben steht, funktionierts nicht. füge ich in zeile 13 ein system.out.println("völligegalwashiersteht"); ein, schon.

hat jemand eine erklärung dafür??

gruß
vR34k$H0w
 

vR34kSH0w

Mitglied
laut den regeln hier sollte eigentlich genau das jeder lesen können:
Code Conventions for the Java(TM) Programming Language: Contents

edit: allerdings such ich hier hilfe und keine diskussion über quelltextformatierung :D

daher hab ichs umformatiert:
Java:
import java.io.*;
import java.util.ArrayList;

public class MessageThreadTest extends Thread {
	public ArrayList <BufferedReader> br = new ArrayList <BufferedReader>();
	
	private ArrayList <String> messages = new ArrayList <String>();
	
	public void run() {
		while (true) {
			
			for (int i=0; i<br.size(); i++) {
				try {
					if (br.get(i).ready()) {
					messages.add(br.get(i).readLine());
					}
				} catch (IOException ioe) {
					System.err.println(ioe);
				}
			}
			
			for (int i=0; i<messages.size(); i++) {
				System.out.println("Message " + (i+1) + ": " + messages.get(i));
			}
		}
	}
}
:toll:
 
Zuletzt bearbeitet:

xehpuk

Top Contributor
Nochmal kurz zur Quelltextformatierung: Wieso hast du die Code Conventions denn nicht gelesen?
Der Code im ersten Post verstößt gegen 4 - Indentation und 8 - White Space.

Was funktioniert denn nicht? Dass der Thread ununterbrochen in einer Endlosschleife läuft, ist zumindest nicht so cool.
 
I

irgendjemand

Gast
gut ... allerdings hält sich sein erster post hieran : NO TITLE

und das ist nun wirklich absoluter bullshit ... kein mensch der welt schreibt in irgendeiner sprache

Code:
for(
init,
condition,
loop
)

und da sag doch mal einer : man muss sich buchstabengenau an die konventionen halten ...
 

vR34kSH0w

Mitglied
niemand sagt dass man sich dran halten MUSS habs mir mehr oder weniger angewöhnt, aber zurück zum problem:
die while schleife könnte ich durch einen timer ersetzen aber es muss ja auf jeden fall permanent nach neuen nachrichten geschaut werden.
was nicht funktioniert is ehrlichgesagt etwas bescheuert.
wie gesagt: wenn ich in zeile 11 (beim code mit der gängigen formatierung) ein system.out.println einfüge egal mit welchem inhalt geht das programm erwartungsgemäß bei br>0 in die for schleife. so wie es da steht leider nicht. br.size() hab ich mir aber ausgeben lassen - in beiden fällen 1 ???:L
 
J

JohannisderKaeufer

Gast
Java:
import java.io.*;
import java.util.ArrayList;
 
public class MessageThreadTest extends Thread {
    public ArrayList <BufferedReader> br = new ArrayList <BufferedReader>();
    
    private ArrayList <String> messages = new ArrayList <String>();
    
    public void run() {
        while (true) {
            System.out.println("1.");
            for (int i=0; i<br.size(); i++) {
                System.out.println("2.");
                try {
                    if (br.get(i).ready()) {
                      System.out.println("3.");
                      messages.add(br.get(i).readLine());
                      System.out.println("4.");
                    }
                } catch (IOException ioe) {
                    System.err.println(ioe);
                }
            }
            
            for (int i=0; i<messages.size(); i++) {
                System.out.println("Message " + (i+1) + ": " + messages.get(i));
            }
        }
    }
}

Was wird da wohl ausgegeben?
Ich vermute
1.
2.
3.

das readline() liest blockierend, d.h. solange kein \r oder \n eingelesen wird gibt es keine Antwort von readline().

Erst wenn das in den entsprechenden reader eingegeben wird, gibt es die Antwort von readline und ein 4. wird ausgegeben, ansonsten hängt es halt in dieser Zeile fest.

Eine Lösung könnte so aussehen, dass jedem Reader ein eigener Thread spendiert wird.

Java:
import java.io.*;
import java.util.ArrayList;
 
public class MessageThreadTest extends Thread {
    public ArrayList <BufferedReader> br = new ArrayList <BufferedReader>();
    
    private ArrayList <String> messages = new ArrayList <String>();
    
    public void run() {
        for(BufferedReader reader : br){
          new Thread(new Runnable(){
            public void run(){
              messages.add(br.readLine());
            }
          }).start();
        }
        while (true) {
            sleep(10000);
            for (int i=0; i<messages.size(); i++) {
                System.out.println("Message " + (i+1) + ": " + messages.get(i));
            }
        }
    }
}
 

vR34kSH0w

Mitglied
schonmal danke für die antworten...

okay die ausgabe von oben wäre:
1.
1.
1.
1.
-- -- irgendwann wird dann mal die nachricht vom client gesendet -- --
1.
2.
Message 1: [etc.]

also nicht so wirklich das was man erwarten würde :(

mit dem lösungsansatz hab ich jetzt mal gearbeitet hat aber leider auch nicht funktioniert
vielleicht überseh ich ja auch was in den anderen programmteilen
hier mal der komplette quelltext...

Server
Java:
import java.io.*;
import java.net.*;

public class ServerTest {
	MessageThreadTest mt = new MessageThreadTest();
	ServerSocket server;
	Socket client;
	
	public ServerTest(int port) throws IOException{
		mt.start();
		
		server = new ServerSocket(port);
		
		while(true) {
			client = server.accept();
			
			mt.br.add(new BufferedReader(new InputStreamReader(client.getInputStream())));
		}
	}
	
	public static void main(String[] args) throws Exception{
		new ServerTest(1234);
	}
}

Client
Java:
import java.io.*;
import java.net.*;

public class ClientTest {
	int port;
	
	BufferedWriter bw;
	Socket socket;
	String ip;
	
	public ClientTest(String ip, int port) throws Exception {
		this.ip = ip;
		this.port = port;
		
		socket = new Socket(ip, port);
		
		bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
		
		bw.write("Hello, I am " + socket.getLocalSocketAddress());
		bw.newLine();
		bw.flush();
	}
	
	public static void main(String[] args) throws Exception {
		new ClientTest("10.0.1.101", 1234);
	}
}

der rest ist ja bekannt...
 

Michael...

Top Contributor
wenn das mal ein Chat werden soll, dann müssen die InputStreams der verbundenen Sockets in einem jeweils separaten Thread ausgelessen werden. Es ist ja üblicherweise nicht unbedingt so, dass sich Chatteilnehmer immer in der angemeldeten Reihenfolge äußern.
Prinzip ChatServer nimmt Verbingung einens Clients an und übergibt dessen InputStream zu auslesen an einen neuen Thread. Hier kann mit z.B. readLine() blockierend auf eine Nachricht des Clients gewartet werden ohne andere Prozesse zustören. Wird eine Nachricht empfangen und per readLine() eingelesen wird dies dem ChatServer mitgeteilt und der muss dann bestimmen was damit gemacht wird, z.B. an alle Chat Teilnehmer versenden.
 

vR34kSH0w

Mitglied
okay kann durchaus etwas dauern bis ich dazu komme das umzuschreiben werd ich aber machen...
folgendes is wahrscheinlich ein denkfehler aber wenn doch der chatserver eine instanz des threads aufruft wie soll dieser dem chatserver dann mitteilen dass eine nachricht vorliegt. der thread hat doch eigentlich keinen zugriff auf methoden des servers !!??
 

Michael...

Top Contributor
die Threads müssen natürlich auf die Methoden des Servers zugreifen können.
Entweder die Threads sind innere Klassen des Servers oder sie bekommen eine Referenz auf diesen übergeben.
 

vR34kSH0w

Mitglied
okay auf die weise hats super geklappt =)
danke für eure hilfe.

Sollte jemand ähnliche Probleme haben, hier der Quelltext:

Server
Java:
package tests.Server;

import java.io.*;
import java.net.*;
import java.util.*;

public class MessageServer
{
	ArrayList <String[]> messages = new ArrayList <String[]>();
	ClientHandlingThread handlingThread;
	ServerSocket server;
	Socket client;
	
	public MessageServer(int port) throws IOException
	{
		server = new ServerSocket(port);

		while(true)
		{
			client = server.accept();
			
			handlingThread = new ClientHandlingThread(
					new BufferedReader(new InputStreamReader(client.getInputStream())),
					this);
			handlingThread.start();
		}
	}
	
	public void addMessage(String userName, String message)
	{
		messages.add(new String[] {userName, message});
		
		System.out.println(userName + ": " + message);
	}
	
	public static void main(String[] args) throws IOException
	{
		new MessageServer(11112);
	}
}

Client
Java:
package tests.Server;

import java.io.*;
import java.net.*;

public class Client
{
	BufferedWriter writer;
	Socket client;
	String userName;
	
	public Client(int port, String ip, String userName) throws IOException, UnknownHostException
	{
		client = new Socket(ip, port);
		
		this.userName = userName;
		
		writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
		
		sendMessage(userName);
		sendMessage("Hi, Server!");
	}
	
	public void sendMessage(String message) throws IOException
	{
		writer.write(message);
		writer.newLine();
		writer.flush();
	}
	
	public static void main(String[] args) throws Exception
	{
		new Client(11112, "10.0.1.101", "user1");
	}
}

Client Handling Thread
Java:
package tests.Server;

import java.io.*;

public class ClientHandlingThread extends Thread
{
	BufferedReader reader;
	MessageServer server;
	String userName;
	
	public ClientHandlingThread(BufferedReader reader, MessageServer server)
	{
		this.reader = reader;
		this.server = server;
	}
	
	public void run()
	{
		try
		{
			userName = reader.readLine();
		}
		catch(IOException ioe)
		{
			System.err.println(ioe);
		}
		
		while(true)
		{
			try
			{
				String message = "";
				
				if(reader.ready())
				{
					message = reader.readLine();
					server.addMessage(userName, message);
				}
			}
			catch(IOException ioe)
			{
				System.err.println(ioe);
			}
		}
	}
}

Für Verbesserungsvorschläge bin ich natürlich noch offen. Morgen oder übermorgen markier ich das Thema dann als erledigt =)

Danke!
 

Empire Phoenix

Top Contributor
Du hast noch einen Fehler drinnen, du must auf das messages array synchroniziert zugreifen, da du sonst multithreading fehler bekommst. (Arraylist ist nicht Threadsafe!)


eine Möglichkeit dieses zu tun wäre um jede stelle wo das array verwendet wird:
synchronized(messages){
mach irgetwas wobei das array sich nicht verändern darf durch andere Threads, zb add, oder ne for schleife
}

Noch so als zusatz:

Per prinzip haste egal was du mit netzwerken amcsht folgende 3 Möglichkeiten:

Socketapi:
-> 2 Threads pro client benötigt, einer zum lesenund iener zum schreiben ( denn auch das out kann blockieren, und wenn da jemand mit nem 32k modem online geht in den chat soll der ja nicht alle anderen blockieren) Dies kann derzeit noch bei dir passieren, die Lösung wäre eine ausgehende Queue pro Client, wo alle noch zu sendenden Strings drinne sind, und dann nen Thread der poll macht und die Nachricht verschickt.(Stichwort Blockingqueue)


Selector/ChannelAPI (NIO):
-> Thread zum lesen und schreiben reicht, da die api jeweils benachrichtigt wenn neue Daten zum lesen verfügbat sind, bzw geschreiben werden kann, und zudem nicht alle bytes schreiben/lesen muss, sondern stattdessen zurücklifert wieviele sie verarbeitet hat, Nachteil alles muss auf ByteBuffer ebene laufen, und man muss manuell sicherstellen, dass man die gesammte Nachricht erhalten hat, bevor man die verarbietet (in diesem Fall wohl relativ leicht lösbar von wegen Zeilenumbruch am ende jeder Nachricht)

DatagramSocket
-> UDP basiert arbeiten, hierbei muss 1 Thread zum lesen benuzt werden, geschreiben werden können belibige Datenmengen ohne verzögerung, jedoch ohne garantie dass selbige ankommen, hier muss man also wenn man Nachrichten zuverlässig übermitteln will sicherstellen, dass diese ankommen. Daher für einen Chat eher ungeeignet.
 
Zuletzt bearbeitet:

vR34kSH0w

Mitglied
wenn ich jetzt alles richtig verstanden habe müsste folgendes funktionieren (hoffe ich)

Java:
package tests.Server;

import java.io.*;
import java.net.*;
import java.util.*;

public class MessageServer
{
	ArrayList <String[]> messages = new ArrayList <String[]>();
	ClientHandlingThread handlingThread;
	List <String[]> syncMessages = Collections.synchronizedList(messages);
	ServerSocket server;
	Socket client;
	
	public MessageServer(int port) throws IOException
	{
		server = new ServerSocket(port);

		while(true)
		{
			client = server.accept();
			
			handlingThread = new ClientHandlingThread(
					new BufferedReader(new InputStreamReader(client.getInputStream())),
					this);
			handlingThread.start();
		}
	}
	
	public void addMessage(String userName, String message)
	{
		syncMessages.add(new String[] {userName, message});
		
		System.out.println(userName + ": " + message);
	}
	
	public static void main(String[] args) throws IOException
	{
		new MessageServer(11112);
	}
}
 

Neue Themen


Oben