Anda di halaman 1dari 56

1

VRML 2.0 con Java CAPÍTULO 19

La Implementación de un
Servidor Multiusuario
2

VRML 2.0 con Java CAPÍTULO 19

Contenido CAPÍTULO 19

• Esquema del servidor


• Red de Java
• El Registro
• Registro de Usuarios: El usuario del servidor
• Chat, el filtro, y actualización
• El montaje de las piezas
• Optimización del servidor
• Ejecutar el servidor

Esquema del servidor

Para mantener la aplicación sencilla, vamos a aprobar el monolítico, modelo de


3

VRML 2.0 con Java CAPÍTULO 19

servidor central. Multicasting bajo Java 1.0 fue en el mejor de un poco de


problema, por lo que vamos a evitar el desorden que haciendo caso omiso de
todo por ahora. Java 1.1 incluye una MultiCastSocket clase, por lo que este
elemento se deja a una futura mejora. Debemos tener en cuenta que en el
futuro podría ser el servidor de particionado la hora de diseñar el código.

Determinar la forma de escribir el código que se logra mejor mirando a las


necesidades del cliente y el protocolo. La solicitud en su conjunto es un
servidor, sino que también se compone de muchas partes. Cada una de estas
partes es un servidor en su propio derecho. A fin de aliviar la confusión, de
ahora en adelante voy a referirme a cada una de las partes como un servidor,
en lugar de toda la aplicación de servidor a la vez. En cuanto a la especulación,
podemos ver que vamos a necesitar cuatro partes separadas: un servidor TCP
para el Registro y, a continuación, tres UDP para los servidores de
actualizaciones, el filtrado, y el chat. Naturalmente, cada uno de estos se
ejecutarán en su propio hilo y el hilo del grupo.

Además de estos servidores de la red básica, tendremos otros dos


pseudoservers, que son para el registro y actualización para el usuario / filtro
de información. Estos componentes de servicio sólo a las solicitudes de otras
partes del sistema, y son realmente muy simplificado de bases de datos.
Queremos que estos servidores para ser independientes de manera que
podamos servir a múltiples solicitudes de red al mismo tiempo, Como se puede
ver en la Figura 19.1, es posible que usted puede tener muchos clientes en
todo el acceso a bases de datos a la vez.

Figura 19.1 El diseño conceptual del servidor multiusuario

Ya que se trata de un servidor de red, algunos números de puerto será


necesario. En aras de la simplicidad, el número de puerto de base se utilizará
para el registro y el servidor de chat de texto, y actualizar compensará por 2 y
filtro compensado por 4. Por defecto el puerto de 3000, esto significa que está
en el chat de texto 3000/UDP, registro en 3000/TCP, actualizar en 3002/UDP, y
filtrar a 3004/UDP

El servidor de código fue escrito y probado en Linux. Sin embargo, debe ser
fácilmente capaz de ejecutar el servidor en cualquier plataforma Java que se
ejecuta.
4

VRML 2.0 con Java CAPÍTULO 19

Redes de Java

Antes de sumergirse en cada uno de los servidores, vale la pena examinar la


creación de redes de estilo de Java. En el capítulo anterior, Bernie cubierto
muchas de las áreas básicas acerca de TCP y UDP y lo que cada uno de los
protocolos será. Ahora voy a llenar la brecha entre-obtener la información entre
el cliente y el servidor.

Casi todos los libros de Java se encuentra siempre se refiere a la creación de


redes. Lamentablemente, la mayoría de las veces los ejemplos son concebidas
para ilustrar un punto en particular. También tienden a ser fuertemente
sesgada hacia el cliente final escrito. ¿Tienes un montón de que en el capítulo
anterior. Ahora vamos a ser el desarrollo de un servidor independiente de Java,
que es la otra mitad de la ecuación que rara vez ve.

Fundamentos de redes de servidor

Un servidor está ahí para servir (naturalmente). Como un viejo mayordomo,


que siempre estará ahí cuando lo necesitas. No debería haber ninguna demora
en responder a las solicitudes, y que nunca debería desaparecer.

Con el fin de cumplir estos requisitos, el servidor debe estar constantemente


buscando nuevas conexiones. Si tenemos una sola aplicación con vistas a
múltiples puertos, deberíamos estar preparados para responder a cualquier
puerto en cualquier momento. Cuando esto se traduce de nuevo a código
escrito, lo que significa que cada servidor debe existir en su propio hilo.

Una vez que tenemos un hilo para cada puerto, ¿cómo sabemos que el hilo
está ahí para recibir información? La respuesta está en la red código de Java.
Cuando le decimos a Java que estamos interesados en obtener alguna
información sobre un determinado puerto, se sienta y espera hasta que algo se
recibe y, a continuación, le permite a su código para continuar. Esto se conoce
como un bloqueo de llamada. El método de los bloques (es decir, no ida y
vuelta) hasta que haya algo para darle. Esto es bueno porque significa que
usted no necesita escribir su propio bucle sin fin que continuamente pruebas
de información de la red. Cada vez que el método devuelve, usted sabe que
tiene información válida.

Una vez que tengamos algo de información, tenemos que ser capaces de
volver a escuchar el reloj lo más rápidamente posible. Decir que tenemos un
gran paquete cuyo contenido tenemos que digerir. Si acabamos de ir y el
proceso que inmediatamente después de su recepción, no se regresan, a la
escucha durante mucho tiempo. Ahora, usted ya debería haber adivinado la
5

VRML 2.0 con Java CAPÍTULO 19

solución: Hilos de nuevo. Después de recibir la información de de la conexión,


lo pasamos a un nuevo hilo que se ocupa de que los paquetes y volver a
escuchar lo más pronto posible.

Escribir un servidor TCP

Como se menciona en el capítulo anterior, TCP es un protocolo orientado a


conexión. Una vez establecida la conexión, puede leer y escribir datos en él
como si se tratara de una normal de puerto I / O (como la consola con
System.out). Esta conexión existe hasta que sea explícitamente roto por uno u
otro fin.

Debido a que el período de sesiones sigue abierto después de la conexión


inicial, el hilo que acepta la conexión no puede seguir manteniendo ese
respecto. Si lo hiciera, el zócalo no sería capaz de escuchar más de nuevas
conexiones. En el zócalo de la terminología, lo que se conoce como un zócalo
de escucha. Lo único que hace es escuchar para una solicitud de crear una
nueva conexión. Una vez que reciba una, se notifica al usuario (usted, el
programador) que una nueva conexión se debe hacer y, a continuación, le
permite enfrentarse a ella.

Para escuchar una nueva conexión

Creación de un socket de escucha es muy fácil: En Java esto se conoce como


un ServerSocket. El único argumento que tiene que dar es el número de puerto
en el que escuchar:

Nota: Java 1.0 no ofrece ninguna de las opciones que podrían utilizarse para el Berkeley
Socket de C / C implementaciones, como la fijación de veces persisten. Algunas de estas
opciones se incluirán en Java 1.1.

intentar
{
ServerSocket listen_socket = new ServerSocket (puerto);
}
catch (IOException e)
{
System.err.println ( "No se puede abrir el Registro socket"
e.getMessage ());
retorno; / / o hacer algo que se detiene el programa, etc
}

Una vez que haya creado un ServerSocket con los número de puerto, tiene un
6

VRML 2.0 con Java CAPÍTULO 19

enchufe que es capaz de escuchar de nuevo las conexiones. (El zócalo es de


hecho ya la escucha, pero es no hacer nada útil a menos que lo conecta a su
software.) ¿Cómo obtener la información de la conexión de la toma para
escuchar el programa?

Trata de una conexión

El zócalo de la escucha no puede hacer nada con una conexión a menos que
usted le pedirá que le avise, que se realiza con el método de aceptar. Este
método trata de dos problemas para usted. No sólo para escuchar una nueva
conexión, pero, una vez que haya uno, sino que también aprobar una nueva
instancia para Socket que usted comience a tratar.

Similar a la anterior código, para escuchar de nuevo es un socket simple. En el


modo de ejecutar su servidor hilo, decirle al zócalo para escuchar de una nueva
conexión:

public void run ()


{
Socket new_connection;
while (true)
{
intentar
{
new_connection = listen_socket.accept ();
}
catch (IOException e)
{
System.err.println ( "Socket aceptar el registro de error"
e.getMessage ());
new_connection = null;
}

if (new_connection! = null)
{
/ / Hacer algo aquí con el recién creado conexión
Thread.yield) ();
}
}

Tenga en cuenta que el método de ejecución tiene un bucle sin fin en el mismo.
Si no, entonces después de responder a la primera conexión que salir, y usted
no sería capaz de responder a cualquier duda, más conexiones, no lo que
7

VRML 2.0 con Java CAPÍTULO 19

quiere hacer. Una vez que el plazo finaliza el método, lo hace el hilo. No hay
manera de que sólo una copia de seguridad de fuego de nuevo.

El método de aceptar sólo una respuesta respecto a la vez. Si desea tener


varias conexiones simultáneas, entonces usted necesita para mantener
llamando aceptar (que es otra razón para tener que sentarse en un bucle sin
fin).

Procesamiento de la información

Ahora que una buena relación se ha hecho, tenemos que hacer algo con la
información que llega desde el cable. Como ya se ha señalado anteriormente
en este capítulo, una clase aparte que se hace se refiere a una solicitud
individual. Esta clase implementa un hilo. Así que una vez que haya creado una
conexión, tendrá que empezar a correr.

El punto blanco que se dejó en el anterior fragmento de código puede ser


llenado con la siguiente pseudocódigo (que se ha tomado de la Secretaría de
clase del servidor):

RegistryMessage msg;
msg = nuevo RegistryMessage (new_connection, servidor, depuración);
msg.start ();
manager.add (msg);

Usted notará que he lanzado en un par de extras aquí. En primer lugar, no se


ha incluido un ThreadGroup para recoger los hilos relacionados con el porque
este es heredado del padre hilo, entonces hay una depuración boolean
bandera, y también tengo un administrador de ahí y (la última línea).

Con todo esto de la carretera, podemos seguir adelante con el procesamiento


de los datos que viene junto. Para ello, tenemos que extraer más información
de la toma de corriente. La más importante es leer y escribir la información de
la alambre.

Un socket TCP es una fuente de información streaming. Eso significa que no


recibe toda la información de una sola vez, sino más bien se trata de forma
progresiva. Espero que son un paso por delante de mí para la siguiente parte:
Tenemos que conseguir un flujo de entrada y salida de la toma de corriente. No
es un problema, ya que la clase Socket dispone que para nosotros con el
getInputStream y getOutputStream métodos. Todos los que tenemos que hacer
es declarar un tipo de cada corriente, y estamos en el negocio.
8

VRML 2.0 con Java CAPÍTULO 19

DataInputStream en = new DataInputStream (_socket.getInputStream ());


