Anda di halaman 1dari 19

Los cliente y servidor ejemplos en el Captulo 2 demuestran el modelo bsico

para
la programacin con sockets en Java. El siguiente paso es aplicar estos
conceptos en diversas
modelos de programacin, como la multitarea , sin bloqueo de E / S , y la
radiodifusin .
4.1 multitarea
Nuestro servidor de eco TCP bsica del captulo 2 se ocupa de un cliente a la
vez. Si un cliente se conecta
mientras que otro que ya est siendo reparado , el servidor no tendr eco los
datos del nuevo cliente hasta
que ha terminado con el cliente actual , aunque el nuevo cliente ser capaz de
enviar datos a medida
pronto como se conecta. Este tipo de servidor se conoce como un servidor
iterativo . Servidores iterativos manejan
clientes de forma secuencial , terminando con un cliente antes de prestar
servicio al siguiente. Funcionan mejor
para aplicaciones en las que cada cliente requiere una cantidad pequea ,
limitada de conexin del servidor
tiempo ; Sin embargo , si el tiempo para manejar un cliente puede ser largo , la
espera experimentado por subsiguiente
clientes pueden ser inaceptables .
Para demostrar el problema, agregue a 10 segundos de sueo usando
Thread.sleep () despus del Zcalo
llamada al constructor en TCPEchoClient.java y experimentar con varios
clientes de forma simultnea
acceder al servidor de eco TCP. Aqu el llamado sueo simula una operacin
que toma significativa
tiempo , tales como archivo lenta o red I / O. Tenga en cuenta que un nuevo
cliente debe esperar a que todos conectados ya -
clientes para completar antes de que llegue el servicio.
Lo que necesitamos es un poco de terreno para cada conexin a proceder de
forma independiente , sin
interferir con otras conexiones . Hebras Java proporcionan exactamente eso:
un mecanismo conveniente

