wtorek, 21 czerwca 2011

Socket - Połączenie klient-serwer

Dzisiaj zajmiemy się połączeniem pomiędzy klientem i serwerem przy użyciu klasy Socket. Klasa Socket pozwala na połączenie sieciowe na podanym porcie pomiędzy dwoma maszynami.

Socket (Java Platform SE 6)

Krótko o działaniu aplikacji. Po uruchomieniu serwer oczekuje na połączenie. Po połączeniu serwer wysyła do klienta komunikat "Ping" i oczekuje na odpowiedź. Jeśli klient odpowie "Pong" to serwer po 5 sekundach ponownie wysyła "Ping". W przypadku otrzymania złej odpowiedzi serwer się wyłącza.

Klient sprawdza nadchodzące komunikaty, jeśli otrzyma "Ping" to odpowiada "Pong". Dla celów testowych klient po 30 sekundach działania wyśle komunikat "Pon g" zamiast "Pong" i się wyłączy.

Linijki potrzebne do działania po stronie serwera:
/*Utworzenie na podanym porcie socketa serwera.*/
ServerSocket serverSocket = new ServerSocket(PORT);

/*Oczekiwanie na połączenie i zaakceptowanie*/
Socket clientSocket = serverSocket.accept();

/*Utworzenie strumieni wejściowego i wyjściowego*/
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

/*Wysłanie komunikatu*/
out.println(KOMUNIKAT);

/*Oczekiwanie i odebranie komunikatu*/
String input = in.readLine();

/*Zamknięcie strumieniu i socketów*/
out.close();
in.close();
clientSocket.close();
serverSocket.close();

Linijki potrzebne do działania po stronie klienta:
/*Połączenie z serwerem*/
Socket socket = new Socket(HOST, PORT);

/*Reszta analogicznie jak w serwerze*/


Kod źródłowy:
/* Server.java */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server
{
    private static final int PORT = 50000;
    static boolean flaga = true;
    
    private static ServerSocket serverSocket;
    private static Socket clientSocket;
    
    public static void main(String[] args) throws IOException
    {
        serverSocket = null;
        try
        {
            serverSocket = new ServerSocket(PORT);
        }
        catch(IOException e)
        {
            System.err.println("Could not listen on port: "+PORT);
            System.exit(1);
        }
        
        System.out.print("Wating for connection...");
        
        Thread t = new Thread(new Runnable()
        {
            public void run()
            {
                try
                {
                    while(flaga)
                    {
                        System.out.print(".");
                        Thread.sleep(1000);
                    }
                }
                catch(InterruptedException ie)
                {
                    //
                }
                
                System.out.println("\nClient connected on port "+PORT);
            }
        });
        t.start();
        
        clientSocket = null;
        try
        {
            clientSocket = serverSocket.accept();
            flaga = false;
        }
        catch(IOException e)
        {
            System.err.println("Accept failed.");
            t.interrupt();
            System.exit(1);
        }
        
        final PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),true);
        final BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        
        t = new Thread(new Runnable()
        {
            public void run()
            {
                try
                {
                    Thread.sleep(5000);
                    
                    while(true)
                    {
                        out.println("Ping");
                        System.out.println(System.currentTimeMillis()+" Ping sent");
                        
                        String input = in.readLine();
                        
                        if(input.equals("Pong"))
                        {
                            System.out.println(System.currentTimeMillis()+" Pong received");
                        }
                        else
                        {
                            System.out.println(System.currentTimeMillis()+" Wrong answer");
                            
                            out.close();
                            in.close();
                            clientSocket.close();
                            serverSocket.close();
                            break;
                        }
                        
                        
                        Thread.sleep(5000);
                    }
                }
                catch(Exception e)
                {
                    System.err.println(System.currentTimeMillis()+" Unexpected Error");
                }
            }
        });
        t.start();
    }
}

/* Client.java */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Client
{
    private static final int PORT = 50000;
    private static final String HOST = "localhost";
    
    public static void main(String[] args) throws IOException
    {
        Socket socket = null;
        
        try
        {
            socket = new Socket(HOST, PORT);
        }
        catch(Exception e)
        {
            System.err.println("Could not connect to "+HOST+":"+PORT);
            System.exit(1);
        }
        
        final PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
        final BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        
        Thread t = new Thread(new Runnable()
        {
            public void run()
            {
                long start = System.currentTimeMillis();
                
                while (true)
                {
                    try
                    {
                        String input = in.readLine();

                        if (input != null)
                        {
                            System.out.println(System.currentTimeMillis() + " Server: " + input);
                        }

                        if (input.equals("Ping"))
                        {
                            if(System.currentTimeMillis()-start>30000)
                            {
                                out.println("Pon g");
                                System.out.println(System.currentTimeMillis() + " Client: Pon g");
                                break;
                            }
                            
                            out.println("Pong");
                            System.out.println(System.currentTimeMillis() + " Client: Pong");
                        }
                    }
                    catch (IOException ioe)
                    {
                        //
                    }
                }
            }
        });
        t.start();
        
        out.close();
        in.close();
        socket.close();
    }
}

