Aplikacja Serwerowa - Socket Exception

0

Witam, piszę od nowa aplikacje pobierajaca dane z serwera i zwracajaca dane do klienta. Jednak mam pewien problem, serwer poprawnie wyrzuca wszystkie dane, jednak przy kolejnym pobraniu danych na kliencie wyskakuje błąd java.io.EOFException, natomiast na serwerze wyskakuje ponizzszy log:

java.net.SocketException: Socket closed
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:170)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at pl.allst.rpi.serwer.core.ResponseThread.run(ResponseThread.java:21)

Poniżej podaje metode run(), która wyrzuca wyjątek:

    public void run() {
    	try(BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));	)
    	{
    		String inputLine ;     
	        	//TODO PROCESS INPUT
    		while ((inputLine = in.readLine()) != null) {
		        if(inputLine.equals("STATUS")){
		        	ObjectOutputStream outO = new ObjectOutputStream(socket.getOutputStream()); 
		        	status.update();
		        	outO.writeObject(status);
		        	outO.flush();
		        	outO.close();
		        }
		        else if(inputLine.equals("NAME")){
		        	PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
		        	out.println(StaticActions.getName());
		        }
		        else if(inputLine.equals("RAMTOTAL")){
		        	PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
		        	out.println(StaticActions.getRamTotal());
		        }
		        else if(inputLine.equals("RESET")){
		        	StaticActions.rebootPi();
		        }
		        else if(inputLine.equals("TEST")){
		        	PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
		        	out.println("OK");
		        }
    		}
	        socket.close();
    	} catch (IOException e) {
            e.printStackTrace();
        }
    
    }

Wydaje mi się że błąd u klienta jest spowodowany tym że serwer zamyka socketa przy ponownym wyslaniu danych, tak jak powiedzialem za pierwszym razem wszystko idzie bez problemu. Jeszcze podam fragment kodu klienta odpowiedzialnego za przetworzenie obiektu przeslanego przez serwer.

    public void run(){

	    try(Socket skt = new Socket(host,port);
	    	PrintStream Output = new PrintStream(skt.getOutputStream());)
	    {
	    	BufferedReader Input = new BufferedReader(new InputStreamReader(skt.getInputStream()));
	    	
	    	Output.println("NAME");
		    String buf=Input.readLine();
		    System.out.println(buf);
		    app.getSystemNameLbl().setText(buf);
		    app.setNameLbl(buf);

		    Output.println("RAMTOTAL");
		    buf=Input.readLine();
		    System.out.println(buf);
		    ramTotal=Integer.parseInt(buf);
		    app.getRamTotalLbl().setText(buf+" MB");
		    app.getRamBar().setMaximum(ramTotal);


		    Status status;
		    Output.println("STATUS");
		    ObjectInputStream ois = new ObjectInputStream(skt.getInputStream());
		    status = (Status) ois.readObject();
		    while(status!=null){
			    app.getRamBar().setValue((int)((ramTotal-status.getRamAvailable())*100)/1024);
			    app.getTempBar().setValue((int)Math.round(status.getTemp()));
			    app.getTempBar().setString(String.valueOf(status.getTemp())+"'C");
			    StringBuilder sb = new StringBuilder();
			    for(String x : status.getPplLogged()){
			      	sb.append(x);
			       	sb.append("\n");
			    }
			    app.getLoggedUsersArea().setText(sb.toString());
			   	Thread.sleep(5000);
			   	Output.println("STATUS");
			   	status = (Status) ois.readObject();
		   	}
		    ois.close();
		}

Proszę o pomoc
Pozdrawiam.

0

Zamykasz stream socketu na serwerze więc czego sie spodziewasz?

0

nawet bez socket.close(); taki sam output.

1

Oczywyście bo do tej linijki nawet nie dochodzisz. Powtarzam: zamykasz STREAM socketu, nie socket jako taki.

0

Ok, dzieki po stronie serwera nie ma juz bledu, jednak teraz po stronie klienta pojawil sie blad java.io.StreamCorruptedException: invalid type code: AC . Wywala przy tej linijce :
status = (Status) ois.readObject();

0

To dlatego że wielokrotnie tworzysz obiekty do komunikacji TYM SAMYM STRUMIENIEM, ale to są różne obiekty i komunikują się w rózny sposób.
BufferedReader Input = new BufferedReader(new InputStreamReader(skt.getInputStream()));
a za chwile masz
ObjectInputStream ois = new ObjectInputStream(skt.getInputStream());
to nie będzie działać bo te obiekty będą sobie "mieszać" w strumieniu. Tak samo zresztą robisz też po stronie serwera.

0

To dlaczego pierwsze przejscie kończy sie sukcesem?
I w jaki sposób byś to zmienił?

3

Ja bym to zaorał i w ogóle nie bawił sie w serializacje obiektów i ich wysyłanie, choćby ze względów bezpieczeństwa, ale też użyteczności takiego rozwiązania. Zamiast tego przesyłałbym jakiegoś JSONa albo XMLa i wtedy problemu w ogóle nie ma.

0

no i fajnie, parsowanie xml tez sie bardzo przyda;) dzieki za pomoc! Sprobuje.

0