permitiendo a los servidores para manejar varios clientes al mismo tiempo. El
uso de hilos , una sola aplicacin
puede trabajar en varias tareas al mismo tiempo , como si varias copias de la
mquina virtual de Java eran
correr . ( En realidad , una sola copia de la JVM es compartida o multiplexado
entre los diferentes
threads . ) En nuestro servidor de eco , podemos darle la responsabilidad de
cada cliente a un independiente
la ejecucin de subprocesos. Todos los ejemplos que hemos visto hasta ahora
consisten de una sola hebra , que
simplemente ejecuta el mtodo main ().
En esta seccin se describen dos enfoques para la codificacin de servidores
concurrentes , a saber , threadper -
cliente , donde un nuevo hilo se genera para manejar cada conexin de cliente
, y del grupo de subprocesos ,
donde las conexiones se asignan a un conjunto prespawned de hilos . Vamos a
describir tambin la
integrado en las instalaciones de Java que simplifican el uso de estas
estrategias para servidores multiproceso.
4.1.1 Threads Java
Java proporciona dos mtodos para realizar una tarea en un nuevo tema: 1 )
definir una subclase
de la clase Thread con un mtodo run () que realiza la tarea , y crear instancias
de ella; o 2 )
la definicin de una clase que implementa la interface Runnable con un mtodo
run ( ) que realiza
la tarea, y que pasa una instancia de esa clase al constructor Thread. En
cualquier caso , la
nuevo hilo no comienza la ejecucin hasta que su mtodo start ( ) se invoca . El
primer enfoque
slo se puede utilizar para las clases que no se extienden ya alguna otra clase ;
por lo tanto, nos atenemos
con el segundo enfoque, que es siempre aplicable . La interfaz Ejecutable
contiene un solo
mtodo de prototipo :
interface Runnable {
void run ();
}
Cuando se invoca el mtodo start () de una instancia de Thread, la JVM hace
que el
El mtodo de instancia run () que se ejecutar en un nuevo tema, al mismo
tiempo que todos los dems.
Mientras tanto , el hilo original regresa de su llamada a start () y contina su
ejecucin
de forma independiente. (Nota que se ejecutan llamando directamente ( ) no
crea un nuevo hilo , sino que el
mtodo run () es simplemente ejecuta en el hilo de la persona que llama , al
igual que cualquier otra llamada al mtodo. ) El
declaraciones de mtodo run () de cada hilo se entrelazan de una manera no
determinista , por lo que en
en general , no es posible predecir con exactitud el orden en que las cosas van
a suceder en diferentes
roscas .
En el siguiente ejemplo , ThreadExample.java implementa la interfaz con un
Ejecutable
mtodo run () que imprime repetidamente un saludo a la corriente de salida del
sistema.
ThreadExample.java
0 java.util.concurrent.TimeUnit importacin;
1
2 clase pblica implementa ThreadExample Ejecutable {
3
4 saludo private String ; / / Mensaje de imprimir a la consola

5
6 ThreadExample pblico ( saludo String) {
7 this.greeting = saludo ;
8 }
9
10 public void run () {
11 while ( true) {
12 System.out.println ( Thread.currentThread () getName () + ":" . + Felicitacin) ;
13 try {
14 / / Sleep 0 a 100 milisegundos
15 TimeUnit.MILLISECONDS.sleep ((( largo) Math.random () * 100 ));
16 } catch ( InterruptedException e) {
17 } / / No debe suceder
18 }
19 }
20
21 principales ( String [ ] args ) { static void pblicas
. 22 nuevo hilo ( nueva ThreadExample ("Hola ")) start () ;
. 23 nuevo hilo ( nueva ThreadExample ( "Aloha ")) start () ;
. 24 nuevos Tema ( nueva ThreadExample ( " Ciao ")) start () ;
25 }
26 }
ThreadExample.java
. 1 Declaracin de aplicacin de la interface Runnable : Lnea 2
Desde ThreadExample implementa el interface Runnable , se puede pasar al
constructor
de hilo. Si ThreadExample no proporciona un mtodo run () , el compilador se quejar .
. 2 variables de miembro y constructor : Lneas 4-8
Cada instancia de ThreadExample contiene su propia cadena de saludo.
. 3 run () : lneas 10-19
Loop para siempre la realizacin de :
? Escriba el nombre y la instancia hilo saludo : Lnea 12
El mtodo Thread.currentThread esttica ( ) devuelve una referencia a la rosca de la
cual
se le llama, y getName ( ) devuelve una cadena que contiene el nombre de ese hilo.
? Suspender hilo : lneas 13-17
Despus de imprimir el mensaje de saludo de su ejemplo, cada hilo tiene capacidad
para una cantidad aleatoria
de tiempo ( entre 0 y 100 milisegundos ) llamando al mtodo esttico Thread.sleep (),
que tiene el nmero de milisegundos a dormir como un parmetro. Math.random ( )
devuelve
un doble aleatorio entre 0,0 y 1,0. Thread.sleep () puede ser interrumpida por otra
hilo , en cuyo caso se produce una InterruptedException . Nuestro ejemplo no incluye
una llamada de interrupcin , por lo que la excepcin no va a suceder en esta
aplicacin.
4 principales ( ): . Lneas 21-25
Cada uno de los tres estados en main () hace lo siguiente: 1 ) crea una nueva
instancia de
ThreadExample con una cadena de saludo diferente, 2 ) pasa a esta nueva
instancia en el constructor
de hilo, y 3) llama al mtodo start () de la nueva instancia de rosca . Cada hilo
ejecuta independientemente del mtodo () de ThreadExample plazo, mientras
que el hilo principal termina.
Tenga en cuenta que la JVM no termina hasta que todos nondaemon ( ver
Tema API ) Hilos
terminar.
Tras la ejecucin , un entrelazado de los tres mensajes de felicitacin se
imprime en la consola.
El entrelazado exacta de los nmeros depende de varios factores que en
general se
observable. Los hilos son perfectos para la implementacin de servidores como
nuestro ejemplo, en el que cada
procesamiento del cliente es independiente de la que se proporciona a todos
los dems clientes. Sin embargo , se trata de una diferente
historia cuando el procesamiento de cliente requiere actualizacin de
informacin que se comparte a travs de las discusiones
en el servidor . En ese caso , el gran cuidado se debe tomar para asegurarse
de que los diferentes temas son adecuadamente
sincronizada con respecto a los datos compartidos ; de lo contrario , la
informacin compartida puede obtener
en un estado inconsistente , y por otra parte , el problema puede ser muy difcil
de rastrear. Un tratamiento completo
de las tcnicas y las instalaciones para la concurrencia requeriran un libro
propio. El libro
por Goetz et al. [ 9 ] , por ejemplo , es excelente .
4.1.2 Protocolo de Servidor
Desde el servidor multitarea se acerca , vamos a describir son independientes
el protocolo cliente - servidor en particular , queremos ser capaces de utilizar el
mismo protocolo de aplicacin
para ambos. El cdigo para el protocolo de eco se da en el EchoProtocol clase.
Esta clase encapsula el procesamiento por cliente en el mtodo
handleEchoClient esttica ().
Este cdigo es casi idntica a la porcin de manipulacin de conexin - de
TCPEchoServer.java ,
excepto que una capacidad de registro (que se describe en breve) ha sido
aadido ; el mtodo de toma referencias
al Socket cliente y la instancia Logger como argumentos.
La clase implementa Ejecutable ( el mtodo run () simplemente llama a manejar
EchoClient ()
con el zcalo de la instancia y referencias Logger ) , para que podamos crear
un hilo que de forma independiente
ejecuta run () . Alternativamente, el procesamiento del protocolo de servidor
puede ser invocado llamando
el mtodo esttico directamente ( pasndole el zcalo y referencias Logger ) .
EchoProtocol.java
0 java.io.IOException importacin;
1 java.io.InputStream importacin;
2 java.io.OutputStream importacin;
3 java.net.Socket importacin;
4 java.util.logging.Level importacin;
5 java.util.logging.Logger importacin;
6
7 public class EchoProtocol implementa Ejecutable {
8 private static int BUFSIZE final = 32 ; / / Tamao ( en bytes) del bfer de E / S

9 clntSock Socket privado; / / Socket conectar con el cliente
10 Logger logger privado; / / Registrador del servidor
11
12 EchoProtocol pblico ( Socket clntSock , Logger logger ) {
13 this.clntSock = clntSock ;
14 this.logger = registrador ;
15 }
16
17 public static void handleEchoClient ( Socket clntSock , Logger logger ) {
18 try {
19 / / Obtener la entrada y la salida I / O fluye de socket
20 InputStream in = clntSock.getInputStream ();
21 OutputStream salida = clntSock.getOutputStream ();
22
23 int recvMsgSize ; / / Tamao del mensaje recibido
24 int totalBytesEchoed = 0 ; / / Bytes recibidos desde el cliente
25 byte [ ] = new byte echoBuffer [ BUFSIZE ] ; / / Bfer de recepcin
26 / / Recibe hasta que el cliente cierra la conexin , indicado por -1
27 while (( recvMsgSize = in.read ( echoBuffer ) ) ! = -1 ) {
28 out.write ( echoBuffer , 0 , recvMsgSize ) ;
29 totalBytesEchoed + = recvMsgSize ;
30 }
31
32 logger.info ( el "Cliente" + clntSock.getRemoteSocketAddress ( ) + " , se hizo eco de
"
33 + + ". TotalBytesEchoed bytes" );
34
35 } catch (IOException ex) {
36 logger.log ( Level.WARNING , " Excepcin en el protocolo de eco " , ex );
37 } finally {
38 try {
39 clntSock.close ( ) ;
40 } catch (IOException e) {
41 }
42 }
43 }
44
45 public void run () {
46 handleEchoClient ( clntSock , logger );
47 }
48 }
EchoProtocol.java
. 1 Declaracin de aplicacin de la interface Runnable : Lnea 7
. 2 variables de miembro y constructor : Lneas 8-15