PrintStream out = nuevo PrintStream (_socket.getOutputStream ());

Con estos dos, ahora podemos utilizar los métodos de manipulación del flujo
normal como ReadLine para la entrada y la salida de println. (Usted debe estar
familiarizado con estos conceptos de la System.out.println pide utilizados en el
código de depuración!)

Registro de Conexiones

En un entorno multiusuario, usted tiene un montón de gente y, por


tanto, las conexiones, entrando y saliendo. El resultado final de esto es
que continuamente se le acepta nuevas conexiones. Cuando usted abre
una conexión, también debe cerrarla. Pero cada hilo también tiene que
ser mantenido.

Por un momento, simplemente eliminar la última línea de un gerente en


el anterior fragmento de código. ¿Qué crees que sucede a medida que
avanzamos en todo el circuito y aceptar una nueva conexión? El
new_connection variable se le asigna un nuevo valor. Ahora la vieja
relación que hemos tenido a partir de la iteración anterior ya no se hace
referencia en ninguna parte del código. Hace que se convierta en un
objetivo primordial para el recolector de basura? No, la clase
RegistryMessage todavía mantiene una referencia, por lo que mientras
que existe, el zócalo se también. El gestor de la clase, aunque no de su
uso inmediato ahora, es muy útil para su posterior expansión. Por
ejemplo, se puede utilizar para controlar el número máximo de
conexiones o para comprobar los tiempos.

Nota: Como lo explicaré más tarde, el cierre de la conexión de socket se deja como
una responsabilidad de cada clase, no de la gerente.

Un gestor de clase es muy poco. Lo único que hace es añadir una nueva
referencia a su interior el almacenamiento de datos. Cada cierto tiempo,
que se desplaza a través de todos ellos y elimina las conexiones de
muertos. Las operaciones se definen por el hilo dejado de publicarse.
Podemos comprobar esta condición IsAlive llamando al método de cada
objeto. Para este proyecto, el dispositivo de almacenamiento de datos es
sólo un vector. 19,1 es la lista completa a la fuente para el gestor de
registro del servidor.

19.1 La lista de registro de Connection Manager

/ / Servidor simple Multiusuario


9

VRML 2.0 con Java CAPÍTULO 19

/ / (C) Copyright 1996 Justin Sofá justin@vlc.com.au


//
/ / Desde el capítulo 19: Late Night VRML 2.0 y Java
//
/ / Este es el administrador de Registro de clase. Mantiene la lista de
activos
/ / Conexiones. Nuevas conexiones se añaden con el método Add (). Una
vez
/ / Cada diez segundos se realiza un bucle a través de la lista de
conexiones y espera
/ / Para los períodos de sesiones inactivas indicada por el isActive ()
método
/ / Para cada conexión. Muertos se eliminan.

paquete de registro;

importación java.util .*;


registry.RegistryMessage de importación;

clase extiende RegistryManager Hilo


{
Vector connection_list = null;

RegistryManager público ()
{

connection_list = new Vector (5, 2);


}

public void run ()


{

int i;
RegistryMessage msg;

while (true)
{

/ / El vector de bloqueo para que no nos los enfrentamientos


/ / Cuando se trata de añadir una nueva conexión
sincronizadas (connection_list)
{
for (i = 0; i <connection_list.size (); i + +)
{

msg =
10

VRML 2.0 con Java CAPÍTULO 19

(RegistryMessage) connection_list.elementAt (i);


if (! msg.isAlive ())
{
connection_list.removeElementAt (i);
i - / / para ajustar el elemento eliminado
}
}
}
Thread.yield ();
}
}

public void añadir (RegistryMessage msg)


{
sincronizadas (connection_list)
{
connection_list.addElement (msg);
{
}
}

Cada uno de los servidores tiene su propio gerente. Podríamos utilizar


una común para todas las conexiones, pero ya que lo desea, puede
separar la funcionalidad de la pista en algún lugar, vamos a seguir en su
propio grupo.

Cerrando la conexión

Cuando se utiliza TCP, debido a la larga data de la naturaleza de la


conexión, lo que necesita saber cuándo cerrar la conexión. En virtud de
Java 1.0.2, es posible tener el zócalo permanecerá abierta
indefinidamente. Java 1.1 soluciona este problema mediante la inclusión
de algunas de las opciones como el establecimiento de BSD y persisten
las horas de tiempo de tomas de corriente. El resultado de esta falta de
mejoras es que usted debe tener un comando específico en el protocolo
para cerrar la conexión.

¿Por qué cerrar una conexión explícita? ¿Te acuerdas de la discusión


sobre el método de finalizar de nuevo en el capítulo 13? El mismo
principio se aplica aquí. Usted no está garantizado para conseguir
finalizar el método llamado. En aplicaciones de corta esto no es un
problema, porque cuando usted salga del programa, se hará todo la
limpieza. En un entorno de servidor, esta es una historia completamente
diferente.

Cada zócalo abierto es otro recurso utilizado por el sistema. Si ha


11

VRML 2.0 con Java CAPÍTULO 19

seguido el consejo de la mayoría de los libros de texto, que el lugar


cerca de la llamada toma de finalizar en el método. Pero como vimos en
el capítulo 13, el método de finalizar podría no ser llamado durante
mucho tiempo. Como resultado, usted termina con un montón de
muertos hilos, todos con conexiones en directo zócalo adjunto, y cada
uno de mascar un recurso. En los sistemas operativos de Microsoft se
trata de un problema importante, pero no tanto de un problema para los
sistemas basados en Unix. Para asegurarse de que hacer las cosas bien
y sin los recursos tan pronto como sea posible, deberá tener una
estrecha relación de mando en el protocolo y responder de inmediato
por el cierre de la conexión que existe y luego explícitamente.

Escribir un Servidor UDP

En comparación con el servidor TCP, UDP un servidor es mucho más


sencillo. Como un servidor TCP, todavía tenemos que escuchar todo el
tiempo y el tenedor de nuevos hilos como nuevas conexiones llegar. La
diferencia es que UDP, ya que Bernie ya ha señalado, es un protocolo sin
conexión.

Para UDP no tenemos una conexión más, un simple paquete. Cada


paquete es independiente, por lo que una vez que hemos terminado de
procesar un paquete, que clase de ejemplo puede ser desechada.

Para escuchar una nueva conexión

Otra diferencia de TCP es que, en virtud de Java, el UDP no tiene una


clase que sólo se refiere a la escucha poco. En lugar de ello, tenemos
una única clase en el multiuso DatagramSocket clase. Usted obtiene un
comportamiento diferente al cambiar el constructor de llamar.
Especificar un número de puerto indica el zócalo que desea escuchar en
lugar de enviar.

Nuestro código de inicio se ve muy similar a la de TCP código:

intentar
{
listen_socket = new DatagramSocket (puerto);
}
catch (IOException e)
{
System.err.println ( "No se puede abrir el filtro de toma de" +
e.getMessage ());
retorno;
}
12

VRML 2.0 con Java CAPÍTULO 19

La única diferencia es que ahora está utilizando un DatagramSocket en


lugar de un ServerSocket.

Trata de una conexión

Hasta ahora, todo bien. Ahora las cosas empiezan a cambiar un poco. En lugar
de escuchar para una nueva conexión, sólo tenemos que recibir un nuevo
paquete. Sí, usted adivinado, la tiene un DatagramSocket recibir método. El
código nuevo esquema es muy similar a la de TCP código, excepto que esta vez
tenemos que tener un DatagramPacket preconstructed para pasar a recibir la
llamada:

public void run ()


{
byte [] buffer;
DatagramPacket paquete;
FilterMessage a_filter;

/ / Ahora el proceso de entrada.


while (true)
{
= new byte buffer [BUFFER_LENGTH];
paquete = new DatagramPacket (buffer, BUFFER_LENGTH);

intentar
{
listen_socket.receive (paquete);
}
catch (IOException e)
{
System.err.println ( "paquete de error");
}

/ / Ahora hacer algo con el paquete.


}
}

Como puede ver, no hay mucha diferencia entre este y la implementación de


TCP. Un truco que tiene que tener cuidado de no volver a utilizar el mismo para
cada paquete de datagrama recibir. Si usted hace esto, se sustituirá el
contenido del paquete anterior. De nuevo, esto no es una buena cosa a hacer si
somos parte a través de la tramitación del paquete anterior. Para evitar este
13

VRML 2.0 con Java CAPÍTULO 19

problema que acabamos de crear una nueva DatagramPacket cada vez.

Procesamiento de la Información

Trata de un paquete es diferente de una fuente de datos de streaming. Desde


ahora tenemos una pieza de longitud fija de la información, podemos tomar y
progresivamente nuestra forma de trabajo a través de todo el mensaje y, a
continuación, generar la respuesta adecuada. No hay necesidad de bucle para
siempre la lectura de nuevos datos de la entrada.

Al igual que el flujo de TCP, se podría utilizar el método estándar de readline,


pero dado que los paquetes son de longitud fija y muy probablemente sin
terminador nulo o un retorno de carro, usando este método podría congelarse,
colgar, o generar una excepción. Además, si se mira a Bernie, la definición de
lo que el formato de los datos será, descubrirá que sabemos que todo está en
los campos de longitud fija, que es perfecto para usar el bajo nivel como
readByte los métodos y readFloat. Por ejemplo, el siguiente fragmento es de la
clase UpdateMessage:

public void run ()


{
int i;
ByteArrayInputStream buffer =
nuevo ByteArrayInputStream (_packet.getData ());
DataInputStream datos = new DataInputStream (buffer);
intentar
{
/ / Algunos controles rápido en el primer par de octetos. Si
/ / No son correctos entonces el paquete de volcado por salir.
if (data.readUnsignedByte ()! = 0x80) / / valor hexadecimal
{
System.out.println ( "paquetes de actualización de error:" +
"En primer lugar no byte 0x80");
retorno;
}

if (data.readUnsignedByte ()! = 79) / / valor decimal


(
System.out.println ( "paquetes de actualización de error:" +
"Segundo byte no 79");
retorno;
}
/ / Terminar de procesar el resto del paquete.
14

VRML 2.0 con Java CAPÍTULO 19

}
catch (IOException e)
{
System.err.println ( "error en el servidor de actualización" + e.getMessage
());
}
}

La llamada a generar la clase de transformación es idéntico al de la clase de


TCP presentadas anteriormente. La diferencia es que esta clase de trata de un
paquete UDP en vez de toda una corriente. Pero el diseño se mantiene igual, y
todavía tenemos que crear una nueva instancia y, a continuación, empezar a
llamar a su método.

El Registro

Teniendo una mirada a la Figura 19-1, verá que el Registro subsistema opera
por separado de los otros servidores. Este parece un buen lugar para empezar.

El registro es un servidor TCP, lo que significa que tenemos que comenzar con
el sistema de servidor de TCP. Ya has visto la mayoría de los del lado del
servidor parte del código, así que me limitaré a comentar sobre las distintas
fases de transformación.

El Registro del servidor

Como puedes ver en la figura 19.1, hay dos partes para el sistema de registro.
Uno de ellos es la interfaz de red, que se ocupa de la conectividad de TCP y el
análisis de la información, y el otro es el servidor de registro. El servidor
mantiene la información acerca de todas las entidades de control y el acceso a
los mismos por los particulares.

En un entorno distribuido, este servidor podría estar situada en un equipo


diferente, con un número de registro en red todos los servidores de
comunicación con él. Sin embargo, a efectos de nuestro diseño, se le mantenga
en el mismo paquete que el resto del código de registro.

El registro de servidor tiene una función: para controlar el acceso a las distintas
entidades. Todas las solicitudes de cambio de información requieren una
entidad ID y el nombre del usuario. Si el nombre de usuario determinado no
representa a los propietarios de la entidad, entonces el usuario no está
autorizado a cambiar las propiedades, y una condición de error se devuelve a
la que llama.
15

VRML 2.0 con Java CAPÍTULO 19

En el CD-ROM: El código de todas estas clases se puede encontrar en el CD-


ROM. Mientras que el código funciona allí, hay algunos bugs conocidos (y
probablemente muchos desconocidos también!). Usted puede encontrar la
versión más reciente del código de http://www.vlc.com.au/mu/server/.

La Entidad de clase

Ser estricto sobre el acceso a una entidad particular, las propiedades se


aplica no sólo a un nivel del servidor, sino también a nivel de la entidad.
La clase de entidad, que define una entidad, se define en el Listado de
19.2.

19.2 La lista de entidades de clase

/ / Servidor simple Multiusuario


/ / (C) Copyright 1996 Justin Sofá justin@vlc.com.au
/ /
/ / Desde el capítulo 19: Late Night VRML 2.0 y Java
/ /
/ / Esta es la clase de entidad. Cada instancia de esta clase
/ / Representa una entidad en el mundo.

paquete de registro;

clase de entidad
{
público int ID;
Cadena apodo privado;
Cadena URL privado;
private int flags = 0;
privado String [] public_info;
privado a largo registration_time = 0;
privado a largo last_update_time = 0;
boolean privado persistente;
boolean is_avatar privado;
Cadena propietario privado;
Cadena CNAME privado;

/ / Crear una nueva entidad. Para el inicio de la copia CNAME


/ / Como el apodo.
Entidad pública (String cn, int new_id, Cadena ident_name)
{
16

VRML 2.0 con Java CAPÍTULO 19

CNAME = cn;
propietario = ident_name;
= alias CNAME;
ID = new_d;

registration_time = System.currentTimeMillis ();


last_update_time = registration_time;
}

pública a largo get_registrationTime ()


{
return (registration_time);
}

pública a largo get_lastUpdateTime ()


{
return (last_update_time);
}

boolean isPersistent público ()


}
return (persistente);
}

boolean isAvatar público ()


{
return (is_avatar);
}

public void set_owner (String new_name)


{
propietario = new_name;
last_update_time = System.currentTimeMillis ();
}

public String get_owner ()


{
return (propietario);
}

public void set_persistent (boolean val)


{
val = persistentes;
last_update_time = System.currentTimeMillis ();
}
17

VRML 2.0 con Java CAPÍTULO 19

public void set_avatar (boolean val)


{
is_avatar = val;
last_update_time = System.currentTimeMillis ();
}

public String get_cname ()


{
return (CNAME);
}

public void set_nickname (String nombre)


{
alias = nombre;
last_update_time = System.currentTimeMillis ();
}

public String get_nickname ()


{
return (alias);
}

public void set_URL (String new_url)


{
URL = new_url;
last_update_time = System.currentTimeMillis ();
}

public String get_URL ()


{
regreso (URL);
}

public void set_flags (int new_flags)


{
flags = new_flags;
last_update_time = System.currentTimeMillis ();
}

int get_flags público ()


{
return (banderas);
}

public void set_publicInfo (String [] new_info)


{
18

VRML 2.0 con Java CAPÍTULO 19

public_info = new_info;
last_update_time = System.currentTimeMillis ();
}

public String [] get_publicInfo ()


{
return (public_info);
}
}

