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)

Aplicacions distribuïdes

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):

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

Nivell 4 OSI. Nivell de Transport

Andrescorazon OSIlv4 nivelldetransport.png

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. Atenció: 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 (aproximadament un 40%). S'utilitza en aplicacions on la velocitat és important i ens podem permetre la pèrdua d'algunes dades (per exemple, serveis en temps real com la telefonia IP o videoconferència).
Andrescorazon taula comparativa TCP UDP.png

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)

Andrescorazon Handshake TCP.png

Acabament de la connexió

Andrescorazon acabament connexio TCP.png

Estats de la connexió

Estats: en els protocols orientats a connexió, la connexió passa per diferents estats.

Exemple de connexió a la web de la UPC i ús de la comanda netstat:

[email protected]:~$ netstat --inet -t -a -c | grep upc
tcp        0      0 A202PC04.aula202.:53571 www.upc.es:https        TIME_WAIT  
tcp        0      0 A202PC04.aula202.:39935 www.upc.es:http         ESTABLECIDO
tcp        0      0 A202PC04.aula202.:53646 www.upc.es:https        ESTABLECIDO
tcp        0      0 A202PC04.aula202.:39878 www.upc.es:http         TIME_WAIT  
tcp        0      0 A202PC04.aula202.:39860 www.upc.es:http         TIME_WAIT  
tcp        0      0 A202PC04.aula202.:39938 www.upc.es:http         ESTABLECIDO
tcp        0      0 A202PC04.aula202.:39893 www.upc.es:http         TIME_WAIT  
tcp        0      0 A202PC04.aula202.:39901 www.upc.es:http         TIME_WAIT  
tcp        0      0 A202PC04.aula202.:39902 www.upc.es:http         TIME_WAIT  
tcp        0      0 A202PC04.aula202.:53649 www.upc.es:https        ESTABLECIDO

Exercici

Control remot amb connexió TCP.

Objecte:

Classe java serializable per a poder enviar-ho per xarxa.

Disposa de constructors, getters, setters i tres atributs:

  • Un boolean que gestiona la connexió entre el client i el servidor.
  • Un enter per seleccionar l'acció a realitzar.
  • Un String per a guardar el missatge.
public class ObjectClient implements Serializable {

	private static final long serialVersionUID = 1L;
	protected boolean estatconnexio;
	protected int accio;
	protected String missatge;
	
	public ObjectClient() {
		super();
		this.estatconnexio = false;
	}

	public ObjectClient(boolean tancarlink, int accio, String missatge) {
		this.estatconnexio = tancarlink;
		this.accio = accio;
		this.missatge = missatge;
	}

	public boolean getEstatConnexio() {
		return estatconnexio;
	}

	public void setEstatConnexio(boolean estatconnexio) {
		this.estatconnexio = estatconnexio;
	}

	public int getAccio() {
		return accio;
	}

	public void setAccio(int accio) {
		this.accio = accio;
	}

	public String getMissatge() {
		return missatge;
	}

	public void setMissatge(String missatge) {
		this.missatge = missatge;
	}

}

Servidor:

public class MyServer {