Cada instancia de EchoProtocol contiene un zcalo para la conexin y una referencia
a la
instancia logger.
3 handleEchoClient ( ): . Lneas 17-43
Implementar el protocolo de eco :
? Recibe las corrientes de entrada / salida de la toma : lneas 20-21
? Recibir y eco : las lneas 25 a 30
Bucle hasta que la conexin est cerrada ( como se indica por lectura ( ) devolviendo -
1 ) , la escritura
lo que se reciba de nuevo inmediatamente.
? Registre los detalles de la conexin en el registro: lneas 32-33
Registre el SocketAddress del extremo remoto junto con el nmero de bytes eco .
? Controlar excepciones : la lnea 36
Entrar ninguna excepcin.
Su servidor est en marcha con miles de clientes por minuto. Hoy en los informes de
usuario
un problema. Cmo se determina lo que pas? Es el problema en el servidor ? tal
vez el
cliente est violando el protocolo. Para hacer frente a esta situacin, la mayora de los
servidores registran sus actividades. este
la prctica es tan comn que Java ahora incluye instalaciones integradas de tala en la
java.util.logging
paquete . Proporcionamos una introduccin muy bsica para el registro aqu ; Sin
embargo , tenga en cuenta que hay
muchas ms caractersticas a la tala de nivel empresarial .
Comenzamos con la clase Logger , que representa una funcin de registro que puede
ser local o
remoto. A travs de una instancia de esta clase, podemos grabar las distintas
actividades del servidor , como se muestra
en EchoProtocol . Usted puede usar varios registradores en su servidor, cada uno
sirviendo a un propsito diferente
y potencialmente comportarse de una manera diferente . Por ejemplo , es posible que
los madereros independientes para
operaciones , los mensajes de seguridad y de error. En Java cada registrador se
identifica por un nico a nivel mundial
nombrar . Para obtener una instancia del Registrador , llame al mtodo esttico fbrica
Logger.getLogger () de la siguiente manera :
Logger logger = Logger.getLogger ( "prctica" );
Esto obtiene el registrador nombrado "prctico" . Si un registrador con ese nombre no
existe, un nuevo registrador
se crea ; de lo contrario , se devuelve la instancia de registrador existente. No importa
cuntas veces usted
hacer el registrador "prctico" en su programa , se devuelve la misma instancia.
Ahora que ha tala , qu debe iniciar ? Bueno, depende de lo que est haciendo.
Si el servidor funciona correctamente , puede que no desee registrar cada paso el
servidor toma
porque el registro consume recursos tales como espacio para almacenar las entradas
del registro y del procesador del servidor
tiempo para escribir cada entrada. Por otro lado , si usted est tratando de depurar, es
posible que desee
iniciar cada paso . Para hacer frente a esto, el registro incluye por lo general la nocin
de nivel o
gravedad , de entradas de registro . La clase de Nivel encapsula la nocin de la
importancia de los mensajes .
Cada instancia de Logger tiene un nivel actual , y cada mensaje registrado tiene un
nivel asociado;
mensajes con niveles por debajo del nivel actual de la instancia se descartan (es decir
, no conectado ) . cada
nivel tiene un valor entero asociado , por lo que los niveles son comparables y se
pueden pedir . siete
instancias reconocidas por el sistema de nivel se definen ; otros , especficos del
usuario , los niveles se pueden crear,
pero rara vez hay necesidad de hacerlo . Los niveles integrados ( definidos como
campos estticos de la clase
Nivel) son: severa , advertencia, info , config , fino , fino , y ms fino .
As que al iniciar la sesin , a dnde van los mensajes? El registrador enva
mensajes a uno o ms
Los manipuladores , que " manejar " la publicacin de los mensajes. Por
defecto, un registrador tiene una sola ConsoleHandler
que imprime mensajes a System.err . Usted puede cambiar el controlador o
agregar controladores adicionales
a un registrador (por ejemplo, FileHandler ) . Tenga en cuenta que al igual que
un registrador , un controlador tiene un nivel de registro mnimo , por lo que
para un
mensaje que se publicar su nivel debe estar por encima tanto el registrador y
el umbral de los manipuladores . Los madereros
y manipuladores son altamente configurables , incluyendo su nivel mnimo .
Una caracterstica importante de Logger para nuestros propsitos es que es
thread -safe- que
Es decir, sus mtodos se pueden llamar desde diferentes hilos se ejecutan
simultneamente , sin necesidad de
sincronizacin adicional entre las personas que llaman. Sin esta caracterstica ,
los diferentes mensajes registrados
por diferentes hilos podran llegar a ser intercalado en el registro !
Registrador: Finding / Creacin
esttica Logger getLogger (String nombre )
esttica Logger getLogger (String nombre , String resourceBundleName )
Los mtodos de generador estticos devuelven el registrador de llamada ,
creando si es necesario.
Una vez que tengamos el registrador , tenemos que ... bueno ... de registro.
Logger ofrece facilidades de registro de grano fino
que diferenciar entre el nivel e incluso de contexto ( llamada al mtodo,
excepcin, etc ) de la
mensaje .
Registrador: Registro de un mensaje
void grave ( String msg )
void advertencia ( String msg )
void info ( String msg )
void config ( String msg )
vaco fino ( String msg )
vaco fino ( String msg )
anular fino ( String msg )
void entrada (String sourceclass , String sourceMethod )
void entrada (String sourceclass , String sourceMethod , param Object)
void entrada (String sourceclass , String sourceMethod , Object [ ] params )
void Salir (String sourceclass , String sourceMethod )
void Salir (String sourceclass , String sourceMethod , el resultado de Object )
void lanzamiento (String sourceclass , String sourceMethod , Throwable torcer)
void log ( nivel Nivel , String msg )
(nivel Nivel , String msg , Throwable torcer) void registro