Desde el código, se puede ver que no hay público para las variables,
excepto la ID. La razón principal de esto es que tengo que seguir la pista
de la última vez que cualquiera de las entidades de las esferas se han
actualizado. Aunque he escrito a la entidad así como el manejo del
servidor, quiero garantizar que la actualización se update_time. Si yo
había dejado las variables públicas, podrían ser fácilmente cambiado sin
informar a la entidad de la nueva actualización del tiempo.

Ser un alma de confianza, me han dejado la entidad de identificación


como público, principalmente porque estamos haciendo un montón de
comparaciones como la búsqueda a través de la lista de documentos de
identidad para el tipo apropiado, y los gastos generales de un método de
comparación para cada llamada (incluso con agresivos inlining
compilador) añade los gastos generales adicionales sólo para la
comparación.

Nota: En un gran número de los métodos se han quedado fuera del código en el
Listado de 19.3. Mucho de la clase consta de varios métodos para ajustar los valores, y
que contiene un número de implementaciones método casi idéntica a la de
set_nickname. Ver el código fuente en el CD de la aplicación completa.

La clase RegistryServer

Como se explicó anteriormente, la RegistryServer y controla el acceso a la


información sobre las entidades de la escena. Para mantenerse al tanto de
estos temas, tenemos dos datos: la lista de identificadores de usuario válido
con sus correspondientes contraseñas, y la lista de entidades.

Aunque sólo hay dos listas que se le mantenga, tengo tres estructuras de datos
(véase el Listado de 19,3). Tablas hash se utilizan para búsquedas rápidas de la
cadena de información basado en el nombre de usuario / contraseña
(user_identities) y el nombre canónico de las entidades (CNAME). Un hash
cadena de búsqueda es mucho más rápido que realizar una completa
búsqueda lineal en la lista y hacer comparaciones de cadenas en cada nombre.
19

VRML 2.0 con Java CAPÍTULO 19

Un vector se utiliza para mantener la lista de entidades.

19.3 La lista RegistryServer Clase

/ / Servidor simple Multiusuario


/ / (C) Copyright 1996 Justin Sofá justin@vlc.com.au
/ /
/ / Desde el capítulo 19: Late Night VRML 2.0 y Java
/ /
/ / Este es el Registro de la clase. Detalles acerca de cada avatar
/ / Se guardan aquí. ID de inicio avatar entidad a partir del 1 de

paquete de registro;

importación java.io. *;
importación java.util .*;

clase pública se extiende RegistryServer Hilo


{
private static int next_entity_id = 0;

Vectores entity_list privado;


Hashtable name_list privado;
Hashtable user_identities privado;

RegistryServer público (ThreadGroup TG)


{
super (TG, "Registro de servidor Hilo");
setPriority (NORM_PRIORITY + 2);

System.out.println ( "Primer Registro del servidor");

entity_list = new Vector (10,2);


name_list = new Hashtable (100, 0.8f);
user_identities = new Hashtable (20, 0.8f);
}

/ / Asignar una nueva entidad.


/ / Valores de retorno:
/ / -1 Si CNAME ya en uso
/ / -2 Si el usuario no identificado
/ /> 0 éxito, el nuevo número de identificación
/ / La persona que llama debe asegurarse de que la identidad está
legalmente
20

VRML 2.0 con Java CAPÍTULO 19

/ / Válida para añadir una nueva entidad.


público sincronizado int asignar (String CNAME, Cadena ident)
{
Entidad jurídica;

/ / Comprobar primero la lista de nombres para la entidad;


if (name_list.get (CNAME)! = null)
retorno (-1);

next_entity_id + +;

entidad = new entidad (CNAME, next_entity_id, ident);

/ / Necesidad de añadir una entidad a la clase de vectores


entity_list.addElement (entidad);
name_list.put (CNAME, entidad);

return (next_entity_id);
}

boolean set_URL público sincronizado (int entid,


Ident cadena,
Cadena url)
{
int i;
Entidad e;

for (i = 0; i <entity_list.size (); i + +)


{
e = (Entidad) entity_list.elementAt (i);

if (e.ID == entid)
{
if (! e.get_owner (). iguala (ident))
return false;
e.set_URL (url);
break;
}
}
return true;
}

boolean set_nickname público sincronizado (int entid,


Ident cadena,
21

VRML 2.0 con Java CAPÍTULO 19

Cadena new_name)

{
int i;
Entidad e;

for (i = 0; i <entity_list.size (); i + +)


{
e = (Entidad) entity_list.elementAt (i);

if (e.ID == entid)
{
if (! e.get_owner (). iguala (ident))
return false;

e.set_nickname (new_name);
break;
}
}

return true;
}

público sincronizado boolean chown (int entid,


Old_owner cadena,
Cadena new_owner)

{
int i;
Entidad e;

for (i = 0; i <entity_list.size (); i + +)


{
e = (Entidad) entity_list.elementAt (i);

if (e.ID == entid)
{
if (! e.get_owner (). es igual a (old_owner))
return false;

e.set_owner (new_owner);
break;
}
}
22

VRML 2.0 con Java CAPÍTULO 19

return true;
}

público sincronizado vacío printInfo (int entid, PrintStream out)


{
int i, j;
Entidad e;
String [] info;

for (i = 0; i <entity_list.size (); i + +)


{
e = (Entidad) entity_list.elementAt (i);

if (e.ID == entid)
{
info = e.get_publicInfo ();

si (info == null)
break;

para (j = 0; j <info.length, j + +)
out.println (info [j]);

break;
}
}
}
público sincronizado vacío printEntityInfo (int entid,
PrintStream a cabo,
Cadena nick)

{
int i;
Entidad e;

for (i = 0; i <entity_list.size (); i + +)


{
e = (Entidad) entity_list.elementAt (i);

/ / La actualidad, no de control sobre la propiedad de la entidad.


if (e.ID == entid)
{

out.print (e.get_cname () + "" + e.get_URL () + "");


23

VRML 2.0 con Java CAPÍTULO 19

out.print (e.get_owner () + "" + e.isAvatar () + "");


out.print (e.isPersistent () + "");
out.println (e.get_registrationTime ());
out.println (e.get_nickname ());
/ / Esto es incorrecto porque debe ser una lista
/ / De los valores SSRC. no sólo una única bandera.
out.println (e.get_flags ());
break;
}
}
}

público sincronizado vacío printList (PrintStream out)


{
int i;

for (i = 0; i <entity_list.size (); i + +)


out.println (((Entidad) entity_list.elementAt (i)). ID);
}
boolean verifyIdent público (String uname, Cadena passwd)
{
Curr_passwd cadena = (String) user_identities.get (uname);

if (curr_passwd == null)
{
user_identities.put (uname, passwd);
return true;
}

return (curr_passwd.equals (passwd));


}

público sincronizado vacío listNew (int tiempo, PrintStream out)


{
int i;
Entidad e;
largo earliest_time = System.currentTimeMillis () - tiempo;

for (i = 0; i <entity_list.size (); i + +)


{

e = (Entidad) entity_list.elementAt (i);

if (e.get_lastUpdateTime ()> earliest_time)


24

VRML 2.0 con Java CAPÍTULO 19

out.println (e.ID);
}
}
}

