IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

IPC. Inter-Process Communications

Aplicacions Distribuides

Cal entendre que un sistema distribuït no implica necessàriament una col·laboració entre màquines diferents sinó que pot ser entre processos diferents (IPC):

  • Cues de missatges
  • Memòria compartida
  • Semàfors
  • TCP/UDP sockets
  • Unix Domain Sockets
  • XML XML-RPC or SOAP
  • Internet Communications Engine (ICE) (C++), DCOP a KDE...

Bàsicament veurem exemples d'arquitectura client-servidor però sense obviar altres arquitectures distribuïdes com Peer To Peer (P2P).

Nivel 4 OSI. Nivell de transport

Hugolucas OSI.jpeg

Funcions

Orientat a connexió (Connection-oriented):

La capa de xarxa(nivell 3 OSI) no proveeix de connexió. TCP introdueix el concepte de connexió a la capa de transport.

Ordre de lliurament (Same Order Delivery)

La capa de xarxa no garanteix que els paquets arribin en el mateix ordre que han sortit de l'emissor. La forma més senzilla és numerar els paquets i reordenar-los al receptor.

Dades fiables (Reliable data)

Els paquets es poden perdre per la xarxa per problemes de congestió, errors, interferències, etc. S'utilitzen tècniques com la detecció de paquets corruptes (checksums) i la retransmissió d'errors.

IMPORTANT: les transmissions 100% sense errors no existeixen el que s'intenta és que hi hagi el mínim d'errors no detectats.

Control de flux (Flow Control):

La memòria dels dispositius de xarxa és limitada i sense un control de flux un dispositiu amb una alta capacitat de transmissió pot saturar fàcilment un dispositiu més lent i amb poca memòria. Actualment això no es gaire problema ja que, les memòries són molt més barates que l'ample de banda.

Control de la congestió (Congestion avoidance):

Quan els paquets es perden es tornen a retransmetre. Si no tenim un control de congestió que permeti parar la transmissió durant una estona, el reenviament automàtic i immediat dels errors pot empitjorar el problema.

Orientació a bytes (Byte orientation):

En comptes de treballar a nivell de paquets, la capa de transport permet treballar a nivell de bytes. La comunicació és un stream de bytes. Facilita la programació, ja que permet que una connexió de xarxa es pugui tractar igual que les comunicacions d'entrada/sortida (fitxers, dispositius, etc.)

Ports

Els ports són una forma essencial de permetre múltiples serveis en una mateixa localització (Adreça IP).

NOTA: Els ports són part de la capa de transport en el model TCP/IP, però formen part de la capa de sessió al model OSI.

TCP vs UDP

Els dos protocols de nivell de transport més utilitzats són TCP i UDP.

TCP és més fiable però més lent. S'utilitza en comunicacions on la integritat de les dades és vital (per exemple la transferència de fitxers).

UDP és menys fiable però més ràpid (aprox. 40% ). S'utilitza en aplicacions on la velocitat és important i ens podem permetrà la pèrdua d'algunes dades (P. ex. serveis en temps real com la telefonia IP o videoconferència).

TCP UDP
Mida de la capçalera 20 bytes 8 Bytes
Unitat de dades Segment Paquet
Control d'errors Si Si
Ports Si Si
Orientat a connexió Si No
Control de flux Si No
Control de congestió Si No
Numeració de segments Si No
Automatic Repeat Request Si No

Protocols orientats a connexió. TCP

Connection-oriented

Els dispositius de cada banda de la connexió (emissor i receptor) utilitzen un protocol preliminar a l'enviament de dades per establir una connexió punta a punta. Sovint també s'anomenen serveis de xarxa fiables (reliable) per què es garanteix que les dades arribaren en l'ordre adequat.

La comunicació pot estar en diferents estats

La comunicació es duu a terme en tres fases:

Fase 1: Establiment de la connexió

Fase 2: Transmissió de dades

Fase 3: Tancament de la connexió:

Encaixada de mans TCP (Handshake)

Hugolucas handshake.jpeg

Programació xarxes Android

Sockets

Servidor:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;


public class MyServer {
 
