Wielokrotna Serializacja

0

Witam.
Piszę grę z komunikacją klient <--> serwer opartą na wątkach. Podczas połączenia klienta z serwerem, serwer uruchamia dla niego wątek tworząc w konstruktorze ObjectInputStream i ObjectOutputStream potrzebne do serializacji. Klient wysyła obiekt do serwera a ten odbiera go w konkretnym wątku i wypisuje na konsolę.
Problem zaczyna się kiedy połączy się dwóch klientów, uruchomią się dwa wątki z osobnymi stworzonymi w konstruktorze kanałami komunikacyjnymi. Pierwszy klient wykonuje dowolną ilość akcji(po wykonaniu wysyłam obiekt do serwera), wątek odbiera obiekt i wypisuje na konsole pola zdeserializowanego obiektu. Potem przełączam okno na drugiego klienta i wykonuje akcje lecz dochodzi tylko jeden obiekt i następuje zawieszenie, w niektórych przypadkach dostaje wyjątki:

java.io.StreamCorruptedException: invalid type code: 00
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.readObject(Unknown Source)
	at siec.Server.readSerializedObject(Server.java:125)
	at siec.Server.run(Server.java:144)

oraz

IOException

klasa serwera:

package siec;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class Server extends Thread {
	public static final int PORT = 5000;
	public static final String IP = "127.0.0.1";
	static InetAddress addr = null;
	static ServerSocket serverSocket = null;
	static Socket socket = null;
	static BufferedReader in = null;
	PrintWriter out = null;
	static ObjectOutputStream oos = null;
	static ObjectInputStream ois = null;
	private int PLAYER_ID = 0;

	private int players = 0;

	public Server() {
		// /////////////
	}

	public void updatePlayerId(String playerId) {
		if (playerId.compareTo("1") == 0)
			PLAYER_ID = 1;
		if (playerId.compareTo("2") == 0)
			PLAYER_ID = 2;
		System.out.println("Nowy gracz o id: " + PLAYER_ID);
	}

	public Server(Socket s, String playerId) throws IOException {
		socket = s;
		updatePlayerId(playerId);
		in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		System.err.println("-->Stworzono BufferedReader dla wątku");
		out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
				socket.getOutputStream())), true);
		System.err.println("-->Stworzono PrintWriter dla wątku");

		oos = new ObjectOutputStream(socket.getOutputStream());
		System.err.println("-->Stworzono ObjectOutputStream dla wątku");
		ois = new ObjectInputStream(socket.getInputStream());
		System.err.println("-->Stworzono ObjectInputStream dla wątku");

		start();
		System.err.println("-->Stworzono wątek dla klienta o id: " + PLAYER_ID);
	}

	public ServerSocket startServer(ServerSocket serverSocket, int port) {
		try {
			serverSocket = new ServerSocket(port);
			System.err.println("-->Serwer wystartował");
		} catch (IOException e) {
			System.err.println("-->Błąd startu servera");
		}
		return serverSocket;
	}

	/*
	 * public Socket acceptConnection(ServerSocket serverSocket, Socket socket)
	 * throws IOException { //serverSocket = new ServerSocket(port); try {
	 * socket = serverSocket.accept();
	 * System.err.println("Przyjeto połączenie o id: " + socket); } catch
	 * (Exception e) { //System.err.println("-->Błąd przyjmowania połączenia");
	 * } return socket; }
	 */

	public void addPlayer() {
		this.players++;
	}

	public String getPlayers() {
		return "" + this.players;
	}

	public String readData(Socket socket) throws IOException {
		String data = null;
		try {
			in = new BufferedReader(new InputStreamReader(
					socket.getInputStream()));
			data = in.readLine();
		} catch (IOException e) {
			System.err.println("-->Błąd czytania danych");
		}
		return data;
	}

	public void writeData(Socket socket, String data) throws IOException {
		try {
			out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
					socket.getOutputStream())), true);
			out.println(data);
		} catch (IOException e) {
			System.err.println("-->Błąd wysyłania");
		}
	}

	public static void writeSerializedObject(Gamer gamer) {
		try {
			oos.writeObject(gamer);
			oos.flush();
			oos.reset();
		} catch (IOException e) {
			System.err.println("Błąd wysyłania obiektu");
		}
	}

	public Gamer readSerializedObject() throws IOException,
			ClassNotFoundException {
		Gamer gamer = null;
		try {
			gamer = (Gamer) ois.readObject();
		} catch (IOException e) {
			System.out.println("Błąd odczytu obiektu");
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			System.out
					.println("Błąd ClassNotFoundException podczas czytania obiektu");
		}
		return gamer;
	}

	@Override
	public void run() {
		Gamer gamer = null;
		Gamer gamerToSend = null;
		String data = null;
		while (true) {
			try {

				gamer = readSerializedObject();
				// data = readData(socket);
				if (gamer != null) {
					if (gamer.getGamerId() == 1)
						System.out.println("Gracz 1: " + socket.getPort()
								+ "  " + gamer.toString());
					if (gamer.getGamerId() == 2)
						System.out.println("Gracz 2: " + socket.getPort()
								+ "  " + gamer.toString());
				}
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (NullPointerException e) {
				e.printStackTrace();
			}

		}
	}
}

