Xeración de servizos en rede con Java
Sumario
Introducción
A nivel pedagóxico, estes son os resultados de aprendizaxe e criterios de avaliación:
- RA2. Desenvolve aplicacións que ofrezan servizos en rede, para o que utiliza librarías de clases e aplica criterios de eficiencia e dispoñibilidade.
- CA2.1. Analizáronse librarías que permitan implementar protocolos estándar de comunicación en rede.
- CA2.2. Programáronse clientes de protocolos estándar de comunicacións e verificouse o seu funcionamento.
- CA2.3. Desenvolvéronse e probáronse servizos de comunicación en rede.
- CA2.4. Analizáronse os requisitos necesarios para crear servizos capaces de xestionar varios clientes concorrentes.
- CA2.5. Incorporáronse mecanismos para posibilitar a comunicación simultánea de varios clientes co servizo.
- CA2.6. Verificouse a dispoñibilidade do servizo.
- CA2.7. Depuráronse e documentáronse as aplicacións desenvolvidas.
Clases de Java para comunicacións en rede
Na unidade didáctica anterior víronse clases de baixo nivel do paquete java.net da biblioteca estándar de clases de Java coas que se poden desenvolver aplicacións de cliente e de servidor de nivel de aplicación sobre os protocolos de nivel de transporte UDP e TCP.
Pódense utilizar estas clases para desenolver aplicacións de cliente para protocolos estándares de red tales coma FTP, POP3, etc; mais isto require dun conñecemento detallado do protocolo e ocuparse dos detalles do seu funcionamento interno. Para a maioría das aplicacións é suficiente (e resulta máis sinxelo) utilizar API ou bibliotecas de clases específicas para estes protocolos. Así pois veremos as API disponibles para os diferentes protocolos.
Clases de alto nivel del paquete estándar java.net
Ademáis das clases de baixo nivel, o paquete java.net da biblioteca estándar de clases de Java proporciona clases de alto nivel para protocolos estándares de nivel de aplicación.
Estas clases de alto nivel están baseadas na abstracción das URI ou URL, que designan e proporcionan acceso a unha grande variedade de recursos accesibles mediante diversos protocolos, coma por exemplo HTTP e FTP. O concepto de URL resulta familiar para calquiera que navegue por internet. As seguintes URL permiten obter un mesmo ficheiro de dous servidores diferentes e mediante dous protocolos diferentes (HTTP e FTP); basta con introducilas na barra de enderezos dun navegador web para iniciar a súa descarga:
- HTTP: http://ldp.rediris.es/mirror/ubuntu-releases/hirsute/ubuntu-21.04-desktop-amd64.iso
- FTP: http://ftp.rediris.es/sites/releases.ubuntu.com/21.04/ubuntu-21.04-desktop-amd64.iso
NOTA. Nas últimas versións dos navegadores xa non é posible acceder mediante o protocolo FTP, por considerarse un protocolo inseguro. De momento aínda permiten o seu uso facendo cambios na configuración por defecto, pero é posible que en pouco tempo xa non sexa posible acceder a sitios FTP mediante o protocolo FTP dende o navegador. A solución pasará por usar un cliente FTP... ou utilizar protocolos máis seguros para a transferencia de ficheiros, coma pode ser scp.
Os recursos son secuencias de bytes e as clases da biblioteca estándar de clases de Java permiten designalos mediante unha URL. As clases de alto nivel de java.net permiten obter un InputStream para ler os contidos dun recurso designado mediante unha URL. Estas clases proporcionan un acceso sinxelo ós recursos de xeito uniforme e independente do protocolo.
Con todo, a funcionalidade destes protocolos vai máis alá do que ofrecen estas clases; para algunhas aplicacións pode ser necesario utilizar funcionalidades adicionais destes protocolos, e existen clases que o permiten.
Apache Commons Net
A biblioteca de clases Apache Commons Net inclúe clases de alto nivel clientes dos protocolos de rede estándares máis importantes.
A biblioteca de clases, a documentación y o código fonte están disponibles no ficheiro
commons-net-x.y-bin.tar.gz x.y é a versión
Este ficheiro comprimido contén varios ficheiros jar que a súa vez conteñen a biblioteca de clases, e os seus javadocs nun directorio api-docs (tamén accesibles na web). É relevante salientar entre as bibliotecas (libraries):
- commons-net-x.y.jar
- commons-net-examples-x.y.jar, con clases auxiliares útiles para algunhas aplicacións
Principales clases clientes de protocolos de Apache Commons Net
A xerarquía para as principais clases de Apache Commons Net clientes de protocolos son:
SocketClient +----- TelnetClient +----- FTP + \----- FTPClient + \----- FTPSClient +----- TFTP + \----- TFTPClient +----- POP3 + \----- POP3Client + \----- POP3SClient +----- SMTP + \----- SMTPClient + \----- SMTPSClient + \----- AuthenticatingSMTPClient +----- IMAP + \----- IMAPClient + \----- IMAPSClient + \----- AuthenticatingIMAPClient +----- DaytimeTCPClient \----- TimeTCPClient DatagramSocketClient +----- NTPUDPClient +----- DayTimeUDPClient \----- TimeUDPClient
En grosa están as clases clientes, que segue as seguintes regras xerais no referente á súa nomenclatura:
- As clases que teñen o nombre dun protocolo (por exemplo, IMAP) teñen diversos métodos que corresponden a comandos do protocolo. Pódense utilizar para desenvolver aplicacións que interactúan co protocolo a baixo nivel, enviando comandos.
- As clases fillas das anteriores, que teñen por nome o nome dun protocolo máis Client (por exemplo, POP3Client), son clases clientes do protocolo. Teñen métodos que permiten utilizar o protocolo a máis alto nivel.
- As clases fillas das anteriores, que teñen o nome do protocolo + S + Client (por exemplo FTPSClient) son clases clientes do protocolo sobre unha capa de seguridade baseada en TLS/SSL.
- As clases fillas das anteriores que comezan por Authenticating (por exemplo AuthenticatingIMAPClient ) teñen métodos que permiten a autenticación de clientes cos mecanismos que o protocolo correspondente proporciona para iso.
As clases Client de Apache Commons Net teñen métodos:
- connect para a conexión cun servidor
- login para o inicio dunha sesión co servidor
- logout para terminar a sesión
- disconnect para pechar a conexión
Outras bibliotecas de clases
O relevante protocolo SSH non conta con soporte das anteriores clases. A biblioteca de clases JSCH proporciona clases que se poden utilizar para desenvolver aplicacións clientes deste protocolo.
Comparativa de APIs
Resumidamente, para dar unha resposta sinxela á necesidade de uso dos protocolos estándares de rede máis habituales, cabe considerar as seguientes API.
| API | Protocolos |
|---|---|
| java.net | HTTP HTTPS FTP FTPS |
| Apache Commons Net | TELNET FTP FTPS POP3 POP3S SMTP SMTPS IMAP IMAPS NTP |
| JSCH | SSH FTPS |
Clases estándares de Java para URL
Pódese interaccionar con recursos identificados mediante unha URL coas clases estándares de Java dentro do paquete java.net
| Significado | Clases | |
|---|---|---|
| URI, URL | Identificación e localizadores | URI URL |
| Conexións a URL |
Para xestionar as conexións a recursos identificados mediante URL | URLConnection: conexión a unha URL HttpURLConnecion: subclase de URLConnection que proporciona funcionalidade específica para o protocolo HTTP. Existe tamén unha API para HTTP máis recente implementada no paquete java.net.http |
| Método | Funcionalidade |
|---|---|
| URL (String spec) | Construtor para un URL a partir da súa especificación en forma de String |
| URL (String protocol, String host, String file) URL (String protocol, String host, int port String file) |
Construtor para un URL a partir dos componentes. Protocolo corresponde ó esquema ou scheme. File corresponde á ruta ou path |
| String getProtocol() String getUserInfo() String getHost() int getPort() int getDefaultPort() String getAuthority() String getPath() String getQuery() String getFile() |
Métodos getter para diferentes partes dunha URL. Comentar que get getFIle() devolve a concatenación de getPath() máis getQuery() |
| URLConnection openConnection() | Devolve un URLConnection que se pode utilizar para obter os contidos do recurso especificado polo URL, e tamén para enviar información hacia él |
| InputStream openStream() | Abre unha conexión ó URL e devolve un InputStream que se pode utilizar para obter os seus contidos |
| Método | Funcionalidade |
|---|---|
| InputStream getInputStream() | Devolve un InputStream para ler os contidos do recurso apuntado pola URL |
| Devolve o valor da cabcera Content-Encoding que contén a codificación utilizada para o contido do recurso | |
| int getContentLength() long getContentLengthLong() |
Devolve o valor da cabecera Content-length, que indica a lonxitude en bytes dos contidos do recurso (preferible o segundo método) |
| string getContentType | Devolve o valor da cabecera Content-Type que indica o tipo de contido do recurso |
| public long getDate() | Devolve a data (ms desde o 1/1/1970 GMT) do recurso referenciado pola URL (ou 0 se é descoñecido) |
| long getExpiration() | Devolve o valor do campo da cabecera Expires |
| long getLastModified() | Devolve o valor da cabecera Last-modified que indica a data e hora da última modificación |
| String getHeaderField(String nombre) | Devolve o valor da cabecera co nome dado |
| Map<String,List<String>> getHeaderFields | Devolve un Map coas cabeceras. O valor para cada cabecera é unha lista de valores |
Clases estándares de Java para URL con HTTP
URLConnection é unha clase abstracta que permite acceder a diversos tipos de URL; e para cada un destes tipos, existe unha subclase non abstracta de URLConnection. Para conexións a URL de HTTP da forma http://, o tipo é HttpURLConnection; que a súa vez ten HttpsURLConnection para conexións URL de HTTPS da forma https://.
Estas clases permiten xestionar algúns aspectos específicos dos protocolos HTTP e HTTPS. O último ten por única diferencia relevante que tras unha fase de negociación, a información transmitida pola conexión TCP vai encriptada). Porén HttpsURLConnection encárgase da negociación previa e da encriptación e desencriptación dos datos.
Algúns dos métodos da clase HttpURLConnection con funcionalidade específica do protocolo HTTP son:
| Método | Funcionalidade |
|---|---|
| int GetResponseCode() | Devolve o código de resposta de HTTP |
| int GetResponseMessage() | Devolve a mensaxe de resposta de HTTP (xunto co código de resposta, no seu caso) |
| public abstract void disconnect() | Indica que non se van realizar máis peticións ó servidor |
| InputStream getErrorStream() | Devolve o stream de erro se fallou a conexión pero o servidor enviou información útil |
| static boolean getFollowRedirects() boolean getInstanceFollowRedirects() static void setFollowRedirects(boolean set) void setInstanceFollowRedirects(boolean followRedirects) |
Consulta / asigna para a clase / para unha instancia da clase HttpURLConnection a capacidade de seguir redireccións en caso de código de respota 3xy |
Exemplos
Telnet
TelnetClientExample
Exemplo de uso de TelnetClient oficial de Apache Commons Net.
WeatherTelnet
Exemplo de uso trivial da clase TelnetClient.
Conecta ó servidor da Universdade de Michigan no porto 3000 (um-weather.sprl.umich.edu:3000) e permítelle o usuario interactuar co servidor vía entrada por teclado.
Telnet pasando servidor e porto como parámetro
Mesmo exemplo que o anterior, pero adaptado de xeito máis xenérico, permitindo acceder a un servidor (por ip ou nome de dominio) e portos específicados como parámetros de liña de comandos.
Con estes parámetros chámase o constructor da clase TelnetClient,que ten os métodos getOutputStream() e getInputStream() para obter os streams que permiten enviar e recibir información a e desde o socket.
Neste exemplo utilízase a clase auxiliar examples.util.IOUtil para enganchar entrada e saída estándares System.in e System.out a estes streams.
Psando como parámetros rainmaker.wunderground.com e 3000 este programa fai o mesmo que o programa de exemplo anterior.
1 import java.io.IOException; 2 import org.apache.commons.net.telnet.TelnetClient; 3 import org.apache.commons.net.examples.util.IOUtil; 4 public class ClienteTelnetGenerico { 5 public static void main(String[] args) { 6 if(args.length < 2) { 7 System.out.println(“ERROR: indicar: servidor puerto”); 8 System.exit(1); 9 } 10 String servidor = args[0]; 11 int puerto = Integer.parseInt(args[1]); 12 13 TelnetClient telnet = new TelnetClient(); 14 try { 15 telnet.connect(servidor, puerto); 16 } catch (IOException e) { 17 e.printStackTrace(); 18 System.exit(2); 19 } 20 IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), 21 System.in, System.out); 22 try { 23 telnet.disconnect(); 24 } catch (IOException e) { 25 e.printStackTrace(); 26 System.exit(3); 27 } 28 } 29 }
FTP
FTPClientExample
Exemplo de uso que permite súbdas, baixadas,etc. sobre FTP, FTPS e FTP sobre HTTP.
ServerToServerFTP
Exemplo de uso que permite a transferencia dun ficheiro de servidor a servidor (de host1 a host2)
TFTPExample
Exemplo de uso de un cliente tfrp (trivial ftp é un protocolo distinto a ftp)
listaficherosftp
O seguiente programa conéctase a un servidor de FTP e proporciona unha lista dos ficheiros e directorios disponibles.
Pásaselle como parámetro de liña de comandos:
- un servidor (enderezo ip ou nome de dominio).
- un usuario e contrasinal (opcional).
Inténtase establecer o modo pasivo con clienteFTP.enterLocalPassiveMode(). O servidor podería non soportar este modo (pouco probable). Establécese o modo binario con clienteFTP.setFileType(FTP.BINARY_FILE_TYPE).
Pódese utilizar este programa para:
- conectarse a un servidor de FTP público que permita conexión anónima (por ejemplo ftp.rediris.es)
- para conectar a un servidor privado, cun nome de usuario e contrasinal.
No tocante o tratamento de error, tras calquera erro unha vez establecida a conexión, saese con return; co que se executa o bloque finally que pecha a conexión.
1 package listaficherosftp; 2 import java.io.IOException; 3 import org.apache.commons.net.ftp.FTPClient; 4 import org.apache.commons.net.ftp.FTPReply; 5 import org.apache.commons.net.ftp.FTPFile; 6 import org.apache.commons.net.ftp.FTP; 7 8 public class ListaFicherosFTP { 9 public static void main(String[] args) { 10 if (args.length < 1) { 11 System.out.println(“ERROR: indicar como parámetros:”); 12 System.out.println(“servidor usuario(opcional) contraseña(opcional)”); 13 System.exit(1); 14 } 15 String servidorFTP = args[0]; 16 String usuario = “anonymous”, password = “”; 17 if (args.length >= 2) { 18 usuario = args[1]; 19 } 20 if (args.length >= 3) { 21 password = args[2]; 22 } 23 FTPClient clienteFTP = new FTPClient(); 24 try { 25 clienteFTP.connect(servidorFTP); 26 int codResp = clienteFTP.getReplyCode(); 27 if (!FTPReply.isPositiveCompletion(codResp)) { 28 System.out.printf(“ERROR: Conexión rechazada con cód. respuesta %d.\n”,codResp); 29 System.exit(2); 30 } 31 clienteFTP.enterLocalPassiveMode(); 32 clienteFTP.setFileType(FTP.BINARY_FILE_TYPE); 33 if (usuario != null && password != null) { 34 boolean loginOK = clienteFTP.login(usuario, password); 35 if (loginOK) { 36 System.out.printf(“INFO: Login con usuario %s realizado.\n”,usuario); 37 } 38 else { 39 System.out.printf(“ERROR: Login con usuario %s rechazado.\n”,usuario); 40 return; 41 } 42 } 43 System.out.printf( 44 “INFO: Conexión establecida, mensaje de bienvenida:\n====\n%s\n====\n”,clienteFTP.getReplyString()); 45 System.out.printf(“INFO: Directorio actual en servidor: %s, contenidos:\n”,clienteFTP.printWorkingDirectory()); 46 47 FTPFile[] fichServ = clienteFTP.listFiles(); 48 for(FTPFile f: fichServ) { 49 String infoAdicFich = “”; 50 if(f.getType() == FTPFile.DIRECTORY_TYPE) { 51 infoAdicFich = “/”; 52 } 53 else if(f.getType() == FTPFile.SYMBOLIC_LINK_TYPE) { 54 infoAdicFich = “ -> “ + f.getLink(); 55 } 56 System.out.printf(“%s%s\n”, f.getName(), infoAdicFich); 57 } 58 } catch (IOException e) { 59 System.out.println(“ERROR: conectando al servidor”); 60 e.printStackTrace(); 61 return; 62 } finally { 63 if (clienteFTP != null) { 64 try { 65 clienteFTP.disconnect(); 66 System.out.println(“INFO: conexión cerrada.”); 67 } catch (IOException e) { 68 System.out.println(“AVISO: no se pudo cerrar la conexión.”); 69 } 70 } 71 } 72 } 73 }