Los severa () , advirtiendo ( ) , etc mtodos de registro el mensaje dado en el nivel
especificado por
el nombre del mtodo . La entrada () y salida () mtodos log entrar y salir de lo dado
mtodo de la clase dada . Tenga en cuenta que puede especificar opcionalmente
informacin adicional
tales como los parmetros y valores de retorno . El lanzamiento () mtodo registra una
excepcin arrojada en un
mtodo especfico . El log () mtodos proporcionan un mtodo de registro genrico
cuando el mensaje de nivel,
y (opcionalmente) la excepcin se puede registrar . Tenga en cuenta que existen
muchos otros mtodos de registro ; somos
slo observando los principales tipos de aqu .
Es posible que desee personalizar nuestro registrador estableciendo el nivel de
registro mnimo o los controladores
para los mensajes de registro.
Registrador: Configuracin / Obtener el nivel y manipuladores
Manejador [ ] getHandlers ()
void addHandler ( manejador Manejador )
void removeHandler ( manejador Manejador )
Nivel getLevel ( )
void setLevel (Nivel newLevel )
boolean isLoggable (nivel Nivel)
El mtodo de los getHandlers () devuelve una matriz de todos los manejadores
asociados con el registrador.
El addHandler () y removeHandler ( ) mtodos permiten adicin / eliminacin de los
manipuladores a / de
el registrador. El getLevel () y setLevel ( ) los mtodos get / set en el nivel de registro
mnimo. la
mtodo isLoggable () devuelve true si el nivel dado se registrar por el registrador.
Ahora estamos listos para introducir algunos enfoques diferentes para servidores
concurrentes.
4.1.3 Tema -per -Client
En un servidor de hilo por cliente , se crea un nuevo hilo para manejar cada conexin.
El servidor
ejecuta un bucle que se ejecuta siempre, la escucha de conexiones en un puerto
especificado y repetidamente
aceptar una conexin entrante de un cliente y entonces lanzando un nuevo hilo para
manejar
esa conexin.
TCPEchoServerThread.java implementa la arquitectura del hilo por cliente . Es muy
similar
al servidor iterativo , el uso de un solo bucle para recibir y solicitudes de los clientes
proceso . La principal
diferencia es que crea un hilo para manejar la conexin en lugar de manipular
directamente.
( Esto es posible porque EchoProtocol implementa la interfaz Ejecutable . ) Por lo tanto
, cuando varios
clientes se conectan en aproximadamente el mismo tiempo , los posteriores no tienen
que esperar a que el servidor
para terminar con los anteriores antes de que lleguen servicio. En su lugar, todos
parecen recibir el servicio
( aunque a un ritmo algo ms lento ) al mismo tiempo .
TCPEchoServerThread.java
0 java.io.IOException importacin;
1 java.net.ServerSocket importacin;
2 java.net.Socket importacin;
3 java.util.logging.Logger importacin;
4
5 public class TCPEchoServerThread {
6
7 principales ( String [ ] args ) static void pblicas lanza IOException {
8
9 if ( args.length ! = 1 ) {/ / Test para la correcta # de args
10 throw new IllegalArgumentException ( " Parmetro ( s ) : <Puerto> ");
11 }
12
13 int echoServPort = Integer.parseInt ( args [ 0]); / / Puerto del servidor
14
15 / / Crear un socket de servidor para aceptar solicitudes de conexin de cliente
16 ServerSocket servSock = new ServerSocket ( echoServPort );
17
18 Logger logger = Logger.getLogger ( "prctica" );
19
20 / / Ejecute siempre, aceptar y generar un subproceso para cada conexin
21 while ( true) {
22 Socket clntSock = servSock.accept (); / / Bloque de espera para la conexin
23 / / hilo de Spawn para manejar nueva conexin
24 hilo de rosca = new Thread ( nueva EchoProtocol ( clntSock , logger ));
25 hilo.start ();
26 logger.info ( " Creado y comenz Tema " + thread.getName ());
27 }
28 / * NO ALCANZADO * /
29 }
30 }
TCPEchoServerThread.java
. 1 de anlisis de parmetros y el servidor de creacin socket / logger : Lneas 9-18
2 Loop para siempre , manejando las conexiones entrantes : . Lneas 21-27
? Aceptar una conexin entrante : Lnea 22
? Crea una nueva instancia de Thread para manejar la nueva conexin : Lnea 24