package siec;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerManager {
	final static int PORT = 5000;
	final String IP = "127.0.0.1";
	static ServerSocket serverSocket = null;
	static Server server = new Server();
	
	static ByteArrayInputStream baisThread = null;	
	static ObjectInputStream oisThread = null;
	static ByteArrayOutputStream baosThread = null;
	static ObjectOutputStream oosThread = null;
	
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		baosThread = new ByteArrayOutputStream();
		oosThread = new ObjectOutputStream(baosThread);
		
		baisThread = new ByteArrayInputStream(baosThread.toByteArray());
		oisThread = new ObjectInputStream(baisThread);
		
		
		serverSocket = server.startServer(serverSocket, PORT);
		try {
			while(true) {
				Socket socket = serverSocket.accept();
				System.err.println("-->Nowe połączenie " + socket);
				server.addPlayer();
				new Server(socket, server.getPlayers());
				server.writeData(socket, server.getPlayers());
			}
		} catch (IOException e) {
				System.err.println("-->Błąd przyjmowania połącznenia");
		}
	}
}

Klient posiada te same metody co serwer więc nie ma sensu wklejać tyle kodu.

Chciałbym zadać przy okazji kolejne pytanie.
Otóż jeśli komunikacja klient <-->serwer<-->klient działałaby, jak mogę przesłać odebrany obiekt od pierwszego klienta w pierwszym wątku do drugiego wątku aby ten mógł wysłać go do drugiego klienta(wszystko za pośrednictwem serwera) ?
Z góry dziękuje za pomoc.

0

Pierwsze co rzuca się w oczy to to, że tworzysz tylko jeden stream, wspólny dla wszystkich klientów. Co więcej, czytasz z tego streamu w różnych wątkach bez żadnej synchronizacji...

0

przecież w konstruktorze Servera tworzę stream dla każdego obiektu, po akceptacji połączenia jest tworzony nowy obiekt Servera i wątek korzysta właśnie z nich..
Jeśli jestem w błędzie to proszę o jakieś wskazówki.

0

A fakt, tak to skomplikowałeś że trudno się w tym połapać. Bo u ciebie Server to jest tak na prawdę wątek obsługi klienta, a ServerManager to jest serwer...
Nadal widzę tu problem z synchronizacją, a raczej jej brakiem. Zapamiętaj że jeśli gdziekolwiek używasz wątków to na 99% trzeba to będzie synchronizować.
Ten kod w ogóle nie trzyma się kupy i trudno mi w ogóle ogarnać co ty tu chciałeś zrobić. Moja rada? Skasuj to i napisz ponownie, tym razem z głową.

0

ok tylko przy jakich założeniach mam to napisać? Jak stworzyć odrębne kanały komunikacyjne dla każdego klienta? Jak odbierać te dane na serwerze? I jak przysyłać serializowane obiekty między wątkami?

0

Najprościej? Przez RMI ;]

0

dobrze tylko w czym pomoże mi RMI? Nie zależy mi na zdalnym wykonywaniu metod na serwerze tylko na komunikacji i przesyłaniu obiektów klient --- serwer --- klient.
Przy wykorzystaniu RMI musiałbym przenieść wszystkie metody do interfejsu serwera i wykonywać je zdalnie za pomocą klienta i pobierać tylko ich wyniki i potem repaint w kliencie. Do tego musiałbym je również zostawić po stronie klienta żeby np. utworzyć obiekt gracza.

0

Oczywiście, ale ja podałem najłatwiejsze rozwiązanie.
Twój pomysł tutaj jest dobry, ale za bardzo to zagmatwałeś.

0

Rozumiem, zabieram się właśnie za pisanie tego od nowa :) Mógłbyś mi napisać jakieś wskazówki jak to dobrze zrealizować i czego się trzymać?

  1. Jak utworzyć dla każdego klienta osobny kanał komunikacyjny?
  2. Jak przesyłać wewnątrz serwera odebrane obiekty od klienta między działającymi wątkami?
  3. Jak synchronizować wykonywane operacje?
1

Pisząc tego posta znalazłem twój błąd w kodzie: nie rozumiesz do czego służy static...

Ja to widzę tak:

  • klasa Server która zajmuje się nasłuchiwaniem na klientów, jest wątkiem. W main() tworzysz jej obiekt i go startujesz i tyle (!). Jeśli nowy klient się dołączy to tworzony jest nowy obiekt obsługi klienta. Wszystkie obiekty obsługi klienta są trzymane w jakiejś kolekcji.
  • klasa ClientHandlerThread zajmuje się obsługą jednego klienta i potrzebuje tylko socket do niego (a razem z socketem ma streamy).
    Synchronizacja nie będzie konieczna, bo w przeciwieństwie do twojego dziwnego rozwiazania nie ma tutaj współdzielenia danych przez wątki.

Czego w takim razie w twoim kodzie być nie powinno, a jest?

        static Server server = new Server();
        static ByteArrayInputStream baisThread = null;        
        static ObjectInputStream oisThread = null;
        static ByteArrayOutputStream baosThread = null;
        static ObjectOutputStream oosThread = null;
        public static final int PORT = 5000;
        public static final String IP = "127.0.0.1";
        static InetAddress addr = null;
        static ServerSocket serverSocket = null;
        static Socket socket = null;
        static ObjectOutputStream oos = null;
        static ObjectInputStream ois = null;
        private int PLAYER_ID = 0;

(socket i streamy nie mogą być statyczne bo będą wspólne dla każdego obiektu!)

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