 public static void main(String[] args){
  ServerSocket serverSocket = null;
  Socket socket = null;
  DataInputStream dataInputStream = null;
  DataOutputStream dataOutputStream = null;
  
  try {
   serverSocket = new ServerSocket(8888);
   System.out.println("Listening :8888");
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  while(true){
   try {
    socket = serverSocket.accept();
    dataInputStream = new DataInputStream(socket.getInputStream());
    dataOutputStream = new DataOutputStream(socket.getOutputStream());
    System.out.println("ip: " + socket.getInetAddress());
    System.out.println("message: " + dataInputStream.readUTF());
    dataOutputStream.writeUTF("Hello!");
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   finally{
    if( socket!= null){
     try {
      socket.close();
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
    
    if( dataInputStream!= null){
     try {
      dataInputStream.close();
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
    
    if( dataOutputStream!= null){
     try {
      dataOutputStream.close();
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
   }
  }
 }
}

Client

IMPORTANT: Per evitar l'error NetworkOnMainThreadException desactiveu l'strictMode [1]

if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = 
        new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
package com.exercise.AndroidClient;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class AndroidClient extends Activity {

EditText textOut;
TextView textIn;

 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
  
     textOut = (EditText)findViewById(R.id.textout);
     Button buttonSend = (Button)findViewById(R.id.send);
     textIn = (TextView)findViewById(R.id.textin);
     buttonSend.setOnClickListener(buttonSendOnClickListener);
 }

 Button.OnClickListener buttonSendOnClickListener
 = new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
 // TODO Auto-generated method stub
 Socket socket = null;
 DataOutputStream dataOutputStream = null;
 DataInputStream dataInputStream = null;

 try {
  socket = new Socket("192.168.1.101", 8888);
  dataOutputStream = new DataOutputStream(socket.getOutputStream());
  dataInputStream = new DataInputStream(socket.getInputStream());
  dataOutputStream.writeUTF(textOut.getText().toString());
  textIn.setText(dataInputStream.readUTF());
 } catch (UnknownHostException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
 finally{
  if (socket != null){
   try {
    socket.close();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }

  if (dataOutputStream != null){
   try {
    dataOutputStream.close();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }

  if (dataInputStream != null){
   try {
    dataInputStream.close();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }
}};
}

El layout Android és el següent:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 >
<TextView
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="@string/hello"
 />
<EditText
 android:id="@+id/textout"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 />
<Button
 android:id="@+id/send"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Send"
 />
<TextView
 android:id="@+id/textin"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 />
</LinearLayout>


Also, you have to grand permission for the App to access internet, by adding the code in AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>

Java Sockets

Datagrames UDP

java.net.DatagramSocket

Aquesta clase Java representa un socket que permet enviar paquets de tipus datagrama, típicament utilitzant el protocol UDP.

Quan sigui possible, un objecte acabat de crear de tipus DatagramSocket té l'opció SO_BROADCAST activada i per tant pot rebre datagrames de broadcast.

Exemple:

DatagramSocket s = new DatagramSocket(null); 
s.bind(new InetSocketAddress(8888)); 

És equivalent a.

DatagramSocket s = new DatagramSocket(8888); 

Els dos casos crearan un DatagramSocket capaç de rebre broadcasts UDP al port 8888

DatagramSocket java.net.DatagramPacket

java.net.DatagramPacket

Els paquets de tipus datagrama s'utilitzen per implementar un servei de tipus sense connexió. Cada missatge s'encamina de forma independent a la resta i nomes s'utilitza la informació que porta inclosa el propi missatge. Múltiples paquets enviats del mateix tipus d'una màquina a un altre poden ser encaminats de forma diferentm i poden arrivar en diferent ordre (és a dir en diferent ordre respecte a l'ordre de sortida). A més no es garateix l'entrega del paquet.

DatagramPacket java.net.DatagramPacket

Estructura típica del codi

Servidor

Primer cal declarar el socket:

 protected DatagramSocket socket = null;

Típicament es pot definir com un atribut de la clase servidor.

Fora del bucle infinit (si implementem el servidor com a un servei) cal crear el socket al constructor de la classe:

socket = new DatagramSocket(4445);

A l'exemple creem un socket UDP al port 4445. El socket s'ha de crear una sola vegada per tant es pot crear al constructor de la clase servidor.

Ara es tracta d'estar rebent continuament paquets de tipus datagrama pel port 4445, per tant dins d'un while infinit:

 while (1) {
            try {
                byte[] buf = new byte[256];
 
                // receive request
                DatagramPacket packet = new DatagramPacket(buf, buf.length);
                socket.receive(packet);
 
                //Aquí el codi amb el que treballarem el packet rebut
                // Com es poden obtenir dades:
                // IP:   InetAddress address = packet.getAddress();
                // Port: int port = packet.getPort();
                // Dades: Exemple String: String received = new String(packet.getData(), 0, packet.getLength()); 
        }
        socket.close();
 

On

 byte[] buf = new byte[256];
 

És la mida màxima del paquet en bytes que esperem. Ha de ser prou gran per que no es tallin els nostres paquets.I:

                DatagramPacket packet = new DatagramPacket(buf, buf.length);
                socket.receive(packet);
 

És la recepció del paquet.

IMPORTANT: Observeu la importància d'alliberar recursos i tancar el socket amb close!

Client

Vegem un exemple d'una sola execució:

    public static void main(String[] args) throws IOException {
 
        DatagramSocket socket = new DatagramSocket();
 
        // send request
        byte[] buf = new byte[256];
        String stringToConvert = "Missatge de prova";        
        buf = stringToConvert.getBytes();
        InetAddress address = new InetAddress ("192.168.0.5")
        DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 4445);
        socket.send(packet);
  
        socket.close();
    }
}

IMPORTANT: Feu coincidir la mida dels dos buffers tal al costat del client com del servidor per evitar problemes.