Desde EchoProtocol implementa el interface Runnable , podemos dar nuestra
nueva instancia
al constructor Thread, y el nuevo hilo ejecutar el mtodo ( ) de recorrido
EchoProtocol (que llama a handleEchoClient ()) cuando se invoca start () .
? Inicie el nuevo hilo para la conexin y registrarla : lneas 25-26
El mtodo getName () de Thread devuelve una cadena que contiene un
nombre para el nuevo hilo.
4.1.4 Tema de la piscina
Cada nuevo hilo consume recursos del sistema : generar un subproceso tiene
ciclos de CPU y cada
hilo tiene sus propias estructuras de datos (por ejemplo , pilas ) que consumen
memoria del sistema. Adems ,
cuando uno subproceso se bloquea , la JVM guarda su estado , selecciona otro
hilo para funcionar , y restauraciones
el estado de la rosca elegido en lo que se llama un cambio de contexto . A
medida que el nmero de hilos
aumenta , ms y ms recursos del sistema se consumen por encima de la
cabeza del hilo. Eventualmente , la
sistema est gastando ms tiempo a tratar con el cambio de contexto y la
gestin de hilos de
con conexiones de servicio . En ese momento, la adicin de un hilo adicional
puede en realidad aumentar
tiempo de servicio al cliente.
Podemos evitar este problema al limitar el nmero total de temas y la
reutilizacin de las discusiones .
En lugar de desove un nuevo hilo para cada conexin , el servidor crea un
grupo de subprocesos en
puesta en marcha por el desove de un nmero fijo de hilos . Cuando una nueva
conexin de cliente llega a la
servidor , se le asigna a un subproceso del grupo . Cuando el hilo termina con
el cliente , se
devuelve a la piscina, listo para manejar otra solicitud . Las solicitudes de
conexin que llegan cuando todos
subprocesos del grupo estn ocupados estn en la cola para ser atendida por
el siguiente hilo disponible.
Al igual que el servidor de hilo - por - cliente, un servidor de grupo de
subprocesos comienza mediante la creacin de un ServerSocket .
A continuacin se reproduce de N hilos , cada uno de los bucles que siempre,
aceptar conexiones desde el (compartido )
Instancia ServerSocket . Cuando mltiples hilos simultneamente llaman
accept () en el mismo servidor
Instancia de Socket , todo lo que el bloque hasta que se establezca una
conexin. A continuacin, el sistema selecciona uno
hilo , y la instancia de Socket para la nueva conexin slo se devuelve en ese
hilo. el otro
hilos permanecen bloqueadas hasta que se establezca la conexin siguiente y
otro afortunado ganador es
elegido .
Dado que cada subproceso del grupo bucles para siempre , conexiones de
procesamiento de uno a uno, un
servidor de grupo de subprocesos es realmente como un conjunto de
servidores iterativos. A diferencia del servidor de hilo por cliente ,
un hilo de rosca de la piscina no termina cuando se acaba con el cliente. En
lugar de ello , se inicia
otra vez , el bloqueo de accept () . Un ejemplo del paradigma de grupo de
subprocesos se muestra en la
TCPEchoServerPool.java .
TCPEchoServerPool.java
0 java.io.IOException importacin;
1 java.net.ServerSocket importacin;
2 java.net.Socket importacin;
3 java.util.logging.Level importacin;
4 java.util.logging.Logger importacin;
5

