Strumienie, gniazda sieciowe i wątki - serwer www (zestaw I)
Dobrą ilustracją metod programowania dla platformy Java w zakresie gniazd sieciowych, strumieni danych i wielowątkowości jest implementacja prostego serwera http. Protokół http jest opisany w odpowiednim dokumencie RFC 2616 (http 1.1). Nasza implementacja będzie wykorzystywała strumienie znakowe, do pobierania danych przesyłanych przez przeglądarkę do serwera, a także strumieni bajtowych - do przesyłania żądanych dokumentów (pamiętaj że oprócz tekstu html, Twój serwer będzie także wysyłał dane binarne - grafikę, dźwięk, czy bajtkody apletów).
ćw. 1
Przypomnij sobie podstawowe koncepcje związane z protokołem http, czyli ze sposobem w jaki przeglądarka internetowa komunikuje się z serwerem www. Przejrzyj i zapoznaj się ze strukturą odpowiednich dokumentów RFC, opisujących protokoły http i https.
ćw. 2
Poniżej znajdziesz prostą implementację serwera http w języku Java. Przeanalizuj dokładnie przykład, zestaw odpowiednią jednostkę kompilacji (pakiet, pliki źródłowe itd), a następnie skompiluj i przetestuj przykład. Oczywiście, klientem serwera będzie przeglądarka internetowa.
import java.io.*; import java.net.*;
public class SerwerHTTP { public static void main(String[] args) throws IOException { ServerSocket serv=new ServerSocket(80); while(true) { //przyjecie polaczenia System.out.println("Oczekiwanie na polaczenie..."); Socket sock=serv.accept(); //strumienie danych InputStream is=sock.getInputStream(); OutputStream os=sock.getOutputStream(); BufferedReader inp=new BufferedReader(new InputStreamReader(is)); DataOutputStream outp=new DataOutputStream(os); //przyjecie zadania (request) String request=inp.readLine(); //wyslanie odpowiedzi (response) if(request.startsWith("GET")) { //response header outp.writeBytes("HTTP/1.0 200 OK\r\n"); outp.writeBytes("Content-Type: text/html\r\n"); outp.writeBytes("Content-Length: \r\n"); outp.writeBytes("\r\n"); //response body outp.writeBytes("<html>\r\n"); outp.writeBytes("<H1>Strona testowa</H1>\r\n"); outp.writeBytes("</html>\r\n"); } else { outp.writeBytes("HTTP/1.1 501 Not supported.\r\n"); } //zamykanie strumieni inp.close(); outp.close(); sock.close(); } } } |
Uwaga:
W powyższym przykładzie, w zasadzie wystarczyłoby użycie strumieni InputStream i OutputStream. Wyspecjalizowane strumienie BufferedReader i DataOutputStream zostały użyte ze względu na wygodę. BufferedReader posiada funkcjonalność (metody) umożliwiającą wygodne operowanie na strumieniach danych tekstowych (request). DataOutputStream posiada funkcjonalność umożliwiającą wygodne operowanie na strumieniach binarnych (response). Wysyłanie tekstu do przeglądarki bezpośrednio za pośrednictwem obiektu OutputStream wymagałoby użycia metody getBytes() i zapisania pobranej tablicy bajtów za pomocą metody write(byte[] bajty).
ćw. 3
Napisz fragment kodu który wydrukuje do standardowego wyjścia żądanie (request), który przeglądarka wysyła do serwera. Przeanalizuj jego treść. W powyższym przykładzie do zmiennej request zapisywany jest tylko pierwszy wiersz żądania (w ogólności może ich być więcej). Napisz fragment kodu, który wydrukuje do standardowego wyjścia komplet informacji przesłanych przez przeglądarkę.
ćw. 4
Napisz fragment kodu który ze zmiennej (tekstowej) request pobierze nazwę pliku, o który prosi przeglądarka i wydrukuje tę nazwę do standardowego wyjścia. Możesz w tym celu użyć metod klasy java.lang.String.
ćw. 5
Napisz odpowiedni fragment kodu który utworzy obiekt java.io.FileInputStream dla pliku o podanej nazwie. Jeżeli taki plik nie istnieje, Twój serwer powinien wysłać do przeglądarki odpowiednią informację poprzedzoną nagłówkiem: "HTTP/1.0 404 Not Found".
ćw. 6
Za pomocą odpowiednich metody int available() klasy java.io.FileInputStream pobierz informację o ilości bajtów w pliku i wykorzystaj ją do zdefiniowania nagłówka Content-Length. Odpowiedni nagłówek Content-Type zdefiniuj na podstawie rozszerzenia pliku - dla plików html i htm: "Content-Type: text/html\r\n", dla pozostałych plików: "Content-Type: \r\n".
ćw. 7
Utwórz bufor (tablicę bajtów o rozmiarze np. 1024), który posłuży do przechowywania bajtów pobieranych z pliku, przed wysłaniem ich do przeglądarki. Napisz odpowiedni fragment kodu, który będzie pobierał bajty z pliku do bufora, a następnie wysyłał zawartość bufora do przeglądarki która przysłała żądanie. Możesz to zrealizować np. wg. następującego schematu:
FileInputStream fis = new FileInputStream(nazwaPliku);
byte[] bufor;
bufor=new byte[1024];
int n=0;
while ((n = fis.read(bufor)) != -1 )
{
outp.write(buffer, 0, n);
}