La principal característica de casi todos los métodos de este código es el


control de la entidad de identificación y el titular de esa entidad. Si el
nombre del llamante, que figura en la cadena ident, no coincide con el
nombre del propietario, entonces rechazar el cambio y devuelva la
condición de error a la persona que llama.

En una serie de casos en la clase, usted verá que los métodos


PrintStream tomar como parámetro. Nunca pasar una entidad a la que
llama, así que a veces la única manera de pasar la información hacia
abajo de la conexión es pasar PrintStream en el registro correspondiente
a la conexión de zócalo e imprimir la información directamente.

También debe notar que cada método se sincroniza, lo que nos impide
conseguir confundirse con múltiples solicitudes de cada método y tener
que tratar con los resultados. No sólo eso, sino que en muchos casos la
estructura de datos individuales está bloqueado con sincronizados. Y lo
que es peor aún, las estructuras de datos como Vector y Hashtable
también hilo de seguridad se realizan al declarar como sus métodos de
acceso sincronizado, también.

Usando todos estos bloqueos sincronizar es el enfoque más seguro. Pero


también nos cuesta muy caro en el rendimiento debido a la sobrecarga
asociada con el monitor del sistema utilizado en Java. Para cada
convocatoria sincronizada, la JVM debe comprobar el estado del monitor
y actuar en consecuencia. En situaciones como apretado bucle los
registros, la fijación y unsetting de estos monitores son lo que nos costó
más en el rendimiento. Un régimen alternativo es examinado más
adelante en el capítulo.

Como para el resto del código, debe ser razonablemente explica por sí
mismo el método de nombres. El nombre del método refleja el registro
de comando especial que Bernie ya ha esbozado en el capítulo anterior.

Manejo de las peticiones

Una vez que haya creado la parte de atrás (de registro de servidor) y
frontal (servidor TCP) las partes del sistema de registro, usted deberá
llenar los espacios en blanco. Esto toma la forma del código en el
analizador RegistryMessage clase (presentado en el Listado 19,4).
25

VRML 2.0 con Java CAPÍTULO 19

19.4 El RegistryMessage lista de clase

/ / Servidor simple Multiusuario


/ / (C) Copyright Justin justin@vlc.com.au Sofá
/ /
/ / Desde el capítulo 19: Late Night VRML 2.0 y Java
/ /
/ / Este es el registro de mensajes de clase. El verdadero trabajo
/ / De analizar el mensaje de una persona se hace aquí y el envío de
/ / Mensajes a los otros usuarios sobre la base de cualquier filtración
/ / Parámetros son necesarios.

paquete de registro;
importación java.net .*;
importación java.io. *;
importación java.util .*;
registry.RegistryServer de importación;

clase extiende RegistryMessage Hilo


{
private static int msg_number = 0;
privado boolean debug = false;

int sequence_num privado;


int text_id privado;
int Msg_type privado;
int msg_length privado;
Cadena de mensaje privado;
privado RegistryServer _SERVER;
Socket _socket privado;
Cadena apodo privado;
Cadena privada de identidad;

DataInputStream en privado;
PrintStream privado a cabo;

Vectores entity_list privado;

/ / Todo lo que aquí se hace es copiar el paquete


/ / En el interior de referencia. Dejamos todo el trabajo real de
/ / El método de ejecución de Java, ya que permite la manipulación del
hilo
/ / Sistema para compartir la carga de trabajo mucho mejor Si no
entonces
/ / Servidor de chat de todo el hilo hasta el bloque que hemos
/ / Trata de la respuesta aquí.
26

VRML 2.0 con Java CAPÍTULO 19

público RegistryMessage (Socket s,


ThreadGroup TG,
RegistryServer servidor,
boolean depurar)
{
super (TG, "Registro de conexión" msg_number + + +);
setPriority (NORM PRIORIDAD);

this.debug = debug;
_socket = s;
_SERVER = server;
System.out.print ( "Nueva conexión");
System.out.println (_socket.getInetAddress (). GetHostName ());
entity_list = new Vector (10, 5);

alias = "Invitado";
identidad = "Invitado";
}

/ / Analizar el paquete y enviar el texto a los demás usuarios.


public void run ()
{
Cadena de entrada = null;

intentar
{
en = new DataInputStream (_socket.getInputStream ());
a = new PrintStream (_socket.getOutputStream ());
}
catch (IOException e)
{
System.err.println ( "Servidor de Registro:" +
"Error al obtener la E / S arroyos" +
e.getMessage ());
}

while (true)
{
intentar
{
input = in.readLine ();
if ((entrada! = null) & & (! processCommand (entrada)))
break;
}
catch (IOException e)
{
27

VRML 2.0 con Java CAPÍTULO 19

System.err.println ( "Registro de mensajes de error" +


e.getMessage ());
}
Thread.yield ();
}

intentar
{
in.close ();
out.close ();
_socket.close ();
}
catch (IOException e)
{
System.err.println ( "Registro de mensajes de error" +
e.getMessage ());
}
System.out.print ( "cierre del Registro de conexión");
System.out.println (_socket.getInetAddress (). GetHostName ());
}
boolean processCommand privado (Cadena de mando)

lanza IOException
{
StringTokenizer strtok;
Cadena simbólica;
int entid;
int i;
set_ok boolean;

if (debug)
System.out.println ( "comando recibido:" + comando);

strtok = new StringTokenizer (comando);

intentar
{
token = strtok.nextToken ();
}
de capturas (NoSuchElementException e1)
{
out.println ( "REHUSÓ carácter no válido en el comando");
return true;
}

if (token.equals ( "hola"))
28

VRML 2.0 con Java CAPÍTULO 19

out.println ( "BIENVENIDO");
else if (token.equals ( "LISTA"))
{
_server.printList (out);
out.println (".");
}
else if (token.equals ( "getinfo"))
{
intentar
{
entid = Integer.parseInt (strtok.nextToken ());
}
de capturas (NoSuchElementException e1)
}
out.println ( "comando REHUSÓ incompletos");
return true;
}
de capturas (NumberFormatException e2)
}
out.println ( "ID REHUSÓ entidad formato incorrecto");
return true;
}

_server.printInfo (entid, a);


out.println (".");
}
else if (token.equals ( "GETENTITY"))
{
intentar
{
entid = Integer.parseInt (strtok.nextToken ());
}
de capturas (NoSuchElementException e1)
{
out.println ( "REHUSÓ dado ninguna entidad ID");
return true;
}
de capturas (NumberFormatException e2)
{
out.println ( "ID REHUSÓ entidad formato incorrecto");
return true;
}
_server.printEntityInfo (entid, fuera, alias);
out.println (".");
}
else if (token.equals ( "INFO"))
29

VRML 2.0 con Java CAPÍTULO 19