6 public class TCPEchoServerPool {
7
8 principales ( String [ ] args ) static void pblicas lanza IOException {
9
10 if ( args.length ! = 2 ) {/ / Test para la correcta # de args
11 throw new IllegalArgumentException ( " Parmetro ( s ) : <Puerto> <Threads> ");
12 }
13
14 int echoServPort = Integer.parseInt ( args [ 0]); / / Puerto del servidor
15 int threadPoolSize = Integer.parseInt ( args [ 1 ]);
16
17 / / Crear un socket de servidor para aceptar solicitudes de conexin de cliente
18 servSock ServerSocket final = nuevo ServerSocket ( echoServPort );
19
20 Logger logger final = Logger.getLogger ( "prctica" );
21
22 / / generar un nmero fijo de hilos para los clientes de servicios
23 for ( int i = 0 ; i < threadPoolSize ; i + + ) {
24 hilo de rosca = new Thread ( ) {
25 public void run () {
26 while ( true) {
27 try {
28 Socket clntSock = servSock.accept (); / / Espera a una conexin
29 EchoProtocol.handleEchoClient ( clntSock , logger ); / / Manjela
30 } catch (IOException ex) {
31 logger.log ( Level.WARNING , "Cliente no aceptar", ex );
32 }
33 }
34 }
35 } ;
36 hilo.start ();
37 logger.info ( " Creado y comenz Tema =" + thread.getName ());
38 }
39 }
40 }
TCPEchoServerPool.java
1 Configuracin : . Lneas 10-20
El nmero de puertos para escuchar y el nmero de roscas son a la vez pasan como
argumentos
a main (). Despus de analizar los creamos las instancias ServerSocket y maderero .
Tenga en cuenta que
ambos tienen que ser declaradas final, debido a que se hace referencia dentro de la
clase annima
instancia creada a continuacin.
. 2 Crear y activar threadPoolSize nuevos temas : lneas 23-38
Para cada iteracin del bucle , se crea una instancia de una clase annima que se
extiende de rosca .
Cuando se llama al mtodo start () de la instancia , el subproceso ejecuta el mtodo
run ()
de esta clase annima . El mtodo run ( ) crea un bucle para siempre, aceptar
una conexin y
luego drselo a EchoProtocol para el servicio.
? Aceptar una conexin entrante : Lnea 28
Dado que hay N diferentes subprocesos que se ejecutan el mismo bucle , hasta
N hilos pueden
ser bloqueado en servSock de accept () , a la espera de una conexin entrante
. El sistema de
asegura que slo un hilo consigue un Socket para cualquier conexin
particular. Si no hay temas
se bloquean en aceptar ( ) cuando se establece una conexin de cliente ( es
decir, si son todos
ocupado el servicio otras conexiones ) , la nueva conexin se pone en cola por
el sistema hasta
la siguiente llamada a accept () ( vase la Seccin 6.4.1 ) .
? Pase el socket de cliente a EchoProtocol.handleEchoClient : Lnea 29
El mtodo handleEchoClient () encapsula el conocimiento de los detalles del
protocolo . registra
la conexin cuando termina , as como las excepciones encontradas en el
camino .
? Maneje excepcin de accept (): la lnea 31
Dado que las discusiones se vuelven a utilizar , la solucin de grupo de
subprocesos slo paga los gastos generales de la rosca
creacin N veces , independientemente del nmero total de conexiones de
cliente . Ya que controlamos
el nmero mximo de subprocesos que se ejecutan de forma simultnea , se
puede controlar la programacin y
consumo de recursos . Por supuesto , si desovan muy pocos temas , todava
podemos tener clientes esperando
un largo tiempo para el servicio ; Por lo tanto, el tamao del grupo de
subprocesos necesita ser sintonizado a la carga ,
de modo que se minimiza el tiempo de conexin del cliente . Lo ideal sera un
centro de despacho que
ampla el grupo de subprocesos ( hasta un lmite ) cuando la carga aumenta, y
se contrae a minimizar
sobrecarga en momentos en que la carga es ligera . Resulta que Java tiene
slo una instalacin de este tipo ; nosotros
describirlo en la siguiente seccin.
4.1.5 gestionada por el sistema de Despacho : El interfaz Ejecutor
En los apartados anteriores hemos visto que encapsula los detalles del cliente-
servidor
protocolo (como en EchoProtocol.java ) nos permite utilizar diferentes "
despacho " mtodos con el mismo
implementacin del protocolo (por ejemplo , TCPEchoServerThread.java y
TCPEchoServerThreadPool.java ) . en
hecho lo mismo es cierto para los propios mtodos de secuenciacin . El
Ejecutor interfaz (parte
del paquete java.util.concurrent ) representa un objeto que ejecuta instancias
ejecutables
de acuerdo con alguna estrategia , que puede incluir detalles acerca de la
puesta en cola y la programacin , o cmo
se seleccionan los trabajos para su ejecucin. La interfaz Ejecutor especifica un
mtodo nico :
interfaz Ejecutor {
void ejecutar (tarea Ejecutable );
}
Java proporciona un nmero de implementaciones incorporadas de Ejecutor
que son convenientes y
sencillo de utilizar , y otros que son ampliamente configurable . Algunas de
ellas ofrecen el manejo de
detalles sucios como el mantenimiento del hilo . Por ejemplo , si un hilo se
detiene debido a una no detectada
excepcin u otra falla , se reproducen automticamente un nuevo hilo para
reemplazarlo.


La interfaz ExecutorService extiende Ejecutor para proporcionar una instalacin ms
sofisticada
que permite que un servicio sea cerrado, ya sea con gracia o abrupta .
ExecutorService tambin permite
para las tareas para devolver un resultado , a travs de la interfaz invocable , que es
como Ejecutable , slo con un
valor de retorno.
Las instancias de ExecutorService se pueden obtener llamando diversos mtodos de
generador estticos de
los ejecutores de clase conveniencia. El TCPEchoServerExecutor.java programa
ilustra el uso
de los servicios bsicos del ejecutor .
TCPEchoServerExecutor.java
0 java.io.IOException importacin;
1 java.net.ServerSocket importacin;
2 java.net.Socket importacin;
3 java.util.concurrent.Executor importacin;
4 java.util.concurrent.Executors importacin;
5 java.util.logging.Logger importacin;
6
7 public class TCPEchoServerExecutor {
8
9 principales ( String [ ] args ) static void pblicas lanza IOException {
10
11 if ( args.length ! = 1 ) {/ / Test para la correcta # de args
12 throw new IllegalArgumentException ( " Parmetro ( s ) : <Puerto> ");
13 }
14
15 int echoServPort = Integer.parseInt ( args [ 0]); / / Puerto del servidor
16
17 / / Crear un socket de servidor para aceptar solicitudes de conexin de cliente
18 ServerSocket servSock = new ServerSocket ( echoServPort );
19
20 Logger logger = Logger.getLogger ( "prctica" );
21
22 servicio Ejecutor = Executors.newCachedThreadPool (); / / Despacho svc
23
24 / / Ejecute siempre, aceptar y generar un subproceso para cada conexin
25 while ( true) {
26 Socket clntSock = servSock.accept (); / / Bloque de espera para la conexin
27 service.execute ( nueva EchoProtocol ( clntSock , logger ));
28 }
29 / * NO ALCANZADO * /
30 }
31 }
TCPEchoServerExecutor.java