	/**
	 * @param args
	 * @throws ClassNotFoundException 
	 */
	@SuppressWarnings("resource")
	public static void main(String[] args) throws ClassNotFoundException {

		ServerSocket serverSocket = null;
		Socket socket = null;
		ObjectInputStream objectInputStream = null;
		DataOutputStream dataOutputStream = null;
		ObjectClient objecte = null;

		try {
			serverSocket = new ServerSocket(8888);
			System.out.println("Listening: 8888");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		boolean estat = false;
		while (true) {
			try {
				socket = serverSocket.accept();
				objectInputStream = new ObjectInputStream(socket.getInputStream());
				dataOutputStream = new DataOutputStream(socket.getOutputStream());
				
				objecte = (ObjectClient) objectInputStream.readObject();			
				String[] comanda1= {"/bin/bash", "-c", "/usr/bin/gnome-terminal"};
				String[] comanda2= {"/bin/bash", "-c", "/usr/bin/nautilus"};
				String[] comanda3= {"/bin/bash", "-c", "/usr/bin/gedit"};
				
				if (objecte.getAccio() == 1) {
					Process process = Runtime.getRuntime().exec(comanda1);
				} else if (objecte.getAccio() == 2) {
					Process process = Runtime.getRuntime().exec(comanda2);
				} else if (objecte.getAccio() == 3) {
					Process process = Runtime.getRuntime().exec(comanda3);
				}
				
				if (objecte.getEstatConnexio() == true) {
					estat = true;
				}
				
				System.out.println("IP: " + socket.getInetAddress());
				System.out.println("Missatge: " + objecte.getMissatge());
				
				Date myDate = new Date();
				SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy:HH-mm-ss");
				String dataString = sdf.format(myDate);

				dataOutputStream.writeUTF(dataString + "Connexió establerta! Comanda executada");
				
			} 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 (objectInputStream != null) {
					try {
						objectInputStream.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();
					}
				}
			}
		}

	}

}
  • Creem dos sockets, un per escoltar i l'altre per rebre les dades.
  • Creem un objecte de la classe java serializable, un objecte ObjectImputStream per rebre l'objecte, un DataOutputStream per enviar dades.
  • Creem un bucle controlat per un boolean que variarà segons el que enviï el client.
  • Inicialitzem el socket per rebre les dades, el ObjectImputStream per agafar l'objecte enviat i el DataOutputStream per enviar el missatge.
  • Parcegem l'objecte rebut a la classe serializable i creem Strings amb les comandes a realitzar al servidor.
  • Controlem l'acció rebuda amb un if-else i amb el mètode Runtime.getRuntime().exec() executem la comanda corresponent.
  • Controlem amb un boolea si ens han enviat l'objecte per tancar la connexió i el socket.
  • Imprimim informació sobre l'enviament de l'objecte i una confirmació de que s'ha establit la connexió.

Client:

public class MainActivity extends Activity {

	EditText ip, port, missatge;
	ToggleButton tancar;
	Spinner opcions;
	TextView textIn;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		tancar = (ToggleButton) findViewById(R.id.closelink);
		opcions = (Spinner) findViewById(R.id.spinner);
		String[] seleccio = {"Obrir terminal","Obrir editor de text","Obrir navegador"};
		ArrayAdapter<String> adaptador = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, seleccio);
		adaptador.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		opcions.setAdapter(adaptador);

		Button buttonSend = (Button) findViewById(R.id.send);

		ip = (EditText) findViewById(R.id.ip);
		port = (EditText) findViewById(R.id.port);
		missatge = (EditText) findViewById(R.id.missatge);
		
		textIn = (TextView) findViewById(R.id.textin);
		
		buttonSend.setOnClickListener(buttonSendOnClickListener);

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

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

		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub
			Socket socket = null;
			ObjectOutputStream objectOutputStream = null;
			DataInputStream dataInputStream = null;
			ObjectClient objecte = new ObjectClient();

			try {
				socket = new Socket(ip.toString(), Integer.parseInt(port.toString()));
				objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
				dataInputStream = new DataInputStream(socket.getInputStream());
				
				Date myDate = new Date();
				SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy:HH-mm-ss");
				String dataString = sdf.format(myDate);				
				objecte.setMissatge(dataString + missatge.getText().toString());
				
				int numaccio = 0;
				
				if (opcions.getSelectedItem().toString() == "Obrir terminal") {
					numaccio = 1;
				} else if (opcions.getSelectedItem().toString() == "Obrir editor de text") {
					numaccio = 2;
				} else if (opcions.getSelectedItem().toString() == "Obrir navegador") {
					numaccio = 3;
				}
				
				objecte.setAccio(numaccio);
				
				boolean estat = false;
				if (tancar.isChecked()) {
					estat = true;
				} else {
					estat = false;
				}
				objecte.setEstatConnexio(estat);

				objectOutputStream.writeObject(objecte);

				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 (objectOutputStream != null) {
					try {
						objectOutputStream.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();
					}
				}
			}
		}
	};

}
  • Declarem les variables a utilitzar:
  1. 3 EditText per a introduir el port, la ip i el missatge.
  2. Un ToggleButton per tancar o obrir la connexió.
  3. Un Spinner, desplegable amb les accions a realitzar.
  4. Un TextView per a imprimir informació sobre la connexió realitzada i el missatge.
  • Dins de onCreate() inicialitzem les variables anteriors i creem algunes com el botó per a enviar tot relacionant-ho amb el elements del layout. Per al desplegable de les accions utilitzarem un String amb 3 opcions i farem servir un ArrayAdapter per a fer un tipus de llista selectiva.
  • Creem el OnClickListener() per al botó i introduir dins els objectes.
  • Creem el socket, el ObjectOuputStream, el DataInputStream i l'objecte de la classe serializable.
  • Utilitzem un try-catch per a inicialitzar el socket amb la IP i el port, el ObjectOuputStream, el DataInputStream.
  • Seguint en el try-cath carreguem el missatge amb el seu mètode de la classe serializable.
  • Utilitzem un if-else per a escollir l'acció a realitzar al servidor.
  • Finalment, comprovem que s'ha realitzat la connexió, enviem el paquet i rebem les dades.

