Diferencia entre revisiones de «Programación de comunicacións en rede con Java»
| Línea 443: | Línea 443: | ||
= Creditos e referencias= | = Creditos e referencias= | ||
* [https://www.sintesis.com/desarrollo%20de%20aplicaciones%20multiplataforma-341/programaci%C3%B3n%20de%20servicios%20y%20procesos-ebook-2910.html Programación de servicios y procesos. Carlos Alberto Cortijo Bon ISBN: 9788413571041] | * [https://www.sintesis.com/desarrollo%20de%20aplicaciones%20multiplataforma-341/programaci%C3%B3n%20de%20servicios%20y%20procesos-ebook-2910.html Programación de servicios y procesos. Carlos Alberto Cortijo Bon ISBN: 9788413571041] | ||
| + | |||
| + | == Sockets == | ||
* [https://docs.oracle.com/javase/tutorial/networking/sockets/readingWriting.html Reading from and Writing to a Socket] | * [https://docs.oracle.com/javase/tutorial/networking/sockets/readingWriting.html Reading from and Writing to a Socket] | ||
* [https://www.cs.uic.edu/~troy/spring05/cs450/sockets/socket.html Socket Examples] | * [https://www.cs.uic.edu/~troy/spring05/cs450/sockets/socket.html Socket Examples] | ||
| + | |||
| + | === Exemplo de xogo de adiviñar números === | ||
| + | * [https://programmerclick.com/article/5830172001/ Ejemplo de juego de números de adivinanzas de operaciones de sockets multiproceso] | ||
| + | * [http://ayudavariables.blogspot.com/2013/08/secreto.html?m=1 Ejemplo Cliente/Servidor número secreto] | ||
| + | * [https://github.com/RNogales94/Practicas-Redes/blob/master/P2%20-%20Cliente%20:%20Servidor/Ejercicio%205/src/fr/practica2/pkg5/ClienteAdivinador.java Otro Cliente/Servidor número secreto] | ||
Revisión del 09:46 24 sep 2021
Sumario
- 1 Comunicaciones en rede
- 2 Interfaces de rede
- 3 Enderezos IP
- 4 Resolución de nomes
- 5 Sockets UDP
- 6 Sockets TCP
- 7 Creditos e referencias
Comunicaciones en rede
O paquete java.net proporciona clases que permiten levar a cabo comunicaciíns entre procesos utilizando os protocolos estándares da familia de TCP/IP. Pode dividirse en dúas seccións:
- Clases de baixo nivel que permiten representar as principales entidades dos niveles de enlace, rede e transporte;
- Clases de alto nivel relacionadas con protocolos de nivel de aplicación
Comezaremos revisando as clases para interfaces de rede e enderezos IP, para despois progresar en algocentral nas comunicacións en rede: a programación de sockets de UDP e de TCP, cos que se desenvolverán programas servidores e clientes que se comunican utilizando os devanditos protocolos de transporte UDP e TCP; e que se pode considerar que implementan sencillos protocolos de nivel de aplicación.
| Interfaces de rede | NetworkInterface: interfaz de rede |
| Enderezos IP | InetAddress: enderezo IP Inet4Address: enderezo IPv Inet6Address: enderezo IPv6 InterfaceAddress:enderezo IP máis información relativa á rede á que pertenece a interfaz |
| Sockets | Socket: socket TCP de cliente, para conectar cun proceso servidor mediante TCP ServerSocket: socket TCP de servidor, que acepta conexións desde un socket de cliente mediante TCP. DatagramSocket: socket de UDP, que se usa para enviar e recibir datagramas de UDP |
Interfaces de rede
As interfaces de red presentes no sistema podense amosar cos comandos:
- ifconfig ou ip en Linux
- ipconfig en Windows.
A clase NetworkInterface ten métodos estáticos que permiten obter unha enumeración de todas as interfaces de rede ou NetworkInterface existentes no sistema.
Una NetworkInterface pode ter máis dunha configuración IP asociada,e normalmente cando menos unha para IPv4 e outra para IPv6. Con distintos métodos da clase podense obter para cada unha obxectos de tipo InterfaceAddress e InetAddress.
A clase InterfaceAddress proporciona información adicional a InetAddress coma por exemplo a lonxitude da máscara de rede ou o enderezo de broadcast da rede á que pertence (para un enderezo IPv4).
Na táboa explicanse alguns dos métodos de NetworkInterface.
| Método | Funcionalidade |
|---|---|
| static Enumeration<NetworkInterface> getNetworkInterfaces() | Devolve todas as interfaces de rede existentes na máquina. A partir do Enumeration pódese obter un Iterator para iterar sobre elas. |
| String getDisplayName() String getName() |
Devuelven o nome da interfaz |
| byte[] getHardwareAddress() | Devolve o enderezo MAC en forma de array de 6 bytes (48 bits). |
| Enumeration<InetAddress> getInetAddresses() List<InterfaceAddress> getInterfaceAddresses() |
Devolven os enderezos IP asociados á interfaz. Será en xeral cando menos un enderezo IPv4 e un IPv6.
|
| boolean isLoopback() | Determina se unha interfaz é de tipo loopback. |
O exemplo permitirache familiarizarte co entorno de rede no que está integrado o teu ordenador; o coñecemento poderá ser de utilidade para crear programas que se comuniquen con outros desenvoltos por outros compañeiros, utilizando os protocolos de transporte UDP e TCP.
Exemplo con programa que amosa información das interfaces de rede
Aquí está o código dun programa que amosa información de todas as interfaces de rede presentes no sistema.
Amosa información da súa configuración para IPv4 e IPv6 (enderezos IP, máscaras de rede, enderezos de broadcast, etc).
Cuestións. Antes de executar o programa asegúrate de que o equipo ten conexión a internet ou a unha rede local. ¿En qué cambia a saída do programa cando se desconecta o cable de rede ou se desactiva a interfaz de rede desde o sistema operativo? ¿Para qué tipos de interfaces se amosa información? ¿Hay alguna que esté sempre activa? ¿Cal? ¿Por qué?
Enderezos IP
Unha interfaz de rede pode ter configurada un ou varios enderezos IPv4 e un ou varios enderezos IPv6; e cada un cos seus correspondentes máscaras de rede.
As clases que permiten xestionar esta información:
- InterfaceAddress: Representa a configuración IP dunha interfaz. Esto inclúe o enderezo IP e parámetros adicionais:
- Lonxitude da máscara de rede (número de bits que no enderezo IP especifican a rede)
- Cando se trata dun enderezo IPv4, enderezo de broadcast para a rede á que pertence a interfaz
- InetAddress: Representa un enderezo IP. Ten dúas subclases para representar enderezos IPv4 e IPv6:
- Inet4Address
- Inet6Address
A clase InterfaceAddresss es muy sencilla y sus principales métodos se muestran en el cuadro 3.6. Para entender mejor su funcionamiento, hay que tener en cuenta que representa la conguración para IP de una interfaz de red. Por lo tanto, puede corresponder a cualquiera de las versiones de este protocolo, IPv4 o IPv6. Tiene un método para obtener la dirección IP y métodos adicionales para obtener otros parámetros de la conguración.
| Método | Funcionalidade |
|---|---|
| InetAddress getAddress() | Devolve o enderezo IP. |
| short getNetworkPrefixLength() | Devolve a lonxitude da máscara de rede (o número de bits dentro do enderezo IP que especifican a rede |
| InetAddress getBroadcast() | Devoelve o enderezo de broadcast para a rede á que pertence o enderezo IP (so no caso de que se trate dun enderezo de IPv4; con enderezo de IPv6 devolve null |
A clase InetAddress ten toda a funcionalidade precisa para realizar calquer tipo de operación sen preocuparse de se o enderezo IP é de IPv4 o IPv6; porén na práctica non adoitan utilizarseas súas subclases Inet4Address e Inet6Address.
A táboa amosa algúns métodos desta clase. Ten ademáis algúns métodos estáticos para resolución de nombres que se verán ne seguinte sección
| Método | Funcionalidade |
|---|---|
| static InetAddress getByName(String host) | Obten unha InetAddress para o host. En host pódese proporcionar un enderezo IP (analízase e valçidase o enderezo IP e obtense un obxecto de clase Inet4Address ou Inet6Address) ou un nome de host (utilizarase o mecanismo de resolución de nomes para obter un enderezo IP) |
| String getHostAddress() | Devolve a representación textual do enderezo IP |
| byte[] getAddress() | Devolve un array cos bytes que compoñen o enderezo IP (4 con IPv4 e 16 con IPv6) |
| static InetAddress getByAddress(byte[] addr) | Devolve unha InetAddress para o enderezo IP dado, especificado como un array de bytes |
| boolean isReachable(int timeout) | Determina se o enderezo é alcanzable, especificando un tempo máximo de espera (timeout) en milisegundos |
| boolean isSiteLocalAddress() | Determina se é enderezo é local ou privado. Os rangos de enderezos para redes privadas especifícanse na RFC 1918. As redes privadas de clase C para IPv4 (as máis frecuentes) son 192.168.X.0/24, con 0 <= X <= 255 |
| boolean isLoopbackAddress() | Determina se o enderezo IP é dunha interfaz de tipo loopback |
| static InetAddress getLoopbackAddress() | Devolve o enderezo de loopback:
|
Resolución de nomes
A clase InetAddress ten métodos estáticos:
- que realizan resolución de nomes: dado un nome, permiten obtener o enderezo IP asociado.
- que realizan resolución inversa de nomes: permiten obter un nome de host a partir do seu enderezo IP.
| Método | Funcionalidade |
|---|---|
| static InetAddress getByName(String host) | Obten un enderezo IP a partir dun nome de host, utilizando servizos de resolución de nomes ou ben a partir dunha representación textual dun enderezo IP |
| static InetAddress[] getAllByName(String host) | Similar o método anterior pero máis completo; devolve un array con todos os enderezos IP que se pueden obter utilizando servizos de resolución de nomes; tamén se pode especificar o host mediante a representación textual dun enderezo IPv4 ou IPv6 |
| public String getHostName() | Este método permite realizar unha resolución inversa de nome: obter un nome de host a partir do seu enderezo IP. Se a InetAddress creouse sen especificar un nome para o host, enton realízase unha resolución inversa de nomes para obter o nome do host a partir do seu enderezo IP. |
| static InetAddress getByAddress (String host, byte[] addr) | Crea unha InetAddress indicando un enderezo IP e un nome de host. Devolve o nome proporcionado ó crear a InetAddress sen realizar resolución inversa de nomes |
| String getCanonicalHostName() | Devuelve o nome de host canónico ou FQDN (fully qualified domain name) utilizando o protocolo DNS para obter esta información. |
| static InetAddress getLocalHost() | Devolve o enderezo IP do propio host. En Linux normalmente obtense o seu nome de /etc/hostname , e resolvese o nome para obter un enderezo IP. Dependendo do sistema poderíase obter o enderezo da interfaz de loopback, un enderezo IP privado pertencente á rede local, ou incluso unha IP externa ou pública. Se non se pode resolver este nombre, lánzase unha excepción de tipo UnknownHostException |
Tarefa Crea un programa que amose o nome e enderezo IP do propio host utilizando o método getLocalHost.
Sockets UDP
Introdución ó protocolo UDP
O protocolo de transporte UDP non é orientado a conexión. So permite o envío de paquetes de datos individuales chamados datagramas. Estos poden chegar en orde distinto a orden en que se enviaron, e incluso non chegar. Neste sentido, o protocolo de transporte UDP non aporta ningún valor engadido ó servizo de envío de datagramas IP proporcionado polo nivel de rede (ou de internet).
Un datagrama de UDP está encapsulado nun datagrama de IP, como se amosa na figura. Cada liña amosa 4 bytes. Arriba amosanse nunha regra os bits do 0 ó 31.
| Porto orixe | Porto destino | ||
|---|---|---|---|
| Lonxitude da mensaxe | Checksum |
| Bits | 0 - 15 | 16 - 31 | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Porto orixen | Porto destino | ||||||||||||||||||||||||||||||
| 32 | Lonxitude da mensaxe | Suma de verificación | ||||||||||||||||||||||||||||||
| 64 | Datos | |||||||||||||||||||||||||||||||
A cabecera UDP consta de 4 campos dos cales 2 son opcionais (con fondo vermello na táboa). Os campos dos portos orixen e destino son campos de 16 bits que identifican o proceso de emisión e recepción. Xa que UDP carece dun servidor de estado e a orixe UDP non solicita respostas, o puerto orixen é opcional; en caso de non ser utilizado, o porto orixen debe ser posto a cero.
Pódese ver que o conxunto dun datagrama de UDP encapsulado nun datagrama de IP conten, ademáis dos datos que se envían do proceso de orixe ó de destino, o enderezo IP e o porto de orixe e o enderezo IP e porto de destino; toda a información necesaria para que os datos cheguen o seu destino e toda a información necesaria para que o proceso que o recibe poida enviar datagramas como resposta ó proceso de orixe. A comunicación entre dous procesos mediante o protocolo UDP é moi similar á comunicación entre dúas personas por correo (postal ou electrónico): xunto coa mensaxe e os datos para que chegue o seu destino, están os datos para enviar unha resposta.
A API de Java permite enviar e recibir datagramas mediante UDP en forma de obxectos da clase DatagramPacket. Este clase ten métodos setter e getter para toda esta información. Amosanse a continuación os métodos getter:
| Método | Funcionalidade |
|---|---|
| byte[] getData() int getLength() |
Obteñen os datos que conten o datagrama coma un array de bytes, e a lonxitude do array. |
| InetAddress GetAddress() int getPort() |
Obteñen enderezos IP e porto desde onde se enviou o datagrama. Poderían non estar informados. Un datagrama no que no están informados sería análogo a unha carta anónima, sen remitente. |
Na figura amósase cómo se pode utilizar a clase DatagramSocket para enviar e recibir datagramas de UDP entre dos procesos.
UDP é un protocolo sinxelo que pode ser apropiado para o intercambio de pequenas cantidades de información, ou cando é preferible rápidez na transmisión aínda que se poida perder algún paquete (coma no caso do streaming); nese caso ten coma vantaxes a súa sinxelez e rapidez.
Por estas características é utilizado por protocolos de nivel de aplicación tales como DNS ( aínda que os servidores de DNS tamén utilizan TCP para determinadas operacións que requiren a transferencia de cantidades maiores de información entre eles), SNMP ou DHCP.
Hai que ten moi en conta as limitacións de UDP e utilizaro só para aplicacións para as que sea axeitado.
A clase DatagramSocket
Un socket de UDP é a combinación dun enderezo IP máis un número de porto. Un proceso servidor que presta un servizo utilizando UDP crea un socket de UDP ligado a un porto e a unha interfaz de rede local, identificada polo seu enderezo. Entón, pode recibir datagramas enviados por procesos clientes. Se cuando se crea un socket non se especifica un enderezo IP, entonces pode recibir datagramas a través de calquera interfaz de rede local: dise que se ligou o socket a unha wildcard address.
A clase DatagramPacket utilízase para representar datagramas de UDP. Pódense enviar e recibir DatagramPacket a través dun DatagramSocket.
UDP permite ademáis de enviar e recibir datagramas de enderezos particulares (comunicación unicast):
- Envío broadcast. Si se especica la dirección de broadcast de la red local, todos los hosts de la red local reciben el datagrama. Esto es muy sencillo y puede tener su utilidad. Más adelante, se propondrá algún ejercicio en que se saque partido de esto.
- Comunicación multicast. Es posible utilizando la clase MulticastSocket . Un grupo de multicast está identicado por una dirección IP de clase D. Se puede crear un MulticastSocket ligado a un puerto local y luego unirlo al grupo especicando su dirección IP. No se verá más acerca de multicast con UDP aquí.
Os cadros amosan algúns dos métodos das clases DatagramSocket e DatagramPacket. Casi todas as operacións realizadas cun DatagramSocket poden provocar excepcións de tipo IOException . Esta clase implementa a interfaz Closeable polo que se pode utilizar nun bloque try … catch con recursos.
| Método | Funcionalidade |
|---|---|
| DatagramSocket() DatagramSocket(int port) DatagramSocket(int port, InetAddress laddr) |
Diversos constructores para un DatagramSocket para envío e recepción de datagramas de UDP.
O porto e o enderezo IP especificados son locales. O enderezo IP e o porto de destino para datagramas enviados van especificados no propio datagrama. Se se especifica o enderezo IP dunha interfaz activa, o socket lígase a ela. Se non, lígase a todas as interfaces activas (wildcard address). Pódese especificar un porto para enviar e recibir datagramas por él. Un proceso servidor normalmente fará iso. Un programa cliente normalmente non especificará un porto, e entón o sistema elixirá un porto efímero |
| void receive(DatagramPacket p) | Recibe un datagrama desde a interfaz (especificada por un enderezo IP) e o porto o que está ligado o socket. Se non está ligado a ningunha interfaz específica pode recibir o datagrama por calquera interfaz. O enderezo IP e o puerto de orixen virán indicados no propio datagrama recibido |
| void send(DatagramPacket p) | Envía un datagrama. O enderezo IP e o porto deben estar especificados no propio datagrama |
| setSoTimeout(int timeout) | Establece un tempo máximo de espera cando se recibe un datagrama co método receive. Se pasado ese tempo non se recibíu un datagrama xenerarase unha excepción de tipo SocketTimeoutException. Un valor 0 para timeout significa espera indefinida, a funcionalidade por defecto de receive() |
| void close() | Pecha o socket |
| void connect (InetAddress address, int puerto) void connect (SocketAddress addr) |
Conecta o socket a un enderezo e porto remotos de xeito que só se poden enviar datagramas a ese enderezo e porto ou recibilos dese enderezo e porto.
O nombre deste método non debe inducir a confusión. Que exista un método connect non é contradictorio co feito de que UDP sexa un protocolo non orientado a conexión, a diferencia de TCP |
| void disconnect{} | Desconecta o socket |
| Método | Funcionalidade |
|---|---|
| DatagramPacket(byte[] buf, int length) DatagramPacket(byte[] buf, int offset, int length) DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) DatagramPacket(byte[] buf, int length, InetAddress address, int port) |
Diversos constructores. Os parámetros corresponden a:
|
| getAddress(), setAddress() getData(), setData() |
Diversos métodos getter e setter. |
Programación de aplicacións servidores e clientes baseadas en UDP
Os programas de exemplo virán en parellas: un programa servidor e un programa cliente. Os enderezos e portos que utilizar se especificarán mediante un argumento de liña de comandos.
Texto entre procesos. Codificación
Moitos dos programas transmiten textos entre procesos; xeneralmente será texto codificado en UTF-8.
Importante. Codificaciones de texto e codificación en UTF-8. Na comunicación entre procesos é preciso facelo de acordo a un protocolo preestablecido de xeito que ambos poidan interpretar correctamente a información recibida desde o outro e así dar a resposta axeitada en cada caso; porén debemos asegurar que ambos utilizan o mesmo formato para os datos que se transmiten entre eles. No caso dun texto o proceso emisor debe utilizar sempre a mesma codificación, e o receptor debe interpretalo seempre conforme a esa mesma codificación. Se ambos procesos se executan en sistemas distintos poderían non usar a mesma codificación (que normalmente será UTF-8) para codificar o texto en orixen e para decodificarlo en destino; por iso non se pode asumir que será sempre UTF-8. Nas aplicaciones típicas co protocolo UDP, a codificación de texto interven cuando se obten unha secuencia de bytes a partir dunha cadena de caracteres ou viceversa. Conven utilizar a codificación UTF-8. Para iso hai que facer o seguiente:
- Obter a secuencia de bytes para un String s con s.getBytes("UTF-8") .
- Crear un String a partir de un byte[] b con new String(b, "UTF8") se todos os datos no array de bytes b son datos válidos. Nembargantes con UDP os datos dun DatagramPacket dg están en dg.getData(), e o número de bytes de datos válidos está en dg.getLength(). Por iso é preciso indicar o número de bytes para crear o String; e se pode crear con new String(dg.getData(), 0, dg.getLength(), "UTF-8") .
Exemplo. Echo de servidor e cliente
No seguiente exemplo o cliente envía unha cadena de caracteres ó servidor e este envía como resposta ó cliente a mesma liña pero antepoñendo un carácter #.
Tanto en cliente coma servidor seguense as pautas para obter un array de byte coa codificación en UTF-8 dun String; e inversamente para crear un String a partir da súa codificación en UTF-8 dada en un array de byte.closeable .
O porto para o que se crea o DatagramSocket pásase o programa servidor por liña de comandos. Pásaselle ó programa cliente ademáis o host servidor (por exemplo "localhost" se ambos se executan na mesma máquina.
Unha vez creado o DatagramSocket, úsase para recibir un datagrama (DatagramPacket) do proceso cliente co método receive. Para recibir os datos, débese crear un array de tipo byte[] datosRecibidos o suficiente grande como para recibir todos os datos que vaia a enviar o proceso cliente. Do datagrama recibido obtense o enderezo IP e o porto de procedencia do datagrama. Con eles crease un DatagramPacket de resposta que se envía polo mesmo DatagramSocket co método send.
Comezamos co código do servidor:
1 package udp_echoserver; 2 import java.io.IOException; 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 import java.net.InetAddress; 6 import java.net.SocketException; 7 public class UDP_EchoServer { 8 private final static int MAX_BYTES = 1400; 9 private final static String COD_TEXTO = “UTF-8”; 10 public static void main(String[] args) { 11 if (args.length < 1) { 12 System.err.println(“ERROR, indicar: puerto.”); 13 System.exit(1); 14 } 15 int numPuerto = Integer.parseInt(args[0]); 16 try(DatagramSocket serverSocket=new DatagramSocket(numPuerto)) { 17 System.out.printf(“Creado socket de datagramas para puerto %s.\n”, 18 numPuerto); 19 while (true) { 20 System.out.println(“Esperando datagramas.”); 21 byte[] datosRecibidos = new byte[MAX_BYTES]; 22 DatagramPacket paqueteRecibido = new DatagramPacket( 23 datosRecibidos,datosRecibidos.length); 24 serverSocket.receive(paqueteRecibido); 25 String lineaRecibida = new String(paqueteRecibido.getData(), 26 0, paqueteRecibido.getLength(), COD_TEXTO); 27 InetAddress IPCliente = paqueteRecibido.getAddress(); 28 int puertoCliente = paqueteRecibido.getPort(); 29 System.out.printf(“Recibido datagrama de %s:%d (%s)\n”, 30 IPCliente.getHostAddress(), puertoCliente, lineaRecibida); 31 String respuesta = “#” + lineaRecibida + “#”; 32 byte[] b = respuesta.getBytes(COD_TEXTO); 33 DatagramPacket paqueteEnviado = new DatagramPacket( 34 b, b.length, IPCliente, puertoCliente); 35 serverSocket.send(paqueteEnviado); 36 } 37 } catch (SocketException ex) { 38 System.out.println(“Excepción de sockets”); 39 ex.printStackTrace(); 40 } catch (IOException ex) { 41 System.out.println(“Excepción de E/S”); 42 ex.printStackTrace(); 43 } 44 } 45 }
O programa cliente crea un DatagramSocket, mais non ligado a un porto como o facía o programa servidor. Le liña tras liña de entrada estándar, obtén a súa codificación en UTF-8, e a envía ó programa servidor. Despois agarda a recibir unha resposta: de non ser por clientSocket.connect(IPServidor, numPuerto), a chamada posterior a client-Socket.receive(paqueteRecibido) podería obter un datagrama enviado desde calquer outro enderezo IP ou porto. Porén é recomendable utilizar este método por precaución, a non ser que se desexe que o programa poida recibir datagramas desde máis dunha posible combinación de enderezo IP e porto.
1 package udp_echoclient; 2 import java.io.BufferedReader; 3 import java.io.IOException; 4 import java.io.InputStreamReader; 5 import java.net.DatagramPacket; 6 import java.net.DatagramSocket; 7 import java.net.InetAddress; 8 import java.net.SocketException; 9 public class UDP_EchoClient { 10 private final static int MAX_BYTES = 1400; 11 private final static String COD_TEXTO =”UTF-8”; 12 public static void main(String[] args) { 13 if (args.length < 2) { 14 System.err.println(“ERROR, indicar: host_servidor puerto”); 15 System.exit(1); 16 } 17 String nomHost = args[0]; 18 int numPuerto = Integer.parseInt(args[1]); 19 try (DatagramSocket clientSocket = new DatagramSocket(); 20 InputStreamReader isrStdIn = new InputStreamReader(System.in, 21 COD_TEXTO); 22 BueredReader brStdIn = new BueredReader(isrStdIn)) { 23 String lineaLeida; 24 System.out.println(“Introducir líneas. Línea vacía para terminar.”); 25 System.out.print(“Línea>”); 26 while ((lineaLeida = brStdIn.readLine()) != null 27 && lineaLeida.length() > 0) { 28 InetAddress IPServidor = InetAddress.getByName(nomHost); 29 byte[] b = lineaLeida.getBytes(COD_TEXTO); 30 DatagramPacket paqueteEnviado = new DatagramPacket( 31 b, b.length, IPServidor, numPuerto); 32 clientSocket.connect(IPServidor, numPuerto); 33 clientSocket.send(paqueteEnviado); 34 byte[] datosRecibidos = new byte[MAX_BYTES]; 35 DatagramPacket paqueteRecibido = new DatagramPacket( 36 datosRecibidos, datosRecibidos.length); 37 clientSocket.receive(paqueteRecibido); 38 String respuesta = new String(paqueteRecibido.getData(), 39 0, paqueteRecibido.getLength(), COD_TEXTO); 40 System.out.printf(“Datagrama recibido de %s:%d: %s\n”, 41 paqueteRecibido.getAddress().getHostAddress(), 42 paqueteRecibido.getPort(), respuesta); 43 System.out.print(“Línea>”); 44 } 45 } catch (SocketException ex) { 46 System.out.println(“Excepción de sockets”); 47 ex.printStackTrace(); 48 } catch (IOException ex) { 49 System.out.println(“Excepción de E/S”); 50 ex.printStackTrace(); 51 } 52 } 53 }
Probar funcionamento do servidor sen necesidade do cliente
Co comando nc podense enviar datagramas de UDP cun texto, e amosar o texto contido no datagrama de resposta recibido. Con iso podemos probar o funcionamiento dun programa servidor sen necesidade de crear un programa cliente. Se o servidor de eco anterior está funcionando no puerto 8000 do mesmo ordenador (localhost), se lle pode enviar un datagrama co texto "ola echo" e amosar a resposta con:
$ echo -n "ola eco" | nc -u localhost 8000 #ola eco#