{
intentar
{
entid = Integer.parseInt (strtok.nextToken ());
}
de capturas (NoSuchElementException e1)
{
out.println ( "REHUSÓ dado ninguna entidad ID");
return true;
}
de capturas (NumberFormatException e2)
{
out.println ( "ID REHUSÓ entidad formato incorrecto");
return true;
}
/ / Verificar la propiedad del objeto
if (! _server.isOwner (entid, identidad))
{
out.println ( "REHUSÓ no propietario");
return true;
}
/ / Bien, por lo que es propio, le permite continuar.
out.println ( "OK");
Info_list vector = new Vector (5,2);
Tmp cadena;
/ / Leer primero en todos los datos en un buffer.
while (true)
{
tmp = in.readLine ();
if (tmp.equals ("."))
break;
info_list.addElement (tmp);
}
/ / Ahora lo copia a una cadena matriz.
String [] str_list = new String [info_list.size ()];
for (i = 0; i <info_list.size (); i + +)
str_list [i] = (String) info_list.elementAt (i);
_server.set_info (entid, la identidad, la str_list);
}
else if (token.equals ( "Adiós"))
{
/ / En primer lugar, la limpieza de todas las entidades que
pertenecen a este
/ / Cliente. Bucle sólo el servidor y dar instrucciones para borrar
/ / Cada uno de ellos.
for (i = 0; i <entity_list.size (); i + +)
30

VRML 2.0 con Java CAPÍTULO 19

_server.delete (
(int) ((Integer) entity_list.elementAt (i)). intValue (),
identidad);
entity_list = null;
return false;
}
else if (token.equals ( "ALLOC"))
{
String name;
intentar
{
name = strtok.nextToken ();
}
de capturas (NoSuchElementException e1)
{
out.println ( "CNAME REHUSÓ entidad no se");
return true;
}
if (identity.equals ( "usuario"))
{
out.println ( "No REHUSÓ ident set");
return true;
}
/ / Ahora que hemos pasado, podemos lograr algo asignar
entid = _server.allocate (nombre, identidad);
switch (entid)
{
caso -1:
out.println ( "Entidad REHUSÓ CNAME en uso");
break;
caso -2:
out.println ( "REHUSÓ usuario no identificados");
break;
por defecto:
out.println (entid);
entity_list.addElement (nuevo Integer (entid));
}
}
else if (token.equals ( "URL"))
{
Url cadena;
intentar
{
entid = Integer.parseInt (strtok.nextToken ());
url = strtok.nextToken ();
}
31

VRML 2.0 con Java CAPÍTULO 19

de capturas (NoSuchElementException e1)


{
out.println ( "comando REHUSÓ incompletos");
return true;
}
de capturas (NumberFormatException e2)
{
out.println ( "ID REHUSÓ entidad formato incorrecto");
return true;
}
if (_server.set_URL (entid, la identidad, url))
out.println ( "OK");
algo más
out.println ( "REHUSÓ razón no disponible");
}
else if (token.equals ( "apodo"))
{
intentar
{
entid = Integer.parseInt (strtok.nextToken ());
alias = strtok.nextToken ();
}
de capturas (NoSuchElementException e1)
{
out.println ( "comando REHUSÓ incompletos");
return true;
}
de capturas (NumberFormatException e2)
{
out.println ( "REHUSÓ formato de número incorrecto");
return true;
}
if (_server.set_nickname (entid, la identidad, apodo))
out.println ( "OK");
algo más
out.println ( "REHUSÓ razón no disponible");
}
else if (token.equals ( "Release"))
{
intentar
{
entid = Integer.parseInt (strtok.nextToken ());
}
de capturas (NoSuchElementException e1)
{
out.println ( "REHUSÓ dado ninguna entidad ID");
32

VRML 2.0 con Java CAPÍTULO 19

return true;
}
de capturas (NumberFormatException e2)
{
out.println ( "ID REHUSÓ entidad formato incorrecto");
return true;
}
if (_server.delete (entid, identidad))
out.println ( "OK");
algo más
out.println ( "REHUSÓ razón no disponible");
}
else if (token.equals ( "persistentes"))
{
Cadena de persistir;

intentar
{
entid = Integer.parseInt (strtok.nextToken ());
persisten strtok.nextToken = ();
}
de capturas (NoSuchElementException e1)
{
out.println ( "comando REHUSÓ incompletos");
return true;
}
de capturas (NumberFormatException e2)
{

out.println ( "ID REHUSÓ entidad formato incorrecto");


return true;
}

if (persist.equals ( "true"))
set_ok = _server.set_persistent (entid,
identidad,
true);
else if (persist.equals ( "false"))
set_ok = _server.set_persistent (entid,
identidad,
false);
else / / Bares
{
out.println ( "REHUSÓ formato incorrecto");
return true;
}
33

VRML 2.0 con Java CAPÍTULO 19

if (set_ok)
out.println ( "OK");
algo más
out.println ( "REHUSÓ razón no disponible");
}
else if (token.equals ( "Avatar"))
{
Av cadena;
intentar
{
entid = Integer.parseInt (strtok.nextToken ());
av = strtok.nextToken ();
}
de capturas (NoSuchElementException e1)
{
out.println ( "comando REHUSÓ incompletos");
return true;
}
de capturas (NumberFormatException e2)
{
out.println ( "ID REHUSÓ entidad formato incorrecto");
return true;
}

if (av.equals ( "true"))
set_ok = _server.set_avatar (entid, la identidad, la verdad);
else if (av.equals ( "false"))
set_ok = _server.set_avatar (entid, la identidad, false);
else / / Bares
{
out.println ( "REHUSÓ formato incorrecto");
return true;
}
if (set_ok)
out.println ( "OK");
algo más
out.println ( "REHUSÓ razón no disponible");
}
else if (token.equals ( "IDENT"))
{
Cadena de pasar;
Cadena = tmp_id identidad;

intentar
{
34

VRML 2.0 con Java CAPÍTULO 19

tmp_id = strtok.nextToken ();


pass = strtok.nextToken ();
}
de capturas (NoSuchElementException e1)
{
out.println ( "comando REHUSÓ incompletos");
/ / Sólo en el caso de que algo follar hasta que no
/ / Quiere perder su actual IDENT
return true;
}
/ / Comprobar ahora con el servidor
if (_server.verifyIdent (tmp_id, pase))
{
= tmp_id identidad;
out.println ( "OK");
}
algo más
out.println ( "REHUSÓ nombre de usuario o contraseña
incorrectos");
}
else if (token.equals ( "Chown"))
{
New_name cadena;
intentar
{
entid = Integer.parseInt (strtok.nextToken ());
new_name = strtok.nextToken ();
}
de capturas (NoSuchElementException e1)
{
out.println ( "comando REHUSÓ incompletos");
return true;
}
de capturas (NumberFormatException e2)
{
out.println ( "ID REHUSÓ entidad formato incorrecto");
return true;
}
if (_server.chown (entid, la identidad, la new_name))
out.println ( "OK");
algo más
out.println ( "REHUSÓ no propietario");
}
else if (token.equals ( "LISTNEW"))
{
int tiempo;
35

VRML 2.0 con Java CAPÍTULO 19

intentar
{
tiempo = Integer.parseInt (strtok.nextToken ());
}
de capturas (NoSuchElementException e1)
{
out.println ( "comando REHUSÓ requiere un" +
"tiempo que se especifica");
return true;
}
de capturas (NumberFormatException e2)
{
out.println ( "REHUSÓ incorrecta formato de hora");
return true;
}
_server.listNew (tiempo, a);
out.println (".");
}
else / / la captura todas las condiciones. Si envía un comando no
válido.
out.println ( "OK");

return true;
}
}

RegistryMessage trata de un individuo respecto de su registro de toda la


vida. Como tal, está siempre a la búsqueda y en la conexión a la espera
de leer la siguiente pieza de información. Las medidas que se pueden
realizar sobre la base de Bernie's esquema del capítulo anterior.

La mayoría de los analizador de código se ocupa de tomar la línea de


entrada, el paso de la primera palabra para descubrir el comando y, a
continuación, la ejecución de la llamada al servidor de registro. Muy
poco tratamiento es realmente hacer en esta clase. La mayoría de los
que es manejo de errores de código.

¡Uf, qué un montón de código! Sólo vistazo más rápidamente se debe


notar que al menos la mitad de las líneas están dedicadas al manejo de
errores.

Procesamiento de un comando

El método de ejecutar sólo mira y espera para el próximo comando a


36

VRML 2.0 con Java CAPÍTULO 19

entrar a través de la conexión. Una simple comprobación de una línea se


hace nula y, a continuación, la línea de entrada se pasa a otro método
(processCommand) para realizar la verdadera transformación.

ProcessCommand implementa un muy simple, nonoptimized analizador.


Simplemente contiene un gran si-otra escalera de cuerda para la
compara cada comando. En primer lugar, se estropea la primera palabra
de la entrada de mando. Basado en esto, entonces se ejecuta el código
para el comando adecuado.

La mayoría de las veces, el código de transformación implica la


obtención de una entidad de identificación y algunos otros datos.
Comprobamos en cada etapa para el tipo de información, como números
o cadenas de texto.

Una vez que la información correcta, se ha procesado, por lo general que


la información pasa directamente a la llamada del servidor de registro.
Comprueba algunos comandos para ver si este período de sesiones es el
titular de esa entidad, pero la mayor parte del tiempo se espera para el
retorno de la condición de método de las llamadas y, a continuación,
generar la respuesta adecuada al cliente.
Cerrando la conexión

La forma de cerrar la conexión es en respuesta al cliente GOODBYE


comando. Con este comando, processCommand regresa con un valor de
falso. Comprobar la condición de la salida en la carrera de este método
detecta y se rompe de la bucle infinito. Una vez salido de la vuelta, el
socket está cerrado y el método de las salidas.
Haciendo el servicio de balas

A diferencia de la efímera paquete UDP, una conexión TCP se mantiene


abierta durante largos períodos de tiempo. Si el cliente nos envía
comandos falsos, tenemos que ser capaces de recuperarse y seguir
funcionando con normalidad. Para hacer esto con éxito, usted necesita
para la captura de todas y cada una excepción que es probable que se
generen. El único que no se pesca aquí es una NullPointerException.

En mi experiencia, la mejor forma de poner a prueba la solidez de un


servidor TCP es de telnet al puerto del servidor se está ejecutando y
después empieza a escribir en los comandos. Sólo si los escribe en,
usted es casi garantizado para comenzar a errores, sintaxis incorrecta, y
todo tipo de entradas erróneas sin siquiera intentarlo!

Tan pronto como se descubra un error, REHUSÓ una respuesta con un


comentario se genera, y el método processCommand sale con un
verdadero estatuto. Salimos con un verdadero estado, porque queremos
37

VRML 2.0 con Java CAPÍTULO 19

que el servidor siga funcionando. Si pasamos de una falsa, entonces el


servidor de terminar este respecto, el cliente deja en el aire sin un
servidor.

Resumen de registro de servidor

Ha visto casi la mitad del sistema. Clases de manejo de la red son


básicamente las acciones que el código se indica al principio de este
capítulo, con algunas piezas de código de la medida para la aplicación.
Usted puede encontrar el código completo para el registro del servidor
en el CD-ROM que acompaña a este libro.

El servidor también fue un gran fragmento de código, sobre todo porque


de todos los controles de seguridad que se deben hacer.
Registro de Usuarios: El usuario del servidor

Al igual que el servidor de registro en la sección anterior, los datos del


usuario también tiene un servidor común. Ámbitos como la actualización
que para enviar mensajes de chat y están cubiertos en este servidor.
Una vez más, al igual que el servidor de registro, el usuario del servidor
podría existir en otro ordenador de las interfaces de red para el chat, el
filtro, y actualizar servidores.

Utilizando la misma estrategia de diseño como el registro del servidor,


creamos este servidor como un hilo. Todas las peticiones al servidor son
independientes y se basan en una exigencia funcional, como para enviar
una actualización o un conjunto de filtrado.

El Usuario de Clase

Diseño de la clase de usuario es totalmente lo contrario de la clase de


entidad. El primer criterio es que no hay necesidad de la seguridad de
los datos internos. El segundo criterio es la velocidad. A diferencia del
registro de servidor, que podría funcionar de forma relativamente lenta,
el usuario del servidor será necesario enviar las actualizaciones con la
frecuencia que se entregan. En un concurrido mundo, esto puede poner
muy altas exigencias en el servidor.

Teniendo en cuenta las consideraciones anteriores, el diseño es


esencialmente el equivalente de un C / C + + struct. Todo es público, y
sólo existe un constructor. La fuente completo figura en el Listado de
19.5.

19.5 La lista de usuario de clase

/ / Servidor simple Multiusuario


38

VRML 2.0 con Java CAPÍTULO 19

/ / (C) Copyright 1996 Justin Sofá justin@vlc.com.au


/ /
/ / Desde el capítulo 19: Late Night VRML 2.0 y Java
/ /
/ / Esta es la clase que tiene información sobre un usuario particular.

paquete de filtro;

importación java.net .*;

público de clase de usuario


{
int entid público;
pública a largo last_update;
InetAddress dirección pública;
int last_seq_num público;
int update_port público;
flotador público agudeza;
público int horizonte;
int current_region público;
público int [] regiones;
público int [] puertos;
público flotar la posición [];
público flotar [] rotación = (0, 0, 1, 0);

público usuario (actualización int, float ac, horz int, int [] reg, int [] prt)

= update_port actualización;
agudeza = ac;
horz = horizonte;
regiones = reg;
puertos = prt;
last_update = System.currentTimeMillis ();
posición = null;
}
}

Una diferencia que usted puede ser que ya han notado es que el tiempo
de actualización se establece por separado. Cuando un filtro nuevo
comando es recibido, tendremos que configurar muchas de las
propiedades a la vez. Es más fácil y más rápido para configurar todos a
la vez en el servidor, en lugar de gastar el tiempo en la función llamada.
Por desgracia, que también requiere que el servidor se encargará de la
actualización de la last_update campo.
39

VRML 2.0 con Java CAPÍTULO 19

La clase UserServer

UDP a los diferentes servidores, el UserServer clase se encarga de toda


