Válí se ti projekty v šuplíku? Dostaň je mezi lidi a získej cool tričko a body na profi IT kurzy v soutěži ITnetwork summer 2017!
Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

Komunikace Klient/Server - 3. díl - Úprava serveru

Java Pro pokročilé Komunikace Klient/Server - 3. díl - Úprava serveru

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

Zdravím u poslední části této minisérie o socketech v Javě, ve které si vytváříme jednoduchou komunikaci klient/server. Dnes si server upravíme tak, aby se na něj mohlo připojit více klientů. Uděláme to pomocí vláken, takže vám doporučuji si nejdříve přečíst článek o Multithredingu v Javě, abyste pochopili, co to ta vlákna jsou, protože to zde nebudu nijak zvlášť rozebírat.

Nejdříve si vytvoříme novou privátní metodu s názvem clients.

private void clients() {

}

Třídě přidáme jeden privátní atribut ArrayList<Buf­feredReader> jménem clientBufReaders. Z konstruktoru odstraníme vše kromě inicializace serverSocketu a přidáme inicializaci clientBufReaders. Nakonec zavoláme naši novou metodu clients.

public Server() {
    try {
        this.serverSocket = new ServerSocket(8080);
        System.out.print("Spuštění serveru proběhlo úspěšně.\nČekám na připojení klienta...\n");
        this.clientBufReaders = new ArrayList<BufferedReader>();

        this.clients();
    } catch (IOException e ) {
        e.printStackTrace();
    }
}

V metodě clients si vytvoříme nové vlákno jménem acceptThread, kterému jako argument předáme rozhraní Runnable s metodou run(). V metodě run si vytvoříme zas nekonečný cyklus while.

Thread acceptThread = new Thread(new Runnable() {
       public void run() {
           while(true) {
          }
    }
});

V cyklu while si vytvoříme Socket pro klienta, jménem clientSocket. Do ArrayListu clientBufReaders si přidáme BufferedReader pro nově připojeného klienta. A vypíšeme hlášku a připojení klienta a nezapomeneme to celé zase obalit do bloku try – catch.

try {
        Socket clientSocket = serverSocket.accept();
        clientBufReaders.add(new BufferedReader(new InputStreamReader(clientSocket.getInputStream())));
        System.out.println("Klient se připojil.");
} catch (IOException e) {
        e.printStackTrace();
}

Mimo metodu run si zavoláme na acceptThread metodu start() pro spuštění nového vlákna, které jsme si vytvořili.

acceptThread.start();

Vytvoříme další nekonečný while cyklus a v něm sekci synchronized, abychom mohli z tohoto vlákna přistupovat do clientBufReaders a nedošlo k race condition. Bez synchronizace by byl problém kdyby k listu obě vlákna přistoupila ve stejnou chvíli, více o synchronizace vláken v sekci Vícevláknové aplikace v Javě.

while(true) {
        synchronized(clientBufReaders) {
        }
}

Teď už zbývá jen poslední věc a to vytvořit si for cyklus, kterým postupně proiterujeme ArrayList a pokud bude BufferedReader nějakého klienta obsahovat text, tak si ho vypíšeme.

for(BufferedReader in :  clientBufReaders) {
    try {
        if(in.ready()) {
            System.out.println(in.readLine());
        } else {
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Konečně kompletní zdrojový kód by měl vypadat takto:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class Server {

    private ServerSocket serverSocket;
    private ArrayList<BufferedReader> clientBufReaders;

    public static void main(String[] args) {
        Server server = new Server();
    }

    public Server() {
        try {
            this.serverSocket = new ServerSocket(8080);
            System.out.print("Spuštění serveru proběhlo úspěšně.\nČekám na připojení klienta...\n");
            this.clientBufReaders = new ArrayList<BufferedReader>();

            this.clients();
        } catch (IOException e ) {
            e.printStackTrace();
        }
    }

    private void clients() {
        Thread acceptThread = new Thread(new Runnable() {
            public void run() {
                while(true) {
                    try {
                        Socket clientSocket = serverSocket.accept();
                        clientBufReaders.add(new BufferedReader(new InputStreamReader(clientSocket.getInputStream())));
                        System.out.println("Klient se připojil.");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        acceptThread.start();

        while(true) {
            synchronized(clientBufReaders) {
                for(BufferedReader in :  clientBufReaders) {
                    try {
                        if(in.ready()) {
                            System.out.println(in.readLine());
                        } else {
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

A tohle je konec minisérie o socketech v Javě. Pod článkem máte samozřejmě celý zdrojový kód ke stažení.


 

Stáhnout

Staženo 110x (36.26 kB)
Aplikace je včetně zdrojových kódů v jazyce java

 

 

Článek pro vás napsal Filip Stryk
Avatar
Jak se ti líbí článek?
7 hlasů
Miniatura
Všechny články v sekci
Java - Pro pokročilé
Miniatura
Následující článek
HTTPS v Javě
Aktivity (1)

 

 

Komentáře

Avatar
vacullik
Člen
Avatar
vacullik:19.12.2014 2:02

Zdravím,
jedna připomínka, myslím, že by jsi měl synchronizovat i přidávání do kolekce clientBufReaders v metodě run(). Metoda add není samozřejmě atomická a v tomto případě je docela velká pravděpodobnost, že se zrovna při jejím vykonávání přepne kontext do main vlákna, které po připojení alespoň jednoho klienta téměř pořád do této kolekce přistupuje. Důsledek je, že se mi při přidání druhého klienta téměř vždy vyhodí výjimka o dvojím přístupu do kolekce.

Editováno 19.12.2014 2:04
 
Odpovědět 19.12.2014 2:02
Avatar
Freddy Krueger:28. března 12:44

Ahoj, můžete někdo poradit, jak by se postupovalo, aby mohl i server odesílat data klientům, jak všem, tak jen jednotlivým? Děkuji

 
Odpovědět 28. března 12:44
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Freddy Krueger
pocitac770:28. března 17:53

Jak v případě klienta, tak serveru pracuješ se Socket objecty, tzn. funguje přesně stejný postup, jako zde byl popsaný, tzn. Zde v kódu si nebudeš ukládat objekty BufferedReaderů, ale Socketů (nebo vlastních objektů, které budou představovat jednotlivé klienty, ve kterých bude uložen jak jejich socket, tak "input/output objekty"), které budeš používat na přijímání/odesílání zpráv. Pokud jde o jednotlivé klienty, tak si každého klienta při připojení nějak označíš, může ti stačit pouhý index v poli, ale může jít třeba o ID, nebo nějaaký nickname, záleží, jaký způsob ukládání dat o příchozích klientech zvolíš, viz výše. Pak jsi už jenom v nějaké své kolekci najdeš ten určitý klient a pouze tomu to odešleš. Na straně klienta vlastně uděláš to samé, tzn. si vezneš input, na kterém budeš číst stejně jako teď na serveru, a output, na který budeš posílat data

 
Odpovědět 28. března 17:53
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 3 zpráv z 3.