Lekce 19 - Java chat - Klient - Spojení se serverem 3. část
V minulé lekci, Java chat - Klient - Spojení se serverem 2. část, jsme se věnovali implementaci klientského komunikátoru.
V dnešním Java tutoriálu konečně vytvoříme stabilní spojení se serverem.
Upravení připojovacího kontroleru
Začneme upravením třídy ConnectController
. Ve třídě
vytvoříme novou instanční proměnnou typu
IClientCommunicationService
. Rovnou této proměnné vytvoříme
setter, abychom mohli komunikátor nastavit:
public void setCommunicator(IClientCommunicationService communicator) { this.communicator = communicator; final BooleanBinding connected = Bindings.createBooleanBinding(() -> this.communicator.getConnectionState() == ConnectionState.CONNECTED, this.communicator.connectionStateProperty()); btnConnect.disableProperty().bind(connected.or(txtServer.textProperty().isEmpty())); btnDisconnect.disableProperty().bind(connected.not()); lblConnectedTo.textProperty().bind(this.communicator.connectedServerNameProperty()); }
Komunikátor musíme takto nastavovat, protože nemáme v projektu
implementovanou žádnou správu závislostí, jako tomu bylo u serveru. Dále v
setteru vytváříme BooleanProperty
connected
, na kterou bindujeme disableProperty
tlačítek pro připojení a odpojení. disableProperty
tlačítka
"připojit" má trošku složitější logiku, protože se musíme ještě
dívat, zda-li se máme k čemu připojit (je vyplněn TextField
se
serverem).
Dále upravíme metodu initialize()
, do které přidáme reakci
na výběr položky z listView
:
lvServers.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { if (newValue == null) { return; } txtServer.textProperty().set(String.format("%s:%d", newValue.getServerAddress().getHostAddress(), newValue.getPort())); });
Voláním metod getSelectionModel().selectedItemProperty()
získáme pozorovatelnou read-only property, na kterou přidáme listener,
který se zavolá vždy, když změníme výběr položky v
listView
. Pokud bude nová hodnota null
, tak nic
dělat nebudeme, v opačném případě vyplníme TextField
txtServer
hodnotou ve formátu: "server:port".
Nyní vytvoříme nejdůležitější metodu v kontroleru,
connect()
, pomocí které se budeme připojovat na server:
private void connect() { final String hostPort = txtServer.textProperty().get(); final String host = hostPort.substring(0, hostPort.indexOf(":")); final String portRaw = hostPort.substring(hostPort.indexOf(":") + 1); int port; try { port = Integer.parseInt(portRaw); } catch (Exception ex) { Alert alert = new Alert(AlertType.ERROR); alert.setHeaderText("Chyba"); alert.setContentText("Port serveru se nezdařilo naparsovat."); alert.showAndWait(); return; } this.communicator.connect(host, port) .exceptionally(throwable -> { Alert alert = new Alert(AlertType.ERROR); alert.setHeaderText("Chyba"); alert.setContentText("Připojení k serveru se nezdařilo."); alert.showAndWait(); throw new RuntimeException(throwable); }) .thenAccept(ignored -> { Alert alert = new Alert(AlertType.INFORMATION); alert.setHeaderText("Informace"); alert.setContentText("Spojení bylo úspěšně navázáno."); alert.showAndWait(); }); }
V první části metody parsujeme z TextField
u adresu serveru a
port, na kterém server naslouchá. Pokud se nepodaří naparsovat port serveru,
zobrazíme upozornění uživateli, že port není ve validním stavu.
Následuje zavolání metody connect()
nad instancí našeho
komunikátoru. Metodou exceptionaly()
ošetříme případ, kdy se
spojení nepodařilo navázat. Když se jednou dostaneme do větve
exceptionaly()
, můžeme v ní zůstat tak, že opět vyhodíme
nějakou výjimku, nebo vrátíme smysluplnou hodnotu, čímž se dostaneme do
výchozí větve. Větev thenAccept()
je zavolána v případě,
že spojení se úspěšně vytvořilo.
Nakonec nám už jenom zbývá nastavit odpovídajícím tlačítkům správnou akci:
@FXML private void handleConnect(ActionEvent actionEvent) { connect(); } @FXML private void handleDisconnect(ActionEvent actionEvent) { communicator.disconnect(); }
Upravení hlavního kontroleru
Nyní se přesuneme do hlavního kontroleru, ve kterém budeme držet jedinou
instanci komunikátoru. Vytvoříme tedy ve třídě MainController
instanční konstantu komunikátoru:
private final IClientCommunicationService communicator = new ClientCommunicationService();
Dále upravíme metodu handleConnect()
, ve které voláme
zobrazení okna:
@FXML private void handleConnect(ActionEvent actionEvent) { try { final ConnectController controller = showNewWindow("connect/connect", "Připojit k serveru..."); controller.setCommunicator(communicator); } catch (IOException e) { e.printStackTrace(); } }
Metoda showNewWindow()
vrací kontroler nového okna. Tento
kontroler si uložíme do lokální proměnné a metodou
setCommunicator()
nastavíme kontroleru komunikátor.
Ještě využijeme metodu onClose()
, která se zavolá při
zavření okna. V této metodě se pro jistotu odpojíme od serveru. Tím
bezpečně ukončíme spojení a veškerá vlákna s ním spojená:
@Override public void onClose() { communicator.disconnect() }
Testování funkčnosti
Konečně se můžeme připojit k serveru. Z předchozí lekce máme
funkční vyhledávání lokálních serverů, tak toho hned využijeme.
Spustíme server a klienta. Dále zobrazíme okno pro připojení. Když se
zobrazí v seznamu náš spuštěný server, klikneme na tuto položku. V té
chvíli by se měl vyplnit TextField
s adresou serveru a portem a
tlačítko na připojení by mělo být aktivní. Po kliknutí na tlačítko by
se měl zobrazit dialog, že připojení proběhlo úspěšně a tlačítko pro
připojení by opět nemělo být aktivní. Naopak tlačítko pro připojení by
se mělo aktivovat. Že jste se opravdu připojili poznáte nejlépe tak, že se
změní u připojeného serveru počet připojených uživatelů:
To by bylo pro dnešní lekci vše.
V následujícím kvízu, Kvíz - Spojení klienta se serverem v Javě, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 15x (121.04 kB)
Aplikace je včetně zdrojových kódů v jazyce Java