Layout del client:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ToggleButton
        android:id="@+id/closelink"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Obrir/Tancar connexió" />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Introdueix la IP" />

    <EditText
        android:id="@+id/ip"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Introdueix el port" />

    <EditText
        android:id="@+id/port"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Introdueix el missatge" />

    <EditText
        android:id="@+id/missatge"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/send"
        android:layout_width="158dp"
        android:layout_height="wrap_content"
        android:text="Enviar missatge" />
    
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Tria l'acció:" />

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/textin"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Imatge de l'aplicació client:

Andrescorazon AndroidApp ClientTCP.png

Protocols no orientats a connexió. UDP

Datagrames

  • S'envien directament paquets de l'emissor al receptor sense establir prèviament una connexió.

User Datagram Protocol (UDP)

  • No és una connexió fiable, ja que els paquets poden arribar en qualsevol ordre, duplicats, es poden perdre...
  • Protocols:

Datagrames

Certes aplicaciones no necessiten la fiabilitat d'una connexió punt a punt que proveix un canal TCP. Al contrari per aquelles aplicacions que no sigui imprescindible o en xarxes d'una alta fiabilitat de per si pot ser interessant utilitzar UDP, ja que és un protocol de transmisió de dades molt més ràpid que TCP.

El protocol UDP proveix una forma de comunicació on les aplicacions envien paquets de dades, anomenats datagrames. Un datagrama és un missatge auto-contingut i independent del qual no es pot garantir ni l'arribada, ni l'ordre d'arribada ni tant sol el seu contingut.

El paquet java.net proveix les classes DatagramPacket i DatagramSocket proveixen d'un sistema per a crear datagrames per a qualsevol sistema (recordeu que Java és multiplataforma) utilitzant UDP

IMPORTANT: La implementació concreta del datagrama o socket UDP depén del sistema operatiu on corre l'aplicació Java, en tot cas el codi no cal canviar-lo per adaptar-lo a cada tipus de sistema operatiu

Sockets

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:

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 (true) {
			try {
				byte[] buffer = new byte[256];

				// receive request
				DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
				socket.receive(packet);

				String received = new String(packet.getData(), 0, packet.getLength());
				InetAddress address = packet.getAddress();
				int port = packet.getPort();

				System.out.println("Rebut: " + received + "\nAdreça: "
						+ address + " Port: " + port);
			} catch (IOException e) {
				e.printStackTrace();
			}
}
 

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:

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

És la recepció del paquet.

IMPORTANT: Observem 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, InterruptedException {
		
		DatagramSocket socket = new DatagramSocket();
		InetAddress address = null;
		
        // send request
        byte[] buffer = new byte[256];
        
        int i = 0;
        while (i < 50) {
        
        	String stringToConvert = Integer.toString(i);
        	buffer = stringToConvert.getBytes();
        	address = InetAddress.getByName("192.168.202.118");
        	DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, 4445);
        
        	socket.send(packet);
        	Thread.sleep(1000);
        	i++;
        }      
        socket.close();
}

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

Exercici

Enviament de paquets. Client-servidor. UDP.

Client