Output serwera:
Wating for connection..........
Client connected on port 50000
1308645130778 Ping sent
1308645130779 Pong received
1308645135779 Ping sent
1308645135779 Pong received
1308645140780 Ping sent
1308645140780 Pong received
1308645145780 Ping sent
1308645145780 Pong received
1308645150780 Ping sent
1308645150780 Pong received
1308645155781 Ping sent
1308645155781 Wrong answer

Output klienta:
1308645130778 Server: Ping
1308645130779 Client: Pong
1308645135779 Server: Ping
1308645135779 Client: Pong
1308645140780 Server: Ping
1308645140780 Client: Pong
1308645145780 Server: Ping
1308645145780 Client: Pong
1308645150780 Server: Ping
1308645150780 Client: Pong
1308645155781 Server: Ping
1308645155781 Client: Pon g

niedziela, 5 czerwca 2011

db4o - Prosta baza danych dla aplikacji java!

Ostatnio zastanawiałem się jak przechowywać dane pomiędzy uruchomieniami aplikacji. Szukając rozwiązań w internecie trafiłem na db4objects http://db4o.com/.

db4o jest bazą embedded przechowującą całe obiekty. Całość działa szybko i jest bardzo prosta w implementacji.

Potrzebne biblioteki pobieramy z tej strony:
http://db4o.com/DownloadNow.aspx -> db4o 8.0 for Java (zip, 40.9 mb).
Rejestracja jest opcjonalna.

Wg pliku \db4o-8.0-java\db4o-8.0\lib\readme.html wystarczy nam biblioteka db4o-8.0.184.15484-core-java5.jar ale do projektu dodajemy również db4o-8.0.184.15484-cs-java5.jar ponieważ wg API (dołączone do paczki) potrzebna nam metoda w klasie Db4o jest zdeprecjonowana.


Oto krótki przykład zastosowania:
/* Main.java */
import com.db4o.ObjectContainer;
import com.db4o.ObjectServer;
import com.db4o.cs.Db4oClientServer;
import com.db4o.query.Predicate;
import java.util.List;

public class Main
{
    static ObjectServer os;
    
    public static void main(String[] args)
    {
        os = Db4oClientServer.openServer("TestDB", 0);
        
        saveToDB();
        loadFromDB();
        
        os.close();
    }
    
    public static void saveToDB()
    {
        ObjectContainer oc = os.openClient();
        oc.store(new SomeObject());
        oc.close();
    }
    
    public static void loadFromDB()
    {
        ObjectContainer oc = os.openClient();
        
        List list = oc.query(new Predicate()
                {
                    public boolean match(SomeObject so)
                    {
                        return so.getClass().getName().equals("SomeObject");
                    }
                });
        
        for(SomeObject so : list)
        {
            System.out.println(so.x+" "+so.y);
        }
        
        oc.close();
    }
}

/* SomeObject.java */
import java.util.Random;

public class SomeObject
{
    public int x;
    public int y;
    
    public SomeObject()
    {
        Random r = new Random();
        
        x = r.nextInt(10);
        y = r.nextInt(100);
    }
}

Wynik 3-krotnego uruchomienia aplikacji, widać że dane są zachowywane:
run:
6 58
3 42
6 22
BUILD SUCCESSFUL (total time: 1 second)

Jak widać potrzebne obsługa bazy jest banalnie prosta, potrzebne linie to:

ObjectServer os = Db4oClientServer.openServer("TestDB", 0); //otworzenie serwera

ObjectContainer oc = os.openClient(); //otworzenie klienta

oc.store(new SomeObject()); //zapisanie obiektu do bazy

//pobranie obiektów SomeObject z bazy
List list = oc.query(new Predicate()
                {
                    public boolean match(SomeObject so)
                    {
                        return so.getClass().getName().equals("SomeObject");
                    }
                });

oc.close(); //zamknięcie klienta

os.close(); //zamknięcie serwera

Metoda openServer() w razie potrzeby utworzy nowy serwer pod podaną nazwą, a jeśli chcemy aby był to serwer TCP zamiast 0 podajemy numer portu pod jakim ma działać.

Pokazane tutaj zastosowanie jest najprostszym z możliwych ale baza ta jest o wiele bardziej rozbudowana.