Synchronizacja strumieni w Javie.

0

Witam. Po prostu pytam z czystej ciekawości bo sam nie jestem w stanie zrozumieć dziwnego zachowania. Mam program, w który po boku są 4 przyciski. Na razie działa tylko jeden, ale to nieważne. Po wciśnięciu go wysyłamy do serwera informacje, że chcemy listę użytkowników, a serwera nam ją odsyła. Problem dostawałem w momencie gdy kliknąłem przycisk dostałem tą listę, ale po chwili chciałem tą listę odświeżyć i ponownie w niego klikałem. Program po prostu zawieszał się. Chciałem dowiedzieć się gdzie błąd, może gdzieś czeka na odpowiedź serwera albo coś. Dodałem 3 linijki w poniższym kodzie:

System.out.println("dsadads");

uruchamiam program i o dziwo teraz wszystko działa jak należy bez żadnej zwiechy. Kiedy te linijki usunę bądź skomentuję to problem powraca. Jest mi ktoś w racjonalny sposób wyjaśnić czemu tak się dzieje? Przecież te linijki nie wpływają na funkcjonowanie programu.

	public List<User> usersList() {
		writer.println("Lista uzytkownikow");
		writer.flush();
		NullObject ol = new NullObject();
		System.out.println("dsadads");
		try {
			oos.writeObject(ol);
			oos.flush();
			System.out.println("dsadads");
			ReplyUsersList rus = (ReplyUsersList) ois.readObject();
			System.out.println("dsadads");
			if (rus.getText() != null) {
				JOptionPane.showMessageDialog(null, rus.getText(), "Błąd",
						JOptionPane.ERROR_MESSAGE);
			}
			return rus.getUsersList();
1

Te 3 linijki powodują opóźnienie i synchronizacje ze strumieniem i/o więc realnie wpływają na wykonanie programu.
Ciekawi mnie co masz w "catch" w tym kodzie i obawiam sie że jest on u ciebie pusty, stąd twoje problemy.
Zamiast wstawiać idiotyczne system outy naucz sie używać debugera.
Eclipse nic do twojego problemu nie ma.

0
	public List<User> usersList() {
		writer.println("Lista uzytkownikow");
		writer.flush();
		NullObject ol = new NullObject();
		System.out.println();
		try {
			oos.writeObject(ol);
			oos.flush();
			System.out.println();
			ReplyUsersList rus = (ReplyUsersList) ois.readObject();
			System.out.println();
			if (rus.getText() != null) {
				JOptionPane.showMessageDialog(null, rus.getText(), "Błąd",
						JOptionPane.ERROR_MESSAGE);
			}
			return rus.getUsersList();
		} catch (IOException e) {
			JOptionPane
					.showMessageDialog(
							null,
							"Wystąpił błąd podczas pobierania danych: "
									+ e.getMessage(), "Bład",
							JOptionPane.ERROR_MESSAGE);
		} catch (ClassNotFoundException e) {
			JOptionPane.showMessageDialog(null,
					"Wystąpił błąd podczas pobierania danych: ", "Bład",
					JOptionPane.ERROR_MESSAGE);
		}
		return null;
	}

Cały kod z catchami powyżej. Jakoś inaczej to da się zsynchronizować? Bo wszystko z printlanami działa prawidłowo.
Edit: Tak mi się przypomniało, że zapomniałem pododawać synchronized przy metodach, ale czy to ma wpływ w tym przypadku? I tak uruchamiałem 1 klienta.

0

Spróbuj to odpalic pod debugerem i jak sie "zawiesi" to dać pause w debugerze i zobaczysz gdzie program stoi.

0

Program działa na takiej zasadzie. Klient podłącza się do serwera i to działa ok:

U klienta klikamy na przycisk użytkownicy i tworzy się nowy JPanel, który wywołuje metodę

usersList();

z klasy receiver.

public class UsersJPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	private List<User> usersList;
	private ClientJFrame cf;

	public UsersJPanel(ClientJFrame cf) {

		this.cf = cf;
		setLayout(null);
		setBounds(200, 150, 694, 472);
		refresh();
	}
	
	public void refresh() {
		usersList = cf.getRec().usersList();
		/*Rysowanie tabeli i przycisko */
	}

Metoda usersList z klasy receiver wysyła zadanie czyli "Lista uzytkownikow" oraz obiekt, w tym przypadku nullObject bo nic nie wysyłamy do serwera.

	synchronized public List<User> usersList() {
		writer.println("Lista uzytkownikow");
		writer.flush();
		System.out.println();
		try {
			NullObject ol = new NullObject();
			oos.writeObject(ol);
			oos.flush();
			System.out.println();
			ReplyUsersList rul = (ReplyUsersList) ois.readObject();
			System.out.println();
			if (rul.getText() != null) {
				JOptionPane.showMessageDialog(null, rul.getText(), "Błąd",
						JOptionPane.ERROR_MESSAGE);
			}
			return rul.getUsersList();
		} catch (IOException e) {
			JOptionPane
					.showMessageDialog(
							null,
							"Wystąpił błąd podczas pobierania danych: "
									+ e.getMessage(), "Bład",
							JOptionPane.ERROR_MESSAGE);
		} catch (ClassNotFoundException e) {
			JOptionPane.showMessageDialog(null,
					"Wystąpił błąd podczas pobierania danych: ", "Bład",
					JOptionPane.ERROR_MESSAGE);
		}
		return null;
	}

W tym czasie na serwer czeka na wiadomość od klienta

	public void run() {
		ObjectInputStream ois = null;
		BufferedReader reader = null;
		ObjectOutputStream oos = null;
		PrintWriter writer = null;
		String command;
		initalizeAction();
		try {
			writer = new PrintWriter(socket.getOutputStream());
			oos = new ObjectOutputStream(socket.getOutputStream());
			reader = new BufferedReader(new InputStreamReader(
					socket.getInputStream()));
			ois = new ObjectInputStream(socket.getInputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			while((command = reader.readLine()) != null) {
				System.out.println(command);
				SendObjectInterface soi = (SendObjectInterface) ois.readObject();
				actionMap.get(command).action(soi, oos);
			}
		} catch (IOException e) {
			JOptionPane.showMessageDialog(null,
					"Błąd podczas odbioru wiadomości: " + e.getMessage(),
					"Błąd", JOptionPane.ERROR_MESSAGE);
		} catch (ClassNotFoundException e) {
			JOptionPane.showMessageDialog(null,
					"Błąd podczas odbioru wiadomości: " + e.getMessage(),
					"Błąd", JOptionPane.ERROR_MESSAGE);
		} finally {
			try {
				ois.close();
				oos.close();
				reader.close();
				writer.close();
			} catch (IOException e) {
				JOptionPane.showMessageDialog(null,
						"Błąd podczas zamykania strumienia: " + e.getMessage(),
						"Błąd", JOptionPane.ERROR_MESSAGE);
			}
		}
	}

W pętli while odbiera komendę co ma zrobić i obiekt. Po czym wywołuje odpowiednią metodę z klasy, w tym przypadku UsersListAction

	@Override
	synchronized  public void action(SendObjectInterface soi, ObjectOutputStream oos) {
		String sql = "Select * from users";
		ReplyUsersList rul = new ReplyUsersList();
		try {
			db = Database.getDB();
			sta = db.getCon().prepareStatement(sql);
			rs = sta.executeQuery();
			while (rs.next()) {
				rul.getUsersList().add(
						new User(rs.getInt(1), rs.getString(2),
								rs.getString(3), rs.getString(4)));
			}
			oos.writeObject(rul);
			oos.flush();
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, "Blad sql " + e.getMessage(),
					"Błąd", JOptionPane.ERROR_MESSAGE);
			rul.setText("Błąd pobierania danych z serwera");
		} catch (IOException e) {
			JOptionPane.showMessageDialog(null,
					"Blad wysyłania wiadomości na serwer" + e.getMessage(),
					"Błąd", JOptionPane.ERROR_MESSAGE);
		}
	}

Metoda wypełnia obiekt z listą użytkowników i wysyła do serwera, wtedy w klasie receiver powinno to być odebebrane:

ReplyUsersList rul = (ReplyUsersList) ois.readObject();

Klikam kilkakrotnie przycisk użytkownicy i wszystko ok, tylko po paru razach się zwiesza. Zauważyłem, że problem znajduje się tutaj:

			while((command = reader.readLine()) != null) {
				System.out.println(command);
				SendObjectInterface soi = (SendObjectInterface) ois.readObject();
				actionMap.get(command).action(soi, oos);
			}

Serwer odczytuje komendę a potem obiekt, ale po paru razach komenda jest odczytywana a obiekt już nie i się zawiesza.

0

Ten kod to jest ogólnie trochę masakra. Nie jest to może najlepsze rozwiąznie, ale nie myślałeś żeby użyc tutaj RMI?

0
Shalom napisał(a):

Ten kod to jest ogólnie trochę masakra. Nie jest to może najlepsze rozwiązanie, ale nie myślałeś żeby użyc tutaj RMI?

Nie, nie znam się na tym. Ale dlaczego to nie działa po paru razach? Co może być tego przyczyną?

0

A ten socket to TCP? UDP? Jak UDP to kolejność wiadomości moze się zmienić i ci się program posypie no nie będzie mógł wczytać obiektu jako stringa. Powodów moze być sporo, a kod jest na tyle nieczytelny ze trudno go analizować.

0

TCP:

	private void goServer() {
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader("serverSettings.txt"));
			serverSocket = new ServerSocket(Integer.parseInt(br.readLine()));
			while (isRun) {
				Socket clientSocket = serverSocket.accept();
				new Thread(new MiniServer(clientSocket)).start();
			}
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null, "Serwer zostal zamknięty",
					"Informacja", JOptionPane.INFORMATION_MESSAGE);
		} finally {
			try {
				if (br != null) {
					br.close();
				}
			} catch (IOException e) {
				JOptionPane.showMessageDialog(null,
						"Wystąpił błąd podczas zamykania strumienia plikowego: " + e.getMessage(),
						"Błąd", JOptionPane.ERROR_MESSAGE);
			}
			try {
				if (serverSocket != null) {
					serverSocket.close();
				}
			} catch (IOException e) {
				JOptionPane.showMessageDialog(null,
						"Błąd podczas zamykania serwera: " + e.getMessage(),
						"Błąd", JOptionPane.ERROR_MESSAGE);
			}
		}
	}

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