public static void main(String[] args) throws IOException, InterruptedException {
		
	DatagramSocket socket = new DatagramSocket();
	InetAddress address = null;
		
        // send request
        byte[] buffer = new byte[256];
        
        int i = 0;
        while (true) {
        
        	String stringToConvert = Integer.toString(i);
        	buffer = stringToConvert.getBytes();
        	address = InetAddress.getByName("192.168.202.118");
        	DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, 4445);
        
        	socket.send(packet);
        	Thread.sleep(1000);
        	i++;
        }      
        socket.close();
}
  • Creem un objecte DatagramSocket, el socket per on enviarem les dades.
  • Creem un objecte InetAdress, on guardarem la IP destí.
  • Creem un buffer de bytes (tant al client com al servidor haurien de tenir la mateixa mida).
  • Creem una variable de tipus enter.
  • Creem un bucle per enviar dades contínuament.
  • Dintre del bucle, incrementem la variable i la convertim en String. Després assignem el String al buffer passat a bytes.
  • Instanciem el DatagramPacket i passem com a paràmetres el buffer, la longitud del buffer, la IP de destí i el port.
  • Enviem el paquet i amb Thrad.sleep fem una pausa.
  • Per finalitzar, tanquem el socket.

Servidor

public class Server {

	protected DatagramSocket socket = null;

	public Server() throws SocketException {
		socket = new DatagramSocket(4445);
	}

	public void listen() {
		
		while (true) {
			try {
				byte[] buffer = new byte[256];

				// receive request
				DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
				socket.receive(packet);

				String received = new String(packet.getData(), 0, packet.getLength());
				InetAddress address = packet.getAddress();
				int port = packet.getPort();

				System.out.println("Rebut: " + received + "\nAdreça: "
						+ address + " Port: " + port);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
                socket.close();
	}
}
  • Creem un objecte DatagramSocket, el socket per on rebrem les dades.
  • Inicialitzem el socket amb el port a escoltar.
  • A la funció per escoltar, creem un bucle infinit.
  • Dintre del bucle, creem un buffer de bytes (tant al client com al servidor haurien de tenir la mateixa mida).
  • Creem un objecte DatagramPaquet i passem com a paràmetres el buffer, la longitud del buffer i rebem el paquet.
  • Creem un String per a ficar el contingut del paquet, creem un objecte InetAdress per veure la IP del paquet i un enter per a guardar el port del paquet.
  • Imprimim per pantalla la informació del paquet i per finalitzar, tanquem el socket.

Adreces IP

Les adreces IPs estan formades per 32 Bits.

  • Permeten adreçar una mica menys de 4300 milions de màquines.
  • El format més comú és el decimal amb punts.
207.142.131.235 correspon als 32 bits:
 11001111.10001110.10000011.11101011

Altres notacions:

IPSNotation.jpg

Ports

Un port és una connexió virtual que pot ser utilitzada per les aplicacions per intercanviar dades.

Els ports més comuns són els dels protocols TCP i UDP.

Notació: Decimal (22, 80) o Hexadecimal.

El fitxer /etc/services manté una llista de ports i els seus serveis associats.

Estats d'un socket. Establiment de connexió

Andrescorazon Estats socket Establiment connexio.png

El diagrama d'estats complert és el següent:

Andrescorazon Estats socket Establiment connexio diagrama.png
  • ESTABLISHED: Connexió establerta.
  • SYN_SENT: El socket està intentant establir de forma activa una connexió.
  • SYN_RECV: S'ha rebut una petició de connexió des de la xarxa
  • FIN_WAIT1: El socket està tancat i la connexió s'està apagant.
  • FIN_WAIT2: El socket està tancat i la connexió està esperant l'apagament de la connexió remota.
  • TIME_WAIT: El socket està esperant la recepció de paquets de la xarxa tot i haver-se apagat la connexió.
  • CLOSE: El socket està apagat.
  • CLOSE_WAIT: La connexió remota s'ha apagat i s'està esperant que el socket es tanqui.
  • LAST_ACK: La connexió remota s'ha apagat i el socket està apagat però en espera d'un acknowledgement.
  • LISTEN: El socket està a l'espera de peticions de connexió.
  • CLOSING: Els dos sockets estan tancats però encara no s'han rebut totes les dades.

Exemples

Exemple simple

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 desactivem el 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>

També hem de donar permisos a l'aplicació per accedir a Internet afegint el següent codi al fitxer AndroidManifest.xml:

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

Comprovacions:

nmap -p 8888 192.168.202.156
telnet 192.168.202.156 8888
sudo lsof -i :8888

Vegeu també

Enllaços externs