1 Configuracin : . Lneas 11-20
El puerto es el nico argumento . Creamos las instancias ServerSocket y
Logger como antes ;
ellos no necesitan ser declarados definitivos aqu, porque no necesitamos un
Tema annimo
subclase .
. 2 Consiga un Ejecutor: la lnea 22
El mtodo de fbrica newCachedThreadPool esttica () de Ejecutores de clase
crea una instancia
de ExecutorService . Cuando su mtodo execute ( ) se invoca con una
instancia de Runnable , el
servicio ejecutor crea un nuevo subproceso para manejar la tarea de ser
necesario. Sin embargo, primero
intenta volver a utilizar un tema existente . Cuando un subproceso ha estado
inactivo durante al menos 60 segundos,
se retira de la piscina . Esta es casi siempre va a ser ms eficaz que cualquiera
de
los dos ltimos TCPEchoServer * ejemplos.
3 Loop para siempre, de aceptar conexiones y ejecutar ellos : . Lneas 25-28
Cuando llega una nueva conexin , se crea una nueva instancia EchoProtocol y
se pasa al
mtodo execute () de servicio, que sea lo entrega a un hilo o ya existente
crea un nuevo hilo para manejarlo. Tenga en cuenta que en el estado
estacionario , el grupo de subprocesos en cach
Servicio Ejecutor acaba teniendo sobre el nmero correcto de hilos , de modo
que cada hilo
Estancias ocupado y la creacin / destruccin de las discusiones es rara.
Una vez que tenemos un servidor diseado para usar Ejecutor para el envo de
los clientes , podemos cambiar
envo de estrategias simplemente cambiando el tipo de ejecutor creamos una
instancia . Por ejemplo ,
si se quiere utilizar un grupo de subprocesos de tamao fijo como en nuestro
ejemplo TCPEchoServerPool.java , es una
cuestin de cambiar una lnea asociada a la configuracin del servicio de envo
:
Servicio Ejecutor = Executors.newFixedThreadPool ( threadPoolSize );
Podramos cambiar a un solo hilo para ejecutar todas las conexiones ya sea
especificando un tamao de grupo
de 1 , o mediante la siguiente llamada:
Servicio Ejecutor = Executors.newSingleThreadExecutor ();
En el enfoque de albacea , si el nico "trabajador " rosca muere debido a algn
fallo, el Ejecutor
lo reemplazar con un nuevo hilo. Adems, las tareas se ponen en cola en el
interior del Ejecutor , en lugar de ser
en cola en el interior del sistema de red , como lo fueron en nuestro servidor
original. Tenga en cuenta que slo hemos
araado la superficie del paquete de concurrencia de Java.


Bloqueo y tiempos de espera
Socket llamadas de E / S pueden bloquear por varias razones. Los mtodos de
entrada de datos read () y recibir (bloque)
si los datos no est disponible. A write () en un socket TCP puede bloquear si no hay
espacio suficiente para
amortiguar los datos transmitidos. El aceptar (mtodo) de ServerSocket () y el
constructor de Socket
tanto hasta que se haya establecido una conexin de bloque (ver seccin 6.4).
Mientras tanto, a largo ida y vuelta
veces, las conexiones de alta tasa de errores y servidores lentos (o fallecidos) pueden
causar la conexin
establecimiento que tome mucho tiempo. En todos estos casos, el mtodo devuelve
slo despus de la
solicitud ha sido satisfecho. Por supuesto, una llamada a un mtodo bloqueado
detiene el progreso de la aplicacin
(y hace que el hilo que se est ejecutando es intil).
Qu pasa con un programa que tiene otras tareas a ejecutar un trabajo a la espera
de la terminacin de llamadas
(por ejemplo, la actualizacin del cursor "ocupado" o responder a las solicitudes del
usuario)? Estos programas pueden tener
hay tiempo para esperar en una llamada al mtodo bloqueado. Qu pasa con la
prdida de datagramas UDP? Si bloqueamos espera
para recibir un datagrama y se pierde, se podra bloquear indefinidamente. Aqu
exploramos las diversas
bloqueando mtodos y enfoques para limitar el bloqueo de comportamiento. En el
captulo 5 que encontraremos
los ms poderosos medios disponibles a travs del paquete NIO sin bloqueo.
4.2.1 accept (), read (), y recibir ()
Para estos mtodos, podemos establecer un lmite en el tiempo mximo (en
milisegundos) para bloquear, utilizando
mtodo de Socket, ServerSocket y DatagramSocket la setSoTimeout (). Si el tiempo
especificado
transcurre antes de que se devuelva el mtodo, un InterruptedIOException se lanza.
Para las instancias de Socket,
tambin podemos utilizar el mtodo disponible () de InputStream de la toma para
comprobar si hay datos disponibles
antes de llamar a read ().
4.2.2 Conexin y Escritura
Los intentos de constructor Socket para establecer una conexin con el host y
el puerto suministran como
argumentos , el bloqueo hasta que la conexin se establece , o un tiempo de
espera impuesto por el sistema de
ocurre . Desafortunadamente , el tiempo de espera impuesto por el sistema es
largo , y Java no proporciona ningn
medios de acortarlo . Para solucionar este problema , llame al constructor sin
parmetros para el zcalo , que devuelve
una instancia sin conectar. Para establecer una conexin , llame al mtodo
connect () en el recin
construida zcalo y especificar tanto un extremo remoto y tiempo de espera (
milisegundos ) .
A write () llame cuadras hasta el ltimo byte escrito se copia en la
implementacin de TCP
bfer local ; si el espacio de memoria intermedia disponible es menor que el
tamao de la escritura , algunos datos deben ser
transferido con xito hasta el otro extremo de la conexin antes de la llamada a
escribir ( ) devolver
( vase la seccin 6.1 para ms detalles) . Por lo tanto , la cantidad de tiempo
que una de escritura ( ) puede bloquear es en ltima instancia
controlado por la aplicacin receptora . Desafortunadamente , Java
actualmente no ofrece ninguna
manera de hacer que un write ( ) para el tiempo de espera , ni puede ser
interrumpido por otro hilo. Por lo tanto ,
cualquier protocolo que enva una gran cantidad suficiente de datos a travs de
una instancia de Socket puede bloquear para
una cantidad ilimitada de tiempo. ( Consulte la seccin 6.2 para una discusin
sobre el potencialmente desastroso
consecuencias de esto. )

4.2.3 Limitacin de Per-Time Client
Supongamos que queremos implementar el protocolo de eco con un lmite en la
cantidad de tiempo necesario
para dar servicio a cada cliente. Es decir, se define un objetivo, lmite de tiempo, y
aplicar el Protocolo de
de tal manera que despus de milisegundos timelimit, se termina la instancia de
protocolo. El protocolo
ejemplo realiza un seguimiento de la cantidad de tiempo restante, y utiliza
setSoTimeout () para asegurarse de que
sin leer () llamada se bloquea durante ms de ese tiempo. Puesto que no hay
manera de enlazado la duracin
de un write () llamada, realmente no podemos garantizar que el plazo se
mantendr. Sin embargo, TimelimitEchoProtocol.
java implementa este enfoque; usarlo con TCPEchoServerExecutor.java,
simplemente cambiar la segunda lnea del cuerpo del bucle while para:
service.execute (nueva TimeLimitEchoProtocol (clntSock, logger));
Una vez ms, el captulo 5 cubrir los mecanismos ms poderosos que pueden
limitar el tiempo que los hilos
puede bloquear-en todas las llamadas de E / S, incluyendo las escrituras-que
utilizan las instalaciones del paquete NIO.


TimeLimitEchoProtocol.java
0 java.io.IOException importacin;
1 java.io.InputStream importacin;
2 java.io.OutputStream importacin;
3 java.net.Socket importacin;
4 java.util.logging.Level importacin;
5 java.util.logging.Logger importacin;
6
7 clase TimelimitEchoProtocol implementa Ejecutable {
8 private static int BUFSIZE final = 32 ; / / Tamao ( bytes ) de tampn
9 private static final String timeLimit = " 10000" ; / Lmite / Default ( ms)
10 static final String TIMELIMITPROP privado = " timeLimit "; / / Propiedad
11
12 private int esttica lmite de tiempo ;
13 clntSock Socket privado;
14 Logger logger privado;
15
16 TimelimitEchoProtocol pblico ( Socket clntSock , Logger logger ) {
17 this.clntSock = clntSock ;
18 this.logger = registrador ;
19 / / Obtener el lmite de tiempo a partir de las propiedades del sistema o
tomar el valor por defecto
20 timelimit = Integer.parseInt ( System.getProperty ( TIMELIMITPROP ,
timeLimit ));
21 }
22
23 public static void handleEchoClient ( Socket clntSock , Logger logger ) {
24
25 try {
26 / / Obtener la entrada y la salida I / O fluye de socket
27 InputStream in = clntSock.getInputStream ();
28 OutputStream salida = clntSock.getOutputStream ();
29 int recvMsgSize ; / / Tamao del mensaje recibido
30 int totalBytesEchoed = 0 ; / / Bytes recibidos desde el cliente
31 byte [ ] = new byte echoBuffer [ BUFSIZE ] ; / / Bfer de recepcin
32 de largo endTime = System.currentTimeMillis () + lmite de tiempo ;
33 int timeBoundMillis = timelimit ;

34
35 clntSock.setSoTimeout (timeBoundMillis);
36 / / Recibe hasta que el cliente cierra la conexin, indicado por -1
37 while ((timeBoundMillis> 0) && / / catch valores cero
38 ((recvMsgSize = in.read (echoBuffer))! = -1)) {
39 out.write (echoBuffer, 0, recvMsgSize);
40 totalBytesEchoed + = recvMsgSize;
41 timeBoundMillis = (int) (endTime - System.currentTimeMillis ());
42 clntSock.setSoTimeout (timeBoundMillis);
43}
44 logger.info (el "Cliente" + clntSock.getRemoteSocketAddress () +
45 ", se hicieron eco de" "bytes". + TotalBytesEchoed +);
46} catch (IOException ex) {
47 logger.log (Level.WARNING, "Excepcin en el protocolo de eco", ex);
48}
49}
50
51 public void run () {
52 handleEchoClient (this.clntSock, this.logger);
53}
54}

La clase TimelimitEchoProtocol es similar a la clase EchoProtocol, excepto que se
los intentos de delimitar el tiempo total de conexin de eco puede existir a 10
segundos. En el momento
el mtodo handleEchoClient () se invoca, el plazo se calcula utilizando la hora actual y
el tiempo definido. Despus de cada read (), el tiempo transcurrido entre la hora actual
y la fecha lmite es
calcula, y el tiempo de espera de toma se establece en el tiempo restante.

Anda mungkin juga menyukai