Programowanie gniazd sieciowych w języku JAVA ============================================= Nawiązywanie połączeń TCP (klient) ---------------------------------- Do nawiązywania połączeń TCP służy klasa ``java.net.Socket``. .. code-block:: java import java.io.*; import java.net.Socket; public class ClientSocket { public static void main(String[] args) throws Exception{ Socket socket = new Socket("google.pl", 80); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bufferedWriter.write("GET / HTTP/1.0\n\n"); bufferedWriter.flush(); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line = reader.readLine(); while (line!=null){ System.out.println(line); System.out.flush(); line = reader.readLine(); } } } Ważniejsze części programu: W konstruktorze socketa podajemy adres i port na który się łączymy: .. code-block:: java Socket socket = new Socket("google.pl", 80); Z socketem powiązane są dwa strumienie, które Państwo znacie z przedmiotu PO Java: * ``OutputStream``, pobierany za pomocą wywołania ``socket.getOutputStream``, służy do wysyłania danych do docelowego hosta * ``InputStream``, pobierany za pomocą wywołania ``socket.getInputStream``, służy do odbierania danych Strumienie te są strumieniami binarnymi (tj. przesyłającymi nie tekst a dane binarne). Ponieważ HTTP jest protokołem tekstowym (w zasadzie) opakowujemy te strumienie do klasy ``BufferedReader`` która pozwala wygodnie pracować na tekście: .. code-block:: java BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); Po wysłaniu linijki danych musimy wykonać polecenie ``flush`` które spowoduje wysłanie danych do hosta (implementacja TCP może czekać aż nie zbierze się dostateczna ilość danych by wysłać *pełny* pakiet: .. code-block:: java bufferedWriter.flush(); Zadanie 2 ---------------------------------- Uruchomić powyższy program w środowisku ``eclipse``. Polączyć się z serwerem ``www.wp.pl`` na porcie ``80``. Na ekran należy wypisać tylko te linijki które zawierają słowo ``sport``. Przyjmowanie połączeń TCP (serwer) ---------------------------------- Do tworzenia serwerów w javie służy klasa ``java.net.ServerSocket``. Ten program stworzy serwer odpisujący na to co mu się wysłało: .. code-block:: java import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class ServerSocketExample { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(12347); while (true){ Socket socket = serverSocket.accept(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bufferedWriter.write("Napisz: \"END\" by zakończyć połączenie."); String line = bufferedReader.readLine(); while (!line.contains("END")){ bufferedWriter.write("Sever says: "); bufferedWriter.write(line); bufferedWriter.write("\n"); bufferedWriter.flush(); line = bufferedReader.readLine(); } socket.close(); } } } Ciekawsze elementy programu: Tworzymy serwer który będzie akceptował połączenia na porcie ``12347``: .. code-block:: java ServerSocket serverSocket = new ServerSocket(12347); Wywołanie ``serverSocket.accept()`` jest **blokujące** tj. metoda ta zakończy się  w momencie w którym serwer otrzyma połączenie. Metoda ta zwraca zwykłego socketa pozwalającego odczytywać i zapisywać dane do zdalnego systemu. Zadanie 3 ---------------------------------- Uruchomić powyższy program w środowisku ``eclipse``. Przerobić program w taki sposób, by do tekstu wysyłanego przez użytkownika dopisywał ``!``. Należy przetestować wprowadzone zmiany za pomocą narzędzia ``telnet``. Proszę zasymulować połączenie się do powyższego programu dwóch różnych klientów (należy odpalić program ``telnet`` dwukrotnie, w różnych terminalach). Co się wtedy dzieje? Wielowątkowy serwer w Javie --------------------------- Serwer z poprzedniego przykładu jest jednowątkowy, tj. kiedy obsługuje jednego klienta nie akceptuje połączeń od innych klientów. Oczywiście jest to niepożądana cecha, w praktyce serwery oprogramowuje się  tak by każde połączenie było oprogramowane w oddzielnym wątku. Ponższy przykład jest w stanie obsłużyć wiele wątków: .. code-block:: java import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MultihreadedServerSocketExample { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(12347); ExecutorService executorService = Executors.newFixedThreadPool(10); while (true){ final Socket socket = serverSocket.accept(); Runnable connection = new Runnable() { @Override public void run() { try { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bufferedWriter.write("Napisz: \"END\" by zakończyć połączenie."); bufferedWriter.flush(); String line = bufferedReader.readLine(); while (!line.contains("END")){ bufferedWriter.write("Sever says: "); bufferedWriter.write(line); bufferedWriter.write("\n"); bufferedWriter.flush(); line = bufferedReader.readLine(); } socket.close(); } catch (IOException e) { e.printStackTrace(); } } }; executorService.submit(connection); } } } Informacje na temat przykładu: * Żeby ``socket`` był widoczny wewnątrz instancji ``Runnable`` musimy poprzedzić go modyfikatorem final. * Pojawił się ``ExecutorService`` * Połączenie obsługiwane jest nie w wątku main a w wątku zarządzanym przez ExecutorService. * Musimy obsłużyć błędy w połączeniu, bo interfejs Runnable nie pozwala na propagację wyjątków z metody run. Zadanie 4 ---------------------------------- Uruchomić powyższy program w środowisku ``eclipse``. Przy użyciu narzędzia ``telnet`` przetestować możliwość łączenia się wielu klientów do powyższego programu. Zadanie 5 ---------------------------------- Proszę w parach napisać prosty program typu "chat" do porozumiewania się pomiędzy dwoma użytkownikami.