Użyłem zamiast serializacji xml, jednak pojawił się mały problem, ale najpierw pokaze kod programu
Klient:

  public void run(){

	    try(Socket skt = new Socket(host,port);
	    	PrintStream Output = new PrintStream(skt.getOutputStream());
	    	BufferedReader Input = new BufferedReader(new InputStreamReader(skt.getInputStream())))
	    {
	    	
	    	Output.println("NAME");
		    String buf=Input.readLine();
		    app.setNameLbl(buf);

		    Output.println("RAMTOTAL");
		    buf=Input.readLine();
		    ramTotal=Integer.parseInt(buf);
		    app.getRamTotalLbl().setText(buf+" MB");
		    app.getRamBar().setMaximum(ramTotal);

		    Status status;
		    Output.println("STATUS");
		    JAXBContext jaxbContext = JAXBContext.newInstance(Status.class);
		    Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
		    status = (Status) jaxbUnmarshaller.unmarshal( Input );
		    System.out.println("Bylem");//Nie przechodzi do tego miejsca, chyba że serwer zakonczy działanie(ctrl+c) wtedy przechodzi dalej.
		    System.out.println(status.getTemp());
		    while(status!=null){
			    app.getRamBar().setValue((int)((ramTotal-status.getRamAvailable())*100)/1024);
			    app.getTempBar().setValue((int)Math.round(status.getTemp()));
			    app.getTempBar().setString(String.valueOf(status.getTemp())+"'C");
			    StringBuilder sb = new StringBuilder();
			    for(String x : status.getPplLogged()){
			      	sb.append(x);
			       	sb.append("\n");
			    }
			    app.getLoggedUsersArea().setText(sb.toString());
			   	Thread.sleep(2000);
			   	Output.println("STATUS");
			   	status = (Status) jaxbUnmarshaller.unmarshal( Input );
		   	}
		}
		catch (Exception ex){
		    ex.printStackTrace();
		    app.getLoggedUsersArea().setText("ERROR: NO RESPONSE FROM SERVER\nPROGRAM IS GOING TO BE CLOSED");
		    JOptionPane.showMessageDialog(app,"Connection Lost","Connection Error",JOptionPane.ERROR_MESSAGE);
		    System.exit(-1);
		}
	}

Oraz serwer odpowiadajacy:

  public void run() {
    	try(BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    		PrintWriter out = new PrintWriter(socket.getOutputStream(), true))
    	{
    		String inputLine ;     
    		while ((inputLine = in.readLine()) != null) {
		        if(inputLine.equals("STATUS")){
		    		JAXBContext jaxbContext = JAXBContext.newInstance(Status.class);
		    		Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
		    		jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		    		
		        	status.update();
		        	jaxbMarshaller.marshal(status, out);
		        	jaxbMarshaller.marshal(status, System.out);//Tutaj wypisuje poprawnego xml	
		        }
		        else if(inputLine.equals("NAME")){		        	
		        	out.println(StaticActions.getName());
		        }
		        else if(inputLine.equals("RAMTOTAL")){
		        	out.println(StaticActions.getRamTotal());
		        }
		        else if(inputLine.equals("RESET")){
		        	StaticActions.rebootPi();
		        }
		        else if(inputLine.equals("TEST")){
		        	out.println("OK");
		        }
    		}
    		socket.close();
    	} catch (IOException | JAXBException e) {
            e.printStackTrace();
        }

Klient pomimo że nie wypluwa błędu, zwiesza się i nie przechodzi dalej ( zaznaczylem, w którym momencie), ale gdy socket zostanie zamkniety(lub caly program serwera) wszystko przechodzi dalej.
A drugim mniej ważnym problemem jest to że pomimo odebrania poprawnego xml to nie sczytuje poprawnie wartosci double.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<status>
    <pplLogged>pi       :0           2016-04-11 10:11 (:0)</pplLogged>
    <pplLogged>pi       tty1         2016-04-11 10:11</pplLogged>
    <pplLogged>pi       pts/0        2016-04-11 10:11 (192.168.1.103)</pplLogged>
    <ramAvailable>782</ramAvailable>
    <temp>49.9</temp>
</status>
0

No a czego sie spodziewałeś? o_O Przekazujesz do Unmarshallera cały input stream który NIE JEST zakończony. Skad ten biedny unmarshaller ma wiedzieć czy coś z tego streama jeszcze przyjdzie czy nie? On czeka na EOF jakiś a ten pojawia sie dopiero jak zamykasz socket. To ty musisz wiedzieć ile danych wczytać z tego strumienia.
Drugiego problemu nie rozumiem.

0

Próbowalem zrobić to w ten sposób:

    		while ((inputLine = in.readLine()) != null) {
		        if(inputLine.equals("STATUS")){
		    		JAXBContext jaxbContext = JAXBContext.newInstance(Status.class);
		    		Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
		    		jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		    		
		        	status.update();
		        	StringWriter dataWriter = new StringWriter();
		        	jaxbMarshaller.marshal(status, dataWriter);	
		        	out.print(dataWriter.toString());
		        	out.println();
		        	out.flush();

		        }

Jednak to nic nie dalo, czy możesz mi powiedzieć jak dodać EndOfFile do tego wyjsciowego xml?
A co do drugiego pytania, po pobraniu obiektu, wartosc temp (double) jest równa zeru. A w xml, który przyszedł jest zgodna z outputem z serwera. Nie wiem dlaczego nie pobrało z xml wartosci zmiennej temp.

0

Nie no ty nie rozumiesz. To po drugiej stronie socketu jest problem. Skąd po tamtej stronie ma być wiadomo że juz przesłałeś wszystkie dane?

0

No najlepiej jakby sprawdzalo czy nie pojawił się znacznik </status>. Tylko nie wiem jak to obsłużyć.

0 użytkowników online, w tym zalogowanych: 0, gości: 0