Sockets 2.4 Sockets Ambas formas de comunicación (UDP y TCP) utilizan la abstracción de sockets, que proporciona los puntos extremos de la comunicación entre procesos. Los sockets (conectores) están presentes en la mayoría de las versiones de UNIX, incluido Linux y también Windows. Los sockets permiten conectar dos programas en red para que se puedan intercambiar datos. Los sockets están basados en una arquitectura cliente/servidor. En esta arquitectura uno de los programas debe estar siempre arrancado y pendiente de que alguien establezca conexión con él. Este programa se denomina servidor. El otro programa lo arranca el usuario cuando lo necesita y es el programa que da el primer paso en el establecimiento de la comunicación. Este programa se llama cliente. El servidor, está continuamente a la escucha y a la espera de que alguien se quiera conectar a él. Si hacemos una comparación con un teléfono, un servidor es una empresa 24 horas, 365 días al año, pendiente de recibir llamadas de sus clientes. El cliente, en un momento dado decide conectarse a un servidor y hacerle alguna petición. Cuando el cliente no necesita al servidor, cierra la conexión. En la comparativa del teléfono, el cliente es el que llama por teléfono a la empresa cuando necesita algo de ella. Por ejemplo, un servidor de páginas web está siempre en marcha y a la escucha. El navegador es el cliente. Cuando arrancamos el navegador y ponemos la dirección del servidor web, el navegador establece la comunicación y le pide al servidor la página web que queremos ver. El servidor la envía y el navegador la muestra. La comunicación entre procesos consiste en la transmisión de un mensaje entre un conector de un proceso y un conector de otro proceso. Para los procesos receptores de mensajes, su conector debe estar asociado a un puerto local y a una de las direcciones Internet de la computadora donde se ejecuta. Los mensajes enviados a una dirección de Internet y a un número de puerto concretos, sólo pueden ser recibidos por el proceso cuyo conector esté asociado con esa dirección y con ese puerto. Los procesos pueden utilizar un mismo conector tanto para enviar como para recibir mensajes. Cada computadora permite un gran número de puertos posibles, que pueden ser usados por los procesos locales para recibir mensajes. Cada proceso puede utilizar varios puertos para recibir mensajes, pero un proceso no puede compartir puertos con otros procesos de la misma computadora. Cualquier cantidad de procesos puede enviar mensajes a un mismo puerto. Cada conector se asocia con un protocolo concreto, que puede ser UDP o TCP. 2.4.1 Sockets flujos (TCP) Son un servicio orientado a la conexión, donde los datos se transfieren sin encuadrarlos en registros o bloques. Si se rompe la conexión entre los procesos, éstos serán informados de tal suceso para que tomen las medidas oportunas. El protocolo de comunicaciones con flujos es un protocolo orientado a la conexión, ya que para establecer una comunicación utilizando el protocolo TCP, hay que establecer en primer lugar una conexión entre un par de sockets. Mientras uno de los sockets atiende peticiones de conexión (servidor), el otro solicita una conexión (cliente). Una vez que los dos sockets estén conectados, se pueden utilizar para transmitir datos en ambas direcciones. El protocolo TCP (Transmission Control Protocol) funciona en el nivel de trasporte, basándose en el protocolo de red IP (Internet Protocol). IP proporciona comunicaciones no fiables y no basadas en conexión, muy dependientes de saturación en la red, caídas de nodos, etc. Por el contrario, TCP está orientado a conexión y proporciona comunicaciones fiables basadas en mecanismos de red que gestionan el control de flujo de paquetes y de congestión en los nodos. En Java, las comunicaciones TCP se realizan utilizando la clásica abstracción de socket. Los sockets nos permiten establecer y programar comunicaciones sin tener que conocer los niveles inferiores sobre los que se asientan. Para identificar el destino de los paquetes de datos, los sockets utilizan los conceptos de dirección y puerto. Los valores numéricos de puertos 1 – 1023 se reservan a servicios de interés general, montados a menudo sobre protocolos de uso extendido: Puerto 80 25 110 119 Los valores de puertos entre 1024 y 49151 se usan para servicios específicos de uso no general, el resto (a partir de 49152) se emplean para designar servicios de uso esporádico. Servicio Para web con http Para correo saliente con SMTP Para correo entrante con POP3 Para el servicio de noticias con NNTP Establecimiento de comunicaciones Java proporciona dos clases de abstracción de comunicaciones TCP: una para los procesos cliente (socket) y otra para los procesos servidor (ServerSocket). La Figura 2.1 que es el esquema básico de establecimiento de comunicaciones TCP. Programa Cliente Programa Servidor 2.accept 1. Instanciación ServerSocket 4. Instanciación Puerto destino 5. Conexión TCP (interno) Socket 3. return del accept Socket Nodo destino Puerto destino 6. Comunicación Puerto Figura 2.1 Comunicación TCP entre un cliente y un servidor. Transmisión de datos TCP es un protocolo especialmente útil cuando se desea transmitir un flujo de datos en lugar de pequeñas cantidades aisladas de información. Debido a está característica, los sockets de Java están diseñados para transmitir y recibir datos a través de los Streams definidos en el paquete java.io. La clase Socket contiene dos métodos importantes que se emplean en el proceso de transmisión de flujos de datos: InputStream getInputStream() OutputStream getOutputStream() Estas clases son abstractas, por lo que no podemos emplear directamente todos sus métodos. En general se usan otras clases más especializadas que nos permiten trabajar con flujos de datos como: DataOutputStream, DataInputStream, FileOutputStream, FileInputStream, etc. Ejemplo Hola Mundo Para esta versión necesitamos: Un programa que se ejecute en el equipo cliente y envíe el texto “Hola mundo”: TCPClienteHolaMundo Un programa que se ejecute en el equipo servidor y reciba e imprima el mensaje: TCPServidorHolaMundo TCPClienteHolaMundo.java import java.net.Socket; import java.io.*; import java.net.UnknownHostException; public class TCPClienteHolaMundo{ public static void main(String[] args){ OutputStream FlujoDeSalida; DataOutputStream Flujo; try{ Socket SocketCliente = new Socket("localhost",8000); FlujoDeSalida = SocketCliente.getOutputStream(); Flujo = new DataOutputStream(FlujoDeSalida); Flujo.writeBytes("Hola Mundo"); SocketCliente.close(); }catch (UnknownHostException e){ System.out.println("Referencia a host no resuelta"); }catch (IOException e){ System.out.println("Error en las comunicaciones"); }catch (SecurityException e){ System.out.println("Comunicacion no permitida por razones de seguridad"); } } } TCPServidorHolaMundo import java.net.ServerSocket; import java.net.Socket; import java.io.*; public class TCPServidorHolaMundo{ public static void main(String[] args){ byte[] Mensaje = new byte[80]; InputStream FlujoDeEntrada; DataInputStream Flujo; try{ ServerSocket SocketServidor = new ServerSocket(8000); Socket ComunicaConCliente = SocketServidor.accept(); System.out.println("Comunicacion establecida"); FlujoDeEntrada = ComunicaConCliente.getInputStream(); Flujo = new DataInputStream(FlujoDeEntrada); int BytesLeidos = Flujo.read(Mensaje); System.out.println(new String(Mensaje)); ComunicaConCliente.close(); SocketServidor.close(); }catch (IOException e){ System.out.println("Error en las comunicaciones"); System.exit(0); }catch (SecurityException e){ System.out.println("Comunicacion no permitida por razones de seguridad"); System.exit(0); } } } Ejemplos de lectura y escritura de un socket A continuación se presenta un simple ejemplo que ilustra como un programa puede establecer la conexión a un programa servidor usando la clase Socket y como el cliente puede enviar datos y recibir datos del servidor a través del socket. El ejemplo implementa un cliente, echoClient, que se conecta al echoServer. El echoServer simplemente recibe datos del cliente y hace echo hacia atrás. import java.io.*; import java.net.*; public class echoClient { public static void main(String[] args) throws IOException { Socket echoSocket = null; PrintWriter out = null; BufferedReader in = null; try { echoSocket = new Socket("taranis", 7); out = new PrintWriter(echoSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader( echoSocket.getInputStream())); } catch (UnknownHostException e) { System.err.println("Don't know about host: taranis."); System.exit(1); } catch (IOException e) { System.err.println("Couldn't get I/O for " + "the connection to: taranis."); System.exit(1); } BufferedReader stdIn = new BufferedReader( new InputStreamReader(System.in)); String userInput; while ((userInput = stdIn.readLine()) != null) { out.println(userInput); System.out.println("echo: " + in.readLine()); } out.close(); in.close(); stdIn.close(); echoSocket.close(); } } 1. 2. 3. 4. 5. El programa cliente debe realizar los siguientes pasos: Abrir un socket Abrir un flujo de entrada y salida para el socket. Leer desde y escribir al flujo de acuerdo al protocolo del servidor Cerrar los flujos Cerrar el socket Únicamente el paso 3 difiere de cliente a cliente, dependiendo del servidor. Los otros pasos son los mismos. Ejercicio: Construya el servidor. TCP, continuación Configuración de las comunicaciones Clase ServerSocket Métodos principales Acción socket accept() Espera a que se realice una conexión y devuelve un socket para comunicarse con el cliente. void bind(SocketAddress a) Asigna la dirección establecida al socket creado con accept, si no se utiliza este método se asigna automáticamente una dirección temporal. void close() Cierra el socket InetAddress getInetAddress() Devuelve la dirección a la que está conectada el socket int getLocalPort() Devuelve el número de puerto asociado al socket int getSoTimeout() Devuelve el valor en milisegundos que el socket espera al establecimiento de comunicación tras la ejecución de accept void setSoTimeout(int ms) Asigna el número de milisegundos que el socket espera al establecimiento de comunicación tras la ejecución de accept Clase Socket Métodos principales Acción void bind(SocketAddress a) Asigna la dirección establecida al socket creado con accept, si no se utiliza este método se asigna automáticamente una dirección temporal void close() Cierra el socket. void connect(SocketAddress a) Conecta el socket a la dirección de servidor establecida void connect(SocketAddress a, int Conecta el socket a la dirección de servidor establecida, ms) esperando un máximo de ms milisegundos. InetAddress getInetAddress() Devuelve la dirección a la que está conectada el socket InputStream getInputStream Devuelve el stream de entrada asociada al socket Int gelLocalPort() Devuelve el número de puerto asociado al socket OutputStream getOutputStream() Devuelve el stream de salida asociado al socket int getPort() Devuelve el valor del puerto remoto al que está conectado int getSoLinger() Devuelve el número de milisegundos que se espera a los datos después de cerrar el socket. ClienteTCP import java.awt.*; import java.net.*; import java.io.*; import java.awt.event.*; class ClienteTCP extends Frame implements ActionListener { Panel panel; Socket conexion; TextField textent,textsal; Button enviar,conectar,desconectar; DataOutputStream salida; DataInputStream entrada; ClienteTCP(String nombre) { super(nombre); setSize(350,200); panel=new Panel(); textsal=new TextField(40); textent=new TextField(40); textent.setText("Pulsa el boton \"Conectar\" para conectarte"); textent.setEditable(false); enviar=new Button("Enviar"); enviar.setEnabled(false); conectar=new Button("Conectar"); desconectar=new Button("Desconectar"); desconectar.setEnabled(false); panel.add(new Label("Datos a enviar")); panel.add(textsal); panel.add(new Label("Datos recibidos")); panel.add(textent); panel.add(enviar); panel.add(conectar); panel.add(desconectar); enviar.addActionListener(this); conectar.addActionListener(this); desconectar.addActionListener(this); addWindowListener(new Cerrar()); add(panel); setVisible(true); } public void actionPerformed(ActionEvent e) { String com=e.getActionCommand(); if (com.equals("Enviar")) { try { textent.setText(""); salida.writeUTF(textsal.getText()); textent.setText(entrada.readUTF()); textsal.setText(""); } catch(IOException excepcion){} } else try { salida.writeUTF("Salir"); conexion.close(); conectar.setEnabled(true); desconectar.setEnabled(false); enviar.setEnabled(false); textent.setText("Pulsa el boton \"Conectar\" para conectarte"); } catch(IOException excepcion){} } class Cerrar extends WindowAdapter { public void windowClosing(WindowEvent e) { dispose(); System.exit(0); } } public static void main(String args[]) { new ClienteTCP("Cliente Angel"); new ClienteTCP("Cliente Pepe"); } } ServidorTCP import java.awt.*; import java.net.*; import java.io.*; import java.awt.event.*; class ServidorTCP extends Frame { TextArea entrada; ServerSocket s; int clientes; ServidorTCP() { setTitle("Servidor"); setSize(350,400); // entrada=new TextArea(20,40); entrada=new TextArea(20,40); entrada.setEditable(false); add(entrada); addWindowListener(new Cerrar()); setVisible(true); clientes=0; } void lanzarServidor() { String cadena; try { s=new ServerSocket(5001,50); while (true) new NuevoCliente(s.accept(),this,clientes++).start(); } catch(IOException e){} } public void añadir(String texto) { entrada.append(texto); } class Cerrar extends WindowAdapter { public void windowClosing(WindowEvent e) { dispose(); System.exit(0); } } public static void main(String args[]) { ServidorTCP Servidor=new ServidorTCP(); Servidor.lanzarServidor(); } } class NuevoCliente extends Thread {Socket conexion; ServidorTCP serv; int cliente; DataInputStream entrada; DataOutputStream salida; NuevoCliente(Socket c,ServidorTCP s,int numero) { conexion=c; serv=s; cliente=numero; try { entrada= new DataInputStream(conexion.getInputStream()); salida= new DataOutputStream(conexion.getOutputStream()); } catch(IOException e){} } public void run() { boolean Salir=false; serv.añadir("Cliente "+cliente+" se ha conectado\n"); while (!Salir) { try { String cadena=entrada.readUTF(); if (cadena.equals("Salir")) Salir=true; else { serv.añadir("El cliente "+cliente+" ha enviado:\n"+" salida.writeUTF("Cadena \""+cadena+"\" recibida"); } } catch(IOException e){} } serv.añadir("Se ha ido el cliente "+cliente+"\n"); try { conexion.close(); } catch(IOException e){} } } "+cadena+'\n'); 2.4.2 Sockets Datagrama (UDP) Servicio de transporte sin conexión. Más eficientes que TCP Pero no está garantizada la fiabilidad. Los datos se envían y reciben en paquetes, cuya entrega no está garantizada. Los paquetes pueden ser duplicados, perdidos o llegar en un orden diferente al que se envió. El protocolo de comunicación con datagramas es un protocolo sin conexión, es decir, cada vez que se envíen datagramas es necesario enviar el descriptor del socket local y la dirección del socket que debe recibir el datagrama. Hay que enviar datos adicionales cada vez que se realice una comunicación, aunque tiene la ventaja de que se pueden indicar direcciones globales y el mismo mensaje llegará a muchas computadoras a la vez. Un datagrama enviado por UDP se transmite desde un proceso emisor a un proceso receptor sin acuse de recibo ni reintentos. Si algo falla, el mensaje puede no llegar a su destino. Se transmite un datagrama, entre procesos, cuando uno lo envía, y el otro lo recibe. La comunicación de datagramas UDP utiliza operaciones de envío, no bloqueantes y recepciones, bloqueantes. La operación send devuelve el control cuando ha dirigido el mensaje a las capas inferiores UDP e IP, que son las responsables de su entrega en el destino. A la llegada, el mensaje será colocado en una cola del conector que está enlazado con el puerto de destino. El mensaje podrá obtenerse de la cola de recepción mediante una invocación pendiente o futura del método recibe sobre ese conector. Si no existe ningún proceso ligado al conector destino, los mensajes serán descartados. El método recibe produce un bloqueo hasta que se reciba un datagrama, a menos que se haya establecido un tiempo límite (time out) asociado al conector. Cualquier proceso que necesite enviar o recibir mensajes debe crear, primero, un conector asociado a una dirección Internet y a un puerto local. Un servidor enlazará su conector a un puerto de servidor (uno que resulte con los clientes de modo que puedan enviarle mensajes). Un cliente ligará su conector a cualquier puerto local libre. El método recibe devolverá, además del mensaje, la dirección Internet y el puerto del emisor, permitiendo al receptor enviar la correspondiente respuesta. Ejemplo, el Servicio de Nombres de Dominio en Internet (Domain Name Service, DNS) está implementado sobre UDP. Los datagramas UDP son, en algunas ocasiones, una elección atractiva porque no padecen la sobrecarga asociadas a la entrega de mensajes garantizada. Existen tres fuentes principales para esa sobrecarga: 1. La necesidad de almacenar información de estado en el origen y en el destino. 2. La transmisión de mensajes extra. 3. La latencia para el emisor. Establecimiento de comunicaciones Programa cliente 5. new DatagramPacket Programa servidor 1. new Programa servidor DatagramSocket 2. new DatagramPacket 4. new DatagramSocket DatagramSock et 6.send 3.receive DatagramSocket Puerto destino Puerto destino Nodo destino Figura 2.2 Comunicación UDP 1. El programa que proporciona el servicio (servidor) crea una instancia de la clase DatagramSocket, hincando el puerto asociado al servicio: DatagramSocket 2. MiSocket = new DatagramSocket(4000); El programa servidor crea una instancia de la clase DatagramPacket, donde se guardarán los datos recibidos: DatagramPacket(buffer, buffer.length); 3. hasta que llegan los datos: El programa servidor invoca el método receive sobre el socket de tipo DatagramSocket. Este método, por defecto, bloquea el programa MiSocket.receive(Paquete); 4. El programa cliente crea una instancia de tipo DatagramSocket; DatagramSocket MiSocket = new DatagramSocket(); 5. El programa cliente crea una instancia de tipo DatagramPacket, proporcionándole los datos, además de la dirección y puerto destino. DatagramPacket Paquete = new DatagramPacket(buffer, Mensaje.length(), InetAddress.getByName(“localhost”),4000) 6. El programa que utiliza el servicio (programa cliente) invoca el método send sobre el socket de tipo DatagramSocket: MiSocket.send(Paquete); Ejemplo Hola Mundo UPDEnvia.java import java.net.*; public class UDPEnvia{ public static void main(String args[]){ try{ DatagramSocket MiSocket = new DatagramSocket(); byte[] buffer= new byte[15]; String Mensaje = "Hola Mundo"; buffer = Mensaje.getBytes(); DatagramPacket Paquete = new DatagramPacket(buffer, Mensaje.length(), InetAddress.getByName("localhost"),1400); MiSocket.send(Paquete); MiSocket.close(); }catch (Exception exc){ System.out.println("Error"); }//try } }//UDPEnvia UDPRecibe.java import java.net.*; public class UDPRecibe{ public static void main(String args[]){ try{ DatagramSocket MiSocket = new DatagramSocket(1400); byte[] buffer= new byte[15]; DatagramPacket Paquete = new DatagramPacket(buffer, buffer.length); MiSocket.receive(Paquete); System.out.println(new String(Paquete.getData())); MiSocket.close(); }catch (Exception e){ System.out.println("Error"); }//try }//main }//UDPRecibe Otros ejemplos de datagramas en Java El paquete java.net contiene tres clases para el uso de datagramas, es decir, para enviar y recibir paquetes en la red: DatagramSocket, DatagramPacket y MulticastSocket. Una aplicación puede enviar y recibir DatagramPackets a través de un DatagramSocket. También, se puede hacer un broadcast a múltiples recipientes escuchando a un MulticastSocket. DatagramPacket: Esta clase proporciona un constructor que crea una instancia compuesta por: una cadena de bytes que almacena el mensaje, la longitud del mensaje y la dirección Internet y el número de puerto local del conector destino, Las instancias de DatagramPacket podrán ser transmitidas entre procesos cuando uno las envía, y el otro las recibe . La clase DatagramSocket proporciona varios métodos que incluyen los siguientes: send y receive estos métodos sirven para transmitir datagramas entre un par de conectores. El argumento de send es una instancia de DatagramPacket conteniendo el mensaje y su destino. El argumento de receive es un DatagramPacket vacío en el que se coloca el mensaje, su longitud y su origen. Tanto el método send como receive pueden lanzar una excepción IOException. setSoTimeout: este método permite establecer un tiempo de espera límite. Cuando se fija un límite, el método receive se bloquea durante el tiempo fijado y después lanza una excepción InterruptedIOException. connect: este método se utiliza para conectarse a un puerto remoto y a una dirección Internet concretos, en cuyo caso el conector sólo podrá enviar y recibir mensajes de esa dirección. UDPClient.java import java.net.*; import java.io.*; public class UDPClient{ public static void main(String args[]){ // args give message contents and server hostname DatagramSocket aSocket = null; try { aSocket = new DatagramSocket(); byte[] m = args[0].getBytes(); InetAddress aHost = InetAddress.getByName(args[1]); int serverPort = 6789; DatagramPacket request = new DatagramPacket(m, args[0].length(), aHost, serverPort); aSocket.send(request); byte[] buffer = new byte[1000]; DatagramPacket reply = new DatagramPacket(buffer, buffer.length); aSocket.receive(reply); System.out.println("Reply: " + new String(reply.getData())); }catch (SocketException e){ System.out.println("Socket: " + e.getMessage()); }catch (IOException e){ System.out.println("IO: " + e.getMessage()); }finally {if(aSocket != null) aSocket.close();} } } UDPServer.java import java.net.*; import java.io.*; public class UDPServer{ public static void main(String args[]){ DatagramSocket aSocket = null; try{ aSocket = new DatagramSocket(6789); byte[] buffer = new byte[1000]; while(true){ DatagramPacket request = new DatagramPacket(buffer, buffer.length); aSocket.receive(request); DatagramPacket reply = new DatagramPacket(request.getData(), request.getLength(), request.getAddress(), request.getPort()); aSocket.send(reply); } }catch (SocketException e){System.out.println("Socket: " + e.getMessage()); }catch (IOException e) {System.out.println("IO: " + e.getMessage()); }finally {if(aSocket != null) aSocket.close();} } } Corrida: Terminal 1 mtovar@linux-w7tc:~/programasTCP> java UDPServer Terminal 2 mtovar@linux-w7tc:~/programasTCP> java UDPClient mireya 148.228.22.6 Reply: mireya mtovar@linux-w7tc:~/programasTCP> java UDPClient mireya localhost Reply: mireya mtovar@linux-w7tc:~/programasTCP> Configuración de las comunicaciones DatagramPacket Métodos Acción InetAddress getAddress() Dirección del nodo comunicación byte[] getData() Devuelve el mensaje que contiene el datagrama int getLength() Devuelve la longitud del mensaje del datagrama int getOffset() Devuelve el desplazamiento que indica el inicio del mensaje (dentro del array de bytes) int getPort() Devuelve el valor del puerto remoto void setAddress(InetAddress d) Establece el comunicación nodo remoto remoto en en la la void setData(byte[] Mensaje) Void setData(byte[] Mensaje, Dezplazamiento, int Longitud) Establece el mensaje que contiene el datagrama int Establece el mensaje que contiene el datagrama, indicando su desplazamiento en el array de bytes y su longitud. Void setLength(int Longitud) Establece la longitud del mensaje del datagrama void setPort() Establece el valor del puerto remoto Void setSocketAddress(SocketAddress Establece la dirección (nodo+ puerto) d) remota en la comunicación DatagramSocket Metódos Acción void bind(SocketAddress a) Asigna la dirección establecida al socket void close() Cierra el socket void connect(SocketAddress a) Conecta el socket a la dirección remota establecida void connect(InetAddress puerto) a, int Conecta el socket a la dirección establecida y el puerto especificado void disconnect() Desconecta el socket InetAddress getInetAddress() Devuelve la dirección a la que está conectada el socket. int getLocalPort() Devuelve el número de puerto asociado al socket OutputStream getOutputStream() Devuelve el stream de salida asociado al socket int getPort() Devuelve el valor del puerto remoto al que está conectado Int getSoTimeout() Devuelve el valor en milisegundos que el socket espera al establecimiento de comunicación Boolean isBound() Indica si el socket está vinculado Boolean isClosed() Indica si el socket está cerrado Boolean isConneted Indica si el socket está conectado Void setSoTimeout(int ms) Indica el valor en milisegundos que el socket espera al establecimiento de comunicación 2.4.3 Diferencias entre Sockets Stream y Datagrama En UDP, cada vez que se envía un datagrama, hay que enviar también el descriptor del socket local y la dirección del socket que va a recibir el datagrama, luego los mensajes son más grandes que los TCP. Como el protocolo TCP está orientado a conexión, hay que establecer esta conexión entre los dos sockets, lo que implica un cierto tiempo empleado en el establecimiento de la conexión, que no es necesario emplear en UDP. En UDP hay un límite de tamaño de los datagramas, establecido en 64 kilobytes, que se pueden enviar a una localización determinada, mientras que TCP no tiene límite; una vez que se ha establecido la conexión, el par de sockets funciona como los streams: todos los datos se leen inmediatamente, en el mismo orden en que se van recibiendo. UDP es un protocolo desordenado, no garantiza que los datagramas que se hayan enviado sean recibidos en el mismo orden por el socket de recepción. Al contrario, TCP es un protocolo ordenado, garantiza que todos los paquetes que se envíen serán recibidos en el socket destino en el mismo orden en que se han enviado. En resumen, TCP parece más indicado para la implementación de servicios de red como un control remoto (rlogin, telnet) y transmisión de ficheros (ftp); que necesitan transmitir datos de longitud indefinida. UDP es menos complejo y tiene una menor sobrecarga sobre la conexión; esto hace que sea el indicado en la implementación de aplicaciones cliente/servidor en sistemas distribuidos montados sobre redes de área local.