la funcionalidad de distribución de información actualizada a los clientes.

UDP TCP es diferente de las comunicaciones. Puesto que no necesita


tener una relación ya establecida para enviar actualizaciones, lo que
podemos hacer todo en el servidor. Todos los demás servidores de red
que tiene que hacer es pasar una petición al servidor para enviar alguna
pieza de información, y el servidor se encargará del resto.

UDP e Hilos

El servidor del usuario se construye a lo largo de diferentes líneas del


servidor TCP. Mucha de esta construcción tiene que ver con los hilos.
Cuando usted hace una clase que es un hilo, el único método que
realmente se ejecuta como un hilo se ejecute. Es decir, llamando a
cualquier otro método de clase que significa que el método no regresar
hasta que haya terminado su tratamiento. Si tenemos una serie de
clases diferentes que son todos los que solicita los servicios de una sola
instancia de una clase, ¿cómo hacer esta clase de rosca ayuda? Cada
uno de estos métodos no volverá hasta que haya terminado, pero
queremos hacer muchas peticiones simultáneas.

Para un método individual, no hay mucho que puede hacer. Con la


creación de redes de código, no se puede hacer de nuevo operador. Las
cosas se ponen muy rápidamente desordenado. La única solución es
hacer que todo el método de sincronización. Incluso con este enfoque,
usted puede llamar a cada uno de los distintos métodos de forma
simultánea, sino que puede hacerlo incluso con un non-threaded clase.

Gestión de Recursos

Piense en el diseño de la UserServer. Posee información sobre todos los


usuarios que el sistema conoce en todo momento. ¿Qué sucede cuando
el usuario abandona la conexión a Internet? Ya no son el envío de
actualizaciones, y actualizaciones de ellos no llegan. Si sólo mantiene el
envío de información basada en la esperanza de que podría ser un
problema temporal, que pronto la sobrecarga de los recursos disponibles
en el servidor. La poda es necesaria. ¿Pero cómo decidir cuándo podar y
cuándo no?

Volviendo a la especificación, podemos ver que si el usuario no ha


40

VRML 2.0 con Java CAPÍTULO 19

enviado un filtro de mensajes en los últimos 10 segundos, debe ser


removido de la lista. Esto nos da los criterios para la gestión. Cada 10
segundos hay que barrer a través de la lista de los usuarios y eliminar
las que no se han actualizado (por parte del cliente el envío de un filtro
de mensaje).

Aquí es donde es útil para hacer la clase de un hilo. El plazo sea el


método utilizado para hacer el barrido. Una vez cada 10 segundos se
despierta, va a través de la lista, y las ciruelas pasas a cabo muerto
usuarios. Entonces se pone a dormir de nuevo.

Una cosa que tiene que tener cuidado es la actualización de veces. No


queremos accidentalmente al quitar una actualización porque sólo
tenemos el momento equivocado. La forma más sencilla de hacerlo es el
tiempo al comienzo de la barra y luego hacer la comparación. Esto
también ayuda a sus resultados, debido a que sólo hacer una llamada al
sistema para obtener el tiempo, no (potencialmente) a cientos.

Además de congelar este momento, también tenemos que velar por el


momento real de la barra. Si acaba de hacer el cheque y luego durmió
durante 10 segundos, todo poco a poco salir de sincronización. El tiempo
para recorrer la lista de usuarios es mayor que cero y, a continuación,
añadir 10 segundos para ello. El resultado es que cada cheque es de
más de 10 segundos (tiempo de proceso + sueño de 10 segundos):

exec_time = System.currentTimeMillis () - start_time;


if (exec_time <MAX_REFRESH_PERIOD)
{
intentar
{
dormir (MAX_REFRESH_PERIOD - exec_time);
}
de capturas (InterruptedException e)
(/ / No hacer nada
}
}

Para asegurarse de que los controles son de 10 segundos, sólo restar el


tiempo de ejecución para el barrido de 10 segundos, y el sueño de esa
cantidad. En el ejemplo anterior, tenemos una variable en la clase para
indicar el tiempo entre actualizaciones.

Envío de actualizaciones

Envío de una actualización es típico de la aplicación de la funcionalidad


del servidor. El método de llamada contiene todos los parámetros que
41

VRML 2.0 con Java CAPÍTULO 19

deben ser enviados en la nueva actualización.

Envío de una actualización requiere dos cosas: la actualización de la


representación de ese usuario, y enviar esa información a todos los
clientes. Actualización de la información personal del usuario es muy
fácil. En primer lugar, sólo recuperar la referencia del usuario y, a
continuación, actualizar los campos:

/ / En primer lugar, vamos a actualizar el usuario


user = (Usuario) address_list.get (host);

/ / Y comprobar que se trata de un número de secuencia más tarde si no


desechar.
if (seq_num <user.last_seq_num)
retorno;

user.last_seq_num = seq_num;
user.entid = src_entid;
user.last_update = System.currentTimeMillis ();
= user.current_region región;
user.acuity = agudeza;
user.position = posición;
= user.rotation rotación;

Te darás cuenta de que también en un tiro muy rápido cheque. Si el


actual paquete es de un menor secuencia de identificación, entonces es
descartado. No hay ningún punto en el envío de actualizaciones de
edad, el usuario tendría bastante confundido con objetos aparentemente
al azar de saltar alrededor de el lugar. "Dos pasos adelante, un paso
atrás" no es una buena filosofía cuando se trata de modelo de la
realidad.

Una vez hecho esto, tenemos que crear un mensaje para enviar a los
clientes. Este es un enfoque en dos etapas. En primer lugar, crear un
ByteArrayOutputStream para escribir los datos a:

/ / Crear el búfer de salida una vez


intentar
{
/ / La cabecera RTP octeto 1 = 0x80, 2 = 79d octeto
out.writeByte (0x80);
out.writeByte (79);

/ / Número de orden, fecha y hora, SSRC


out.writeShort (seq_num);
out.writeInt (timestamp);
42

VRML 2.0 con Java CAPÍTULO 19

out.writeInt (src_entid);

/ / La posición
out.writeFloat (posición [0]);
out.writeFloat (posición [1]);
out.writeFloat (posición [2]);

/ / La posición
out.writeFloat (rotación [0]);
out.writeFloat (rotación [1]);
out.writeFloat (rotación [2]);
out.writeFloat (rotación [3]);

/ / Región, la agudeza, banderas


out.writeInt (región);
out.writeFloat (agudeza);
out.writeInt (0);
/ / Cuando se envía una actualización a cabo es 0
}
catch (IOException e)
{
System.out.println ( "usuario del servidor: error de actualización de
la escritura");
retorno;
}

Más tarde, los distintos paquetes crear a cada usuario de esta:

intentar
{
socket = DatagramSocket nuevo ();
}
catch (IOException e)
{
/ / ARGH, Barf!
System.out.println ( "UserServer: incapaz de" +
"saliente actualización crear socket");
retorno;
}

/ / Todo se hace para el mensaje, ahora que enviar a los clientes


sincronizadas (user_list)
{
for (i = 0; i <user_list.size (); i + +)
{
user = (Usuario) user_list.elementAt (i);
43

VRML 2.0 con Java CAPÍTULO 19

paquete = new DatagramPacket (bytes.toByteArray (),


bytes.size (),
user.address,
user.update_port);
intentar
{
socket.send (paquete);
}
catch (IOException e)
{
System.out.println ( "usuario del servidor:" +
"error al enviar paquetes de actualización");
}
}
socket.close ();

Eso es todo lo que hay que crear la salida. Con un diagrama de la


distribución de paquetes de cualquier mensaje, creando el paquete es
simplemente un caso de utilizar el método adecuado para escribir la
longitud de los datos necesarios (8, 16 o 32 bits). Sólo asegúrese de que
escribe en el orden correcto. Octeto 1 octeto se escribe antes de 2, y así
sucesivamente.

Nota: En la jerga de red, 8 bits de la secuencia se conoce como un octeto en lugar


de un byte. Poco en el octeto de numeración comienza a partir del 1 y no 0, también.

Filtrado de datos

En el código anterior, no hemos filtrado los datos. Cada usuario ahora


recibe todas las actualizaciones. Que no es realmente una buena cosa
para que, teniendo en cuenta uno de nuestros objetivos es la agudeza
basado en el filtrado. Así que tenemos que añadir que la capacidad en la
anterior fragmentos de código.

Bernie Como ya ha mencionado, la agudeza se utiliza como prueba de la


filtración. Tenemos que trabajar si la agudeza de los clientes de que el
remitente sea inferior al especificado por cada cliente. Si es así, la
actualización no se enviará. La agudeza es sólo la relación de tamaño a
distancia. El tamaño es el valor suministrado por el remitente. Cálculos
de distancia son sólo la raíz cuadrada normal de los cuadrados de cada
componente de la diferencia entre las posiciones.

La raíz cuadrada cálculos son un cálculo extremadamente lento, por lo


que tratar de evitarlos siempre que sea posible. Podemos hacer esto por
la cuadratura ambos lados y hacer la comparación:
44

VRML 2.0 con Java CAPÍTULO 19

if (tamaño * tamaño / (x * x + y + z * y * z)> = agudeza agudeza *)


sendAnUpdate (...)

donde x, y y z son las diferencias entre el cliente y la posición de la


posición del remitente. Si nos fijamos en esto, se puede ver que
podemos optimizar las cosas un poco. Podemos precalcular el cuadrado
de las distancias del tamaño y la agudeza y dejar a los de la clase de
usuario. Nuestro lazo para enviar actualizaciones de ahora se convierte
en

¡Precaución!: No se olvide de comprobar de dividir por cero o


problemas para volver a organizar el cheque.

sincronizadas (user_list)
{
for (i = 0; i <user_list.size (); i + +)
{
user = (Usuario) user_list.elementAt (i);

user.position x = [0] - posición [0];


y = user.position [1] - la posición [1];
z = user.position [2] - la posición [2];
if ((size_sqd / (x * x + y + z * y * z)) <user.acuity)
continuar;

paquete = new DatagramPacket (bytes.toByteArray (),


bytes.size (),
user.address,
user.update_port);

intentar
{
socket.send (paquete);
}
catch (IOException e)
{
System.out.println ( "usuario del servidor:" +
"error al enviar paquetes de actualización");
}
}
}

Tenga en cuenta que si la agudeza es demasiado pequeño, que


acabamos de probar el siguiente usuario llamando a continuar en vez de
tener una prueba de éxito y, a continuación, introduzca el código dentro
de si una declaración. La razón de este enfoque se hace evidente
45

VRML 2.0 con Java CAPÍTULO 19

cuando empezar a hacer más pruebas de filtrado.

Existen también otras opciones de filtrado para llevar a cabo. Podemos


comprobar en contra de la lista de las regiones que el cliente desea
recibir la información de. La implementación actual de la clase de
usuario almacena la lista de las regiones como una matriz, lo que
significa que tendrá que buscar en la matriz para ver si da la ID del
remitente región es uno de los que el cliente quiere conocer. Una simple
búsqueda lineal se lleva a cabo en el servidor de código, aunque más
sofisticada búsqueda binaria sería más rápido:

para (j = 0; j <user.regions.size, j + +)
if (user.regions [j] == región)
{
encontrado = true;
break;
}
if (! encontrados)
continuar;

Filtrado basado en contar con el horizonte es mucho más difícil, porque


es necesario para ordenar la lista de entidades por la distancia de cada
cliente antes de realizar los cálculos. Esto se deja como ejercicio para el
lector de aplicar.

Filtro de mensajes de Procesamiento

El usuario de servicios deberá también cuenta para los comandos


disponibles en el filtro de mensajes. Un usuario puede enviar suspender,
REQUEST_REFRESH, comandos y se ha ido, por lo que necesitará
algunos métodos para hacer frente a esta.

Actualmente, la lista de usuarios se mantiene como un vector. Cada


usuario en esta lista es un beneficiario potencial de un mensaje de
actualización. Para suspender a un cliente de obtener las
actualizaciones, que acabamos de quitar de la lista. Para eliminar
completamente el cliente (Lo mando), el mismo proceso se sigue. Este
es también el mismo criterio adoptado en la gestión de los recursos en
la fase de barrido método, discutido anteriormente.

Una vez que un usuario es eliminado de esta lista de usuarios, ya no son


conocidos por el usuario del servidor. Para que reciba mensajes de
actualización de nuevo, es necesario que se añade a la lista de usuarios
una vez más, como se discutirá más adelante.

Solicitar una frecuencia de actualización es un poco más de trabajo. El


46

VRML 2.0 con Java CAPÍTULO 19

remitente de esta petición quiere obtener una actualización para cada


objeto dentro de la escena, por lo que necesitamos para tener la
actualización del código de la send_refresh método discutido
anteriormente y modificarlo. La diferencia es que ahora tenemos que
enviar todas las actualizaciones de vuelta a la cliente, no enviar un
mensaje a todos los clientes. Esto es básicamente una reorganización
del código. El bucle for es ahora para abarcar todo el código para que
podamos crear un nuevo mensaje de salida de cada cliente:

public void actualizar (String dirección)


{
int i;
ByteArrayOutputStream bytes =
nuevo ByteArrayOutputStream (BUFFER_LENGTH);
DataOutputStream out = nuevo DataOutputStream (bytes);

DatagramPacket paquete;
DatagramSocket zócalo;

Usuario usuario;
Usuario me = (Usuario) address_list.get (dirección);

intentar
{
socket = DatagramSocket nuevo ();
}
catch (IOException e)
{
/ / ARGH, Barf!
System.out.println ( "UserServer:" +
"no se puede crear zócalo saliente de refresco");
retorno;
}

for (i = 0; i <user_list.size (); i + +)


{
user = (Usuario) user_list.elementAt (i);

/ / Restablecer el flujo a comenzar desde el principio de la bolsa


bytes.reset ();

intentar
{
/ / Escribir los paquetes de datos para este mensaje
}
catch (IOException e)
47

VRML 2.0 con Java CAPÍTULO 19

{
System.out.println ( "usuario del servidor: error de actualización de
la escritura");
retorno;
}

/ / Todo se hace para el mensaje, ahora lo enviará al solicitante


paquete = new DatagramPacket (bytes.toByteArray (),
bytes.size (),
me.address,
me.update_port);

intentar
{
socket.send (paquete);
}
catch (IOException e)
{
System.out.println ( "usuario del servidor:" +
"error al enviar paquetes de actualización");
}
}
}

Chat, filtros, y de actualización

Ahora que hemos hablado de cómo enviar mensajes a los clientes,


tenemos que mirar la parte delantera del sistema: indicando al usuario
del servidor que necesita para llevar a cabo ciertas acciones. A
diferencia de los basados en TCP RegistryMessage clase, muy poco
trabajo que hay que hacer para ampliar los genéricos UDP servidor que
vimos en el capítulo anterior.

La raíz de los debates se refieren sólo a la clase manejador de mensaje


para cada servidor.

El filtro del servidor

El filtro de mensaje de controles de servidor que se actualiza y en qué


condiciones. Hay cuatro posibles mensajes que se pueden enviar. Estos
han sido discutidos anteriormente en este capítulo y en el capítulo
anterior.

Si no se ha establecido bandera (es decir, las banderas de campo es


nulo), entonces es sólo un simple filtro de mensaje. El método de
set_filter servidor que el usuario se llama con los parámetros adecuados
48

VRML 2.0 con Java CAPÍTULO 19

de recuperar el mensaje.

Cuando el método se llama set_filter, se comprueba si el cliente existe


en la lista de usuarios. Si no es así, añade el cliente, si es así, entonces
se actualiza el contenido de la entrada del cliente, teniendo en cuenta
para establecer el ámbito last_update. Si el cliente se ha perdido desde
el sistema, porque no enviar un mensaje de filtro para los últimos 10
segundos, y luego otro filtro mensaje lo convierten en un cliente activo
de nuevo.

El único otro punto a tener en cuenta es la lectura de los datos de la lista


de puertos y regiones. Estos campos son de longitud variable. A través
de una serie de lecturas no funciona:

/ / Lista de las regiones


region_count = data.readUnsignedShort ();

region_list = new int [region_count];

for (i = 0; i <region_count; i + +)
{
region_list [i] = data.readUnsignedShort ();
}

Usted necesita ser más flexible: en primer lugar leer el número de


entradas para ser leído y luego asignar la cantidad de espacio en un
arreglo para almacenar los datos y, por último, bucle y leer los datos en
la matriz.

El servidor de chat

El servidor de chat se basa en los principios básicos esbozados


anteriormente en este capítulo. La única restricción es que el texto de
cada mensaje está limitado a 1.024 caracteres. No demasiado de un
problema, yo esperaría.

Una vez que el mensaje ha sido analizado, se llama a la send_chat


método del usuario del servidor. En esta primera aplicación, sólo hay un
tipo de mensaje. En futuras versiones de este sistema, se puede ampliar
esta funcionalidad para incluir cosas como uno a uno y el grupo de chat.
Para ello sería necesario que se busque en el pabellón y, a continuación,
llamar al método adecuado (que tendría que ser el original) en el
servidor del usuario.

Una opción que puede considerar es la tala, ya que el texto viene a


través. Ya que cada mensaje es recibido, el texto, junto con el ID de
49

VRML 2.0 con Java CAPÍTULO 19

usuario, está escrito en un archivo como un archivo o registro de la


conversación.

El servidor de actualizaciones

Al igual que el servidor de chat de texto, este componente es bastante


genérica. Después de leer los campos de la entrada de paquetes, el
sistema llama al método send_update del usuario del servidor. Este
método ya ha sido ampliamente cubiertos en este capítulo.

Montaje de las piezas

Una vez que todas las piezas se han presentado hasta la fecha, es
necesario convertirlos en algo útil. Muserver la clase se encarga de ello.
Queremos que el servidor se ejecute como una aplicación
independiente, lo que implica que tenemos que poner en un método
principal en alguna parte.

Arrancar el servidor

Muserver.java se encarga de conseguir la aplicación y funcionamiento. El


principal método de toma estática en la línea de comandos y lo analiza
para cualquier información adicional que puedan ser necesarios.

Esta primera aplicación sólo tiene dos argumentos: un facultativo a su


vez la bandera de depuración, y el número de puerto base para los
servidores, también opcional. Parsing-es muy sencillo, simplemente,
mirar en la primera letra de cada argumento a favor de un signo menos
y, a continuación, elaborar el argumento de comparar con una cadena a
partir de ahí:

public static void main (String [] args)


{
int i;
int port = 3000;
boolean_debug = false;

if (args.length> 0)
{
for (i = 0; i <args.length; i + +)
{
if (args [i]. charAt (0) == '-')
{
if (args [i]. iguales ( "-d"))
_debug = true;
algo más
50

VRML 2.0 con Java CAPÍTULO 19

{
System.out.println ( "Uso: muserver [-d] [puerto]");
System.exit (1);
}
}
algo más
{
intentar
{
port = Integer.parseInt (args [i]);
}
de capturas (NumberFormatException e)
{
System.out.println ( "número de puerto no válido");
System.out.println ( "uso de puerto por defecto 3000");

/ / Ajuste de la paranoia
port = 3000;
}
break;
}
}
}
muserver nuevo (puerto, _debug);

Si el usuario ha establecido una línea de comandos incorrecta, el


servidor se interrumpe con el habitual estilo UNIX de uso de mensajes, o
los intentos de recuperar el uso de los puertos por defecto. Una vez que
esto ha terminado, se pasa a la creación de una instancia de esta clase.

Otros Servidores de comenzar la

Con la aplicación principal en marcha, ahora tenemos que crear todos


los demás servidores. En primer lugar fuera de la lista son las dos
nonnetwork consciente de servidores: UserServer y RegistryServer.

Para cada uno de los servidores, he creado para contener ThreadGroups


relacionados hilos. Estos hilos son estáticamente crea cuando se crea la
clase y luego se pasa al servidor adecuado cuando se creó. Para cada
servidor se pasa a utilizar el puerto, el ThreadGroup al que ésta
pertenece, y la bandera de depuración. Para cada uno de estos es
también aprobó el servidor interno que es apropiado. Registro se pasa a
la RegistryServer, y los otros obtener UserServer.

Después de que todos los servidores, se han creado, el método se llama


empezar en cada una de ellas para que sean activos.
51

VRML 2.0 con Java CAPÍTULO 19

Después de partir

Volver al principio de este capítulo he dicho que quiero a este servidor ser tan
robusto como sea posible. Esto incluye la recuperación de circunstancias no
habituales. Una de las mejores maneras en que podemos lograr esta capacidad
es comprobar continuamente sobre el estado de cada uno de los servidores de
un tercero.

Esta verificación de terceros puede ser alcanzado por nuestra clase y muserver.
Sin embargo, para ello, tenemos que tener también en funcionamiento todo el
tiempo. No hay problema: Vamos a hacer en otro hilo también.

En el método de ejecución de esta clase, vamos a comprobar cada cierto


tiempo sobre el estado de cada uno de los servidores. Si todavía están
funcionando correctamente, entonces puede llamar a la isActive método para
cada uno de ellos y obtener un resultado positivo. Si no es así, tenemos que
reiniciar el servidor. Por esta manifestación, supongo que lo peor y crear toda
una nueva instancia del servidor requiere:

if (! chat.isAlive ())
{
System.err.println ( "Reiniciar servidor de chat");
chat = new Chat (base_port 4, chatThreadGroup, userver);
chat.start ();
}

El control de los servidores se realizan sólo para los servidores de red. El


registro de usuario y los servidores no se benefician de estos controles. Si ellos
han muerto, entonces no hay nada que podamos hacer otra cosa que apagar el
servidor y empezar de nuevo. Si hemos intentado reiniciar ellos, habría todo
tipo de extraños problemas con las conexiones de medio concluido que tendría
que ser cancelado. Es más seguro para salir y dejar que el administrador del
sistema, reinicie el servidor:

if (! rserver.isAlive ())
{
System.err.println ( "Registro del servidor ha muerto, de salir.");
System.exit (2);
}

Control de los servidores no es una tarea especialmente importante para todo


el sistema, por lo que le dan una prioridad de MIN_PRIORITY 1 (que es justo por
encima de la del sistema de recolección de basura). A continuación, ejecutar el
método de control de la situación cada 30 segundos o menos.

Optimización del servidor

Después de haber escrito ahora el servidor de base, hay un montón de cosas


52

VRML 2.0 con Java CAPÍTULO 19

que podemos hacer para hacerlo funcionar mejor. El primer lugar que hay que
ver es cuando nosotros estamos usando el Java incorporado en los tipos de
almacenamiento-es decir, Vector y Hashtable.
Manipuladores de datos personalizadas

Java incorporado en manipuladores de datos fueron diseñados para ser tan


robusto como sea posible. Tenían que ser hilo de seguridad y en condiciones de
recuperarse de casi todo lo que se lanza en su favor. Esto hace que sean
inherentemente lento. Mediante la creación de nuestros propios manipuladores
de datos, que puede acelerar los tiempos de acceso a los datos
considerablemente.

Los recursos necesarios para bloquear y desbloquear los monitores de


vigilancia de las estructuras son muy lentos. La cifra aceptada es que los
tiempos de acceso lento por un factor de entre 5 y 10. Tenemos que hacer algo
para mejorar la velocidad, ya que hay 3 niveles de monitores utilizados en la
mayoría de los accesos tanto al registro del servidor y el usuario del servidor.
Acelerar la tramitación de recursos de Contención: Un escritor de un solo lector
de múltiples Guardia

Vamos a considerar lo que estamos tratando de hacer. La mayoría de las veces,


hemos de intentar múltiples lecturas que tendrá lugar, y algunos de la época,
que lo desea, puede escribir los datos para actualizar la información.
Normalmente, hay mucho más que escribe dice que quieren que suceda en
paralelo. Hmm, único escritor y múltiples lectores: una perfecta aplicación de
algunas técnicas de la programación concurrente mundo (especialmente en el
ámbito del diseño de sistema operativo). El único escritor-, de múltiples Reader
(SWMR) tiene un problema bien establecido solución que demanda nuestra
tarea perfectamente.

Si bien esto todavía no se ha integrado en el servidor, voy a explicar cómo


escribir su propio SWMR sistema (conocido como la Guardia SWMR, SWMRG).
La base fundamental para una SWMRG es mantener a un contrario de leer el
número de solicitudes y peticiones de escritura. Un usuario debe tener permiso
para escribir o leer antes de proceder. Cuando haya terminado, deben solicitar
su liberación inmediata.

Acciones de lectura y escritura

Estas dos solicitudes requieren una llamada al beginRead o beginWrite. Cada


uno de estos métodos a continuación, se examina el número de lectores o de
escritores que actualmente espera. Si, por una lectura, hay un número de
lectores ya los escritores, pero no activo en espera, entonces el sistema se le
permite comenzar a leer después de incrementar el número de lectores
bandera y el método de salir de inmediato. Una vez que la llamada ha
terminado con el recurso, pide endRead o endWrite. Por otro lado, si hay un
escritor de espera (número de escritores de espera es distinto de cero),
entonces la llamada beginRead bucles y bloques, comprobando las condiciones
hasta que se le permite leer de nuevo.
53

VRML 2.0 con Java CAPÍTULO 19

Si sólo fue con este esquema básico, que muy pronto en problemas. ¿Qué pasa
si tenemos dos peticiones de escritura en cola, y una serie de solicitudes a que
se lea bien? Un escritor se permite el acceso, y luego viene otra petición en
escribir, dejando dos todavía en la cola. ¿Quién consigue el acceso al escribir la
primera libera su bloqueo?
Prevenir bloqueos de recursos

En este punto, tenemos que tomar una decisión acerca de los derechos de
acceso. Decidimos cuántos consecutivos escribe se permite antes de leer las
peticiones, es un honor. Una vez que el número de escrituras pasa por encima
de nuestro límite preestablecido, los lectores se les permite volver a los datos.
Una vez que el lector tiene acceso, se puede entonces definir el número de
escritores de vuelta a cero o decremento por 1 para cada lectura permite pulg

Cualquiera solución tiene algunos problemas. En un sistema muy cargado, el


ex recurre básicamente a permitir una lectura seguida de una escritura. Esta
última solución sería bloquear posibles escribe a la espera de que el número de
escrituras para volver a cero. En un sistema ligeramente cargado, esto podría
tomar mucho tiempo. Usted necesita para que el diseño elegido.

El uso de este guardia en el registro del servidor sería ideal. Hay muchas
lecturas, pero normalmente no son muchos los escribe. Cada uno de los
métodos puede ser sincronizado para evitar la entrada de los problemas, pero
después de que podemos tirar la intrínsecamente lento y tablas hash vectores,
reemplazarlos con nuestras propias implementaciones, y combinarlos con un
SWMRG para actuar como mecanismo de seguridad.

Reescritura de la estructura de datos

Como ya he mencionado en esta sección, hay que reescribir la Java


proporcionado datos con nuestros propios manejadores de
implementaciones personalizadas.

Hastables, dependiendo de la aplicación de la clasificación alogorithm


internamente, puede ser muy rápida para obtener datos sobre la base
de las claves que hash muy bien-como cadenas. Números no funcionan
tan bien. Por otra parte, un vector no es mucho mejor que una norma de
la matriz.

¿Por qué no sustituir estas estructuras de datos con estructuras mucho


más eficaz para los tipos de búsquedas necesarias? Muchas veces
cuando se utilizó un vector, una lista por separado normal funcionaría
igual de bien. Con tablas hash, que podría ser más exóticas y la
utilización de una estructura basada en un árbol. (Rojo-Negro o árboles
B-árboles son generalmente los mejores en este escenario.) Estas
decisiones de diseño se dejan a usted para jugar.
54

VRML 2.0 con Java CAPÍTULO 19

SUGERENCIA: Lea Doug tiene una colección de clases que


implementan muchas de las estructuras de datos común. Estos pueden
ser encontrados en la http://gee.oswego.edu/dl/classes/collections/.

Más rápido Parsing

La clase RegistryMessage utiliza una gran lista de cadena se comparan


para decidir qué es el siguiente comando. Completo cadena compara
son lentos. Para acelerar las cosas, podemos crear nuestros propios
pseudostring control para la comparación de los caracteres en orden.
Mediante esta técnica, se puede decidir a menudo mucho más rápido
que el comando es simplemente mirando las primeras letras.

Estoy seguro de que usted está familiarizado con esta técnica de


programación en otros idiomas, como C / C + +. El pseudocódigo es algo
a lo largo de las líneas de

switch (token.charAt (0))


{
caso 'A':
if (token.charAt (1) == 'L')
/ / Asignar
else if (token.charAt (1) == 'V')
/ / Avatar
break;

case 'C': / / entonces tenemos un chown


break;

case 'G':
if (token.charAt (1) == 'O')
/ / Adiós
else if (token.length == 9)
/ / GetEntity
else if (token.length == 13)
/ / GetEntityInfo
break;

/ / Etc etc
por defecto:
out.println ( "OK");
}

Hay algunos problemas que surgen con este diseño. No suele asegurar
55

VRML 2.0 con Java CAPÍTULO 19

que la palabra que constituye la primera razón es realmente la correcta.


La decisión se hace sólo en un número determinado de caracteres.

Un parcial alternativas podrían incluir comparaciones de cadena


completa, en cada caso, declaración, ya que hay sólo tres en la mayoría
de los comandos que tienen la misma primera letra. Problemas de
rendimiento no se plantearía en este caso, porque sólo tenemos un
pequeño número de comparaciones. Con esta estrategia, podemos
garantizar que toda la palabra está correctamente introducido.
Ejecutando el servidor

Una vez que el servidor ha sido compilado, se puede ejecutar como


cualquier otra aplicación. En la línea de comandos, puede escribir

java muserver

para iniciar el servidor. Esto lleva a los parámetros por defecto del
servidor en ejecución. La base se fija en el puerto 3000, y la información
de depuración está desactivada.

Para activar la depuración, puede configurar el primer argumento-d. El


segundo argumento es la base de número de puerto, si quieres usar algo
más que el puerto 3000. Por ejemplo:

muserver java-d 5010

se inicia el servidor con la depuración en la base y la dirección del


puerto 5010.

Como el servidor se inicia, debe ver una serie de líneas de información


que se está imprimiendo a la salida:

A partir de usuario multi servidor de Medio Ambiente


A partir del Registro del servidor
A partir de usuario del servidor
Servidor de Actualización de partir en el puerto UDP 3000
A partir del Registro del servidor en el puerto TCP 3000
A partir del filtro de servidor en el puerto UDP 3002
A partir de chat del servidor en el puerto UDP 3004

Si usted ve esto, entonces todas las cosas están funcionando bien. Si no


es así, entonces algunos otros mensajes serán impresos, especialmente
si el servidor no puede crear un socket en un puerto de la dirección.

Ahora ya está listo para las conexiones de los clientes. No hay nada más
que lo que tiene que hacer.
56

VRML 2.0 con Java CAPÍTULO 19

Resumen

En este capítulo se ha ido muy fuertemente en la manera de escribir un


proceso de servidor en Java. Como has visto, hay un poco más a la
escritura de un utilizable, robusto proceso de servidor que acaba de
lanzar un par de hilos y tomas de corriente y la esperanza de que así
funciona. Escribir un proceso de servidor no es una tarea trivial y no
debe tomarse a la ligera, pero resulta mucho más fácil cuando se tiene
un acuerdo en trabajar con un protocolo particular.

Diseño servidor también requiere de mucha consideración de cómo


aplicar las cosas. Como he señalado, por un simple cambio de tipos en
incorporado a nuestro propio código, podemos aumentar el rendimiento
considerablemente.

En el siguiente capítulo, nos fijamos en la parte final de tener una


presencia en el ciberespacio. Usted necesita un órgano. Una de las
preguntas más frecuentes es: "¿Cómo puedo hacer un avatar?" El
siguiente capítulo se analiza la forma en que usted realmente puede
hacer que usted parezca Prince Charming o Marilyn Monroe.