Professional Documents
Culture Documents
La Aplicación de un Cliente
2
Contenido CAPÍTULO 18
El flujo de datos se envía a través de una red multicast a todos los demás
clientes, cada uno de los cuales se presenta el flujo de datos a sus usuarios.
Cada cliente también mantiene una base de datos de entidades cuyo estado se
actualiza utilizando los datos recibidos.
Filtrado
Cada cliente tiene una agudeza ajuste, lo que indica el tamaño mínimo a través
de la distancia en la proporción necesaria para que una entidad a ser visible
para que el sistema cliente. El servidor tiene el tamaño de una entidad y la
divide por la distancia entre la entidad y la ubicación del punto de vista del
sistema cliente. Si la proporción es inferior a la agudeza para el cliente,
entonces la entidad es demasiado pequeño y / o de lejos para que el cliente
vea. En tal caso, no hay actualizaciones de la entidad que se envían al cliente.
Por supuesto, hay muchas maneras de optimizar este cálculo a fin de evitar
cálculos de la distancia real o divisiones, algunas de estas se discuten en el
capítulo 19.
Entidad de Registro
Figura 18-1 muestra las relaciones entre los diversos componentes del sistema.
Hay varias cosas importantes de destacar. El registro y el filtrado de acogida
pueden estar situados en el mismo sistema, e incluso pueden ser parte del
mismo proceso. Ambos residen en la red multicast, que es utilizado para
comunicarse tanto los datos de los registros y actualizaciones.
Figura 18.3 La interfaz entre las diferentes partes del software de cliente
Los tres de los principales módulos (navegador VRML, applet de chat de texto,
multiusuario applet) se encuentra atado por el hecho de estar en la misma
página web, como se muestra en la Figura 18.4.
7
Vamos a comenzar por el examen de las cinco capas de TCP / IP modelo para
las comunicaciones de red, como se muestra en la Figura 18.5. La capa física
se refiere a la transmisión (cables coaxiales, fibra óptica, cables serie, y así
sucesivamente). La capa de enlace de datos se refiere a la escasa capa de
protocolo, como SLIP o PPP o Ethernet elaboración.
RTP se suele utilizar sobre UDP, aunque no hay nada acerca de RTP que
requiere. RTP proporciona la funcionalidad básica que suele ser requerida por
cualquier aplicación en tiempo real: los números de secuencia, tiempo, y
fuente de identificación. Vamos a utilizar todos estos en nuestra aplicación.
El encabezado de RTP
Cada paquete de RTP, que suele ser encapsulada dentro de un paquete UDP,
tiene un título de al menos 12 bytes. Este encabezado se muestra en la Figura
18.6.
Los dos primeros bits son un número de versión, que actualmente es 2. Hay
otro pabellón bits que no va a utilizar: Relleno, la extensión y el marcador.
También hay un recuento de las fuentes de los contingentes, que no va a
utilizar. Si eres curioso acerca de lo que estos campos se utilizan para, consulte
RFC 1889. En nuestro caso, sólo tendremos que configurar todos ellos a cero.
Tipo de la carga útil es un número de 7 bits que indica cómo el contenido de los
paquetes de RTP debe interpretarse. Algunos de estos tipos de carga ya se han
asignado, en su mayoría de diversos tipos de datos de audio comprimido.
Vamos a seleccionar arbitrariamente sin utilizar dos tipos de carga útil para
nuestros propósitos: 79 de movimiento y 80 actualizaciones para los mensajes
de texto.
9
La cabecera es seguida por la carga útil, los datos reales que se envían. Al
igual que la cabecera de los usos de la carga útil de red estándar de pedidos de
bytes (de alto orden octetos primero). Tendremos dos tipos diferentes de carga
útil: el movimiento y las actualizaciones de los mensajes de texto.
Propuesta Actualizaciones
Cuando enviar una actualización de posición para una entidad, la carga útil
tendrá el formato se muestra en la Figura 18.7. Los campos son todos bastante
sencillos. Los tres primeros de 32 bits de punto flotante son los valores x, y y z
de la entidad-en otras palabras, un SFVec3f VRML. Los próximos cuatro son el
eje y el ángulo que especifica la orientación de la entidad, que debe reconocer
como uno de VRML SFRotation.
Tras esta es una medida del tamaño de la entidad. Esto se utiliza para el
filtrado basado en la agudeza, como se describe anteriormente en este
capítulo. No es esencial que este valor se precisa en particular, algo así como
el diámetro de una esfera saltando alrededor de la entidad, o incluso la
duración de la más larga del eje de una caja, lo haría bien.
Por último, hay un conjunto de banderas. Por el momento, sólo el de bajo orden
se utiliza poco, si conjunto, indica que la entidad que envía esta actualización
es salir de la simulación.
Mensajes de texto
Filtrar mensajes
La actualización del puerto local es el puerto UDP en el cliente para que las
actualizaciones de movimiento deben ser enviadas por el filtro de acogida.
Desde este puerto se asignan dinámicamente por la pila TCP / IP en el cliente,
es necesario enviar esta información para el filtro de acogida. Por ejemplo,
digamos que el cliente crea un DatagramSocket usando el paquete java.net en
la que espera recibir actualizaciones movimiento. El puerto local es la dirección
asignada dinámicamente, por lo que el filtrado de acogida debe ser informada
de este número de puerto para enviar mensajes de actualización de
movimiento a la misma.
La x, y y z son los valores de punto flotante números que dan la posición del
punto de vista. Tenga en cuenta que estos no tienen que corresponder a la
ubicación de cualquiera de las entidades, es decir, estos x, y, z y los valores
son independientes de los utilizados en la propuesta de actualización de
mensajes. Sin embargo, para el avatar del usuario, estos valores suelen ser los
mismos que en la propuesta de actualización de mensajes.
Estos campos son seguidos por un recuento del número de regiones que son
visibles al cliente y, a continuación, por una serie de los números de la región.
Si el cliente puede ver las regiones 4, 18, 92, y 104, entonces la región se
cuentan 4 y será seguido por esos cuatro valores. Sólo mensajes de
actualización de las entidades en estas regiones se enviarán al cliente.
Recordemos que la actualización de cada mensaje contiene el número de
región de la entidad que lo envió.
(que se especifica como el primero de los puertos adicionales al final del filtro
de mensajes, como se ha descrito anteriormente).
El cliente abre una conexión TCP en el registro y uso de una línea orientada a
ASCII protocolo similar al utilizado por SMTP o NNTP. El cliente envía un
comando al servidor y obtener una respuesta de vuelta.
El cliente puede solicitar información sobre cualquier entidad, obtener una lista
de entidades, o para obtener una lista de entidades que han actualizado
recientemente sus entradas de registro.
Si el registro está conectado a una red multicast, puede utilizar RTCP (parte de
la RTP protocolo descrito anteriormente) para enviar la información de registro
a otros anfitriones en la forma de SDES (Fuente descripción) mensajes. Esta
funcionalidad no está implementada en esta versión del software de servidor,
para obtener más información acerca de RTCP, consulte el documento RFC
1889.
REHUSÓ reason_for_refusal
Por ejemplo, si el servidor sólo está dispuesta a aceptar hasta 25 clientes, que
podría responder con un mensaje a cualquier REHUSÓ adicionales clientes
potenciales.
El Comando GOODBYE
GOODBYE
GETENTITY entid
GETENTITY 2357
alias
SSRC_values
El CNAME es el nombre que utiliza cuando la entidad fue creada (es decir, el
nombre que se especifica en el comando ALLOC, descrito más adelante). El
campo url es la URL del archivo VRML que describe esta entidad. El propietario
de campo es el nombre de usuario de la IDENT que estaba en efecto cuando el
ALLOC de esta entidad se ha hecho. Tenga en cuenta que la respuesta se dé
por concluido con un punto (.) En una línea por sí mismo, lo que nos da la
flexibilidad, si decide regresar más información en las futuras versiones del
protocolo.
Getinfo entid
LISTA
17
El registro responde con una lista de entids, uno por línea, terminando con una
línea que contenga sólo un punto.
El Comando LISTNEW
LISTNEW millisecs
El Comando IDENT
OKAY
REHUSÓ optional_reason_for_refusal
Tenga en cuenta que varios IDENT comandos pueden ser emitidas en el mismo
período de sesiones, la más reciente está siempre en vigor.
El Comando ALLOC
ALLOC CNAME
El registro crea una entrada para la nueva entidad y se asigna una ID única
entidad para la misma. La respuesta al cliente es la entidad o el
REHUSÓ optional_reason_for_refusal
LIBERACIÓN entid
OKAY
REHUSÓ optional_reason_for_refusal
OKAY
REHUSÓ optional_reason_for_refusal
INFO entid
OKAY
REHUSÓ optional_reason_for_refusal
Como puede ver, la clase de mundo es bastante mínima. Esto se debe a que
existe principalmente para mantener la base de datos de entidades, muchas
de las funcionalidades que usted puede ser que esperan encontrar aquí es
realmente en la clase de entidad.
La Entidad de clase
Cada entidad tiene una única entidad de 32 bits de identificación que se asigna
por el Registro. Este es el mismo que el valor SSRC en la propuesta de
actualización de mensajes, y que sirve de base para la identificación de las
entradas en la base de datos de la entidad. Un método es siempre para
encontrar el ID de una entidad:
Una vez que el applet multiusuario avisos de que una entidad es STALE o
desaparecido, se puede eliminar de la entidad en tres dimensiones de la
representación de VRML mundo y, a continuación, establecer el estado de la
entidad a MUERTOS. En este punto, la API es libre de eliminar la entidad,
siempre es conveniente hacerlo.
Cuando una entidad que aparece por primera vez en la simulación, todos los
24
Aviso de nuevo el uso de la utilidad Vec3 clase. Observe también que tenemos
una clase de utilidad para las orientaciones, llamado de rotación. Estas clases
se describen más adelante en este capítulo.
Una entidad puede ser "silenciado" (lo que significa que los mensajes de texto
25
no debe ser recibido de ella) y / o "oculto" (lo que significa que no es visible).
Estas propiedades se pueden fijar y se preguntó a través de un conjunto de
métodos:
Una vez que el applet se ha acelerado la nueva URL u otros datos de los
registros, es necesario borrar la bandera que dice que ha cambiado los datos:
Por último, tenemos alguna manera de asociar a una entidad con una
representación visual en el mundo VRML. Cada entidad almacena una
referencia a un java.lang.Object (la clase base para todos los objetos en Java)
que hace referencia a los datos de VRML para la representación visual de la
entidad. Esta referencia se puede leer y escribir con los siguientes métodos:
método. Tenga en cuenta que no hay nada acerca de esta API que es en
realidad VRML específicos, la representación puede ser cualquier cosa, y la
dirección URL se puede referir a cualquier tipo de datos.
información:
El TextReceiver clase es sólo una muy delgada capa en todo el bajo nivel de
formato de mensaje de texto. El constructor no tiene argumentos y
simplemente crea un socket para recibir mensajes de texto en:
Hay métodos para establecer y obtener el ID de texto (es decir, el SSRC de los
paquetes RTP utiliza para enviar mensajes de texto):
29
No hay ningún método para fijar el tipo de mensaje, ya que la actual versión
del software siempre se fija el tipo de mensaje a cero.
El Cliente-a-cliente de la API
Y, por último, hay referencias a todos los hilos que el mundo crea objeto:
Sin embargo, vale la pena señalar que los métodos que establecen los
parámetros de filtro que el filter_sender hilo que despertar y enviar un nuevo
mensaje para el filtro de acogida. Por ejemplo, aquí está lo que el setViewpoint
método tiene el siguiente aspecto:
Otros dos métodos son dignos de mención, ya que proporcionan acceso a las
"bases de datos" de las entidades locales y entidades:
(
/ / Conectar hasta registro de acogida
InetAddress = InetAddress.getByName anfitrión (host);
registry_socket = new Socket (host, puerto, true);
registry_input = new
DataInputStream (registry_socket.getInputStream ());
registry_output = new
PrintStream (registry_socket.getOutputStream (), true);
registry_output.println ( "hola");
Cadena de respuesta registry_input.readLine = ();
if (response.length ()> 7)
bandera = response.substring (8);
algo más
bandera = "";
if (response.startsWith ( "REHUSÓ"))
lanzar nuevos ConnectionRefusedException (bandera);
connected_time = System.currentTimeMillis ();
incoming_socket = new DatagramSocket ();
LocalEntity.setHost (host, puerto 2) / / puerto de datos
/ / Iniciar hilos
update_receiver = new UpdateReceiver (este);
filter_sender = new FilterSender (este, host, puerto);
segador = new Reaper (este);
registry_updater = new RegistryUpdater (este);
change_monitor = new ChangeMonitor (este);
)
El nombre de host se resuelve en una dirección IP, un socket TCP, se crea para
comunicarse con el Registro de acogida, y un par de arroyos se crean para
hablar con el Registro. HOLA el mensaje es enviado, la respuesta está marcada,
y, en caso necesario, se lanza una ConnectionRefusedException.
El constructor termina por crear una serie de hilos para tratar diferentes
aspectos del sistema multiusuario. Estos hilos se describen por separado a
continuación. El hilo se pasa filter_sender la dirección y el puerto al que filtrar
33
Este método fija el pasado las fuerzas de la bandera y el hilo filter_sender para
enviar un nuevo mensaje al filtro de acogida. A continuación, se detiene los
hilos que se han creado en el constructor, dice adiós a la secretaría de acogida,
y cierra la conexión.
Esa es la clase de mundo, ahora echemos un vistazo a los hilos que se genera.
El Hilo FilterSender
paquete multianual;
world.request_refresh = false;
)
if (world.gone)
banderas | = 0x0004;
out.writeShort (world.incoming_socket.getLocalPort ());
out.writeShort (banderas);
out.writeFloat (world.location.getX ());
out.writeFloat (world.location.getY ());
out.writeFloat (world.location.getZ ());
out.writeFloat (world.acuity);
out.writeInt (world.horizon);
out.writeShort (0); / / no las regiones
if (world.text_port == 0)
out.writeShort (0); / / sin puertos
else (
out.writeShort (1) / / un puerto adicional
out.writeShort (world.text_port); / / el texto del puerto
)
out.writeShort (0);
)
catch (IOException e) (
System.out.println (
"Error al edificio filtro mensaje:" + e);
retorno;
)
DatagramPacket paquete =
nuevo DatagramPacket (bout.toByteArray (), bout.size (),
dirección, puerto);
try (out_socket.send (paquete);)
catch (IOException e) (
System.out.println ( "No se pudo enviar paquetes:" + e);
retorno;
)
try (espera (5000);)
de capturas (InterruptedException e) ()
)
)
)
El constructor sólo mantiene una referencia al mundo, así como a la acogida y la dirección
del filtro de acogida. La carrera () crea un método de toma de datagrama, luego se sienta en
un bucle activamente la construcción de filtrar los mensajes y enviarlos. Duerme durante
cinco segundos (es decir, 5000 milisegundos) entre las actualizaciones, o que si menos de
notificar () es llamado cuando el hilo.
El Hilo UpdateReceiver
UpdateReceiver el hilo se crea para recibir los mensajes de actualización del filtro de
acogida.
37
paquete multianual;
Mundo mundo;
El código es muy sencillo. La carrera () hace que el método de hilo para bloquear los
paquetes en espera. Cuando uno llega, que se convirtió en un nuevo UpdateMessage. La
entidad de identificación de la entidad que envía el mensaje se extrae, y la entidad se
observa en la base de datos local. Si no es encontrado, entonces no hemos escuchado de
38
esta entidad antes, por lo que añadirlo a la base de datos. Por último, dijo que la entidad de
que se actualice el mensaje.
El Hilo UpdateMessage
paquete multianual;
importación java.io. *;
clase UpdateMessage (
int seqnum;
int entid;
largo de tiempo;
Vec3 ubicación;
Orientación de la rotación;
int región;
flotador tamaño;
booleano pasado;
/ / Métodos de acceso:
público int getEntityId () (return entid;)
público Vec3 getLocation () (return location;)
Rotación getOrientation público () (return orientación;)
pública a largo getTimestamp () (return hora;)
público int getRegion () (return zona;)
flotador público getSize () (return tamaño;)
boolean isGone () (return pasado;)
int getSequenceNumber () (return seqnum;)
Después de una comprobación rápida para asegurarse de que el tipo de carga útil es
correcta, los campos son recogidos y almacenados. Tenga en cuenta que dejamos de lado
los paquetes que llegan fuera de secuencia.
El Hilo ChangeMonitor
paquete multianual;
importación java.io. *;
importación java.util .*;
Mundo mundo;
while (true) (
Cadena de respuesta;
try (
respuesta = world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a NUEVA LISTA:"
+ Ex);
break;
)
if (response.startsWith ("."))
break;
int entid = Integer.parseInt (respuesta);
Entidad e = world.getEntity (entid);
if (e == null)
e = world.addEntity (entid);
algo más
e.needsAllInfo ();
)
)
)
)
)
Una vez más, nada demasiado sorprendente aquí. El hilo se despierta cada 10
segundos, y pide el registro de una lista de las entidades que han cambiado
durante ese período. Para cada entidad, se efectúe una prueba para ver si ya
existe, si no es así, que ha creado. En cualquier caso, la entidad se marca como
la necesidad de tener todas sus información (apodo, dirección URL, etc)
actualizado.
El Hilo RegistryUpdater
paquete multianual;
importación java.io. *;
importación java.util .*;
41
Mundo mundo;
. iguales ( "true"));
e.setPersistent (tokens.nextToken ()
. iguales ( "true"));
e.setCreationTime (Long.parseLong (
tokens.nextToken ()));
try (respuesta =
world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a GETENTITY:"
+ Ex);
retorno;
)
e.setNickName (respuesta);
try (respuesta =
world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a GETENTITY:"
+ Ex);
retorno;
)
tokens = new StringTokenizer (respuesta);
e.setTextSSRC (Integer.parseInt (
tokens.nextToken ()));
do (
try (respuesta =
world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a
GETENTITY: "
+ Ex);
retorno;
)
) While (! Response.equals ("."));
world.registry_output.println (
"Getinfo" e.getId + ());
String [] info = new String [20];
int ind = 0;
while (true) (
43
try (respuesta =
world.registry_input.readLine ();
)
catch (IOException ex) (
System.out.println (
"no se ha podido leer la respuesta a
Getinfo: "
+ E);
break;
)
if (response.startsWith ("."))
break;
if (ind <info.length)
info [ind + +] = new
String (respuesta);
)
e.setInfo (información);
e.gotAllInfo ();
)
)
)
)
)
)
)
Si una entidad va fuera del alcance del cliente o en una región que
actualmente no es visible para el cliente, o si se deja por completo la
simulación, los datos de esa entidad debe ser removido de la copia local de la
base de datos:
paquete multianual;
44
Mundo mundo;
paquete multianual;
/ / Registro de acogida:
CNAME cadena / / único nombre canónico (como por RTP)
Cadena url / / URL del archivo de descripción de la entidad
Cadena apodo / / apodo de la entidad
largo creationTime / / timestamp mundo de la creación de la entidad
boolean persistente = false; / / entidad persiste después propietario hojas
boolean avatar = false; / / entidad está dirigida por un ser humano
Cadena propietario / / nombre de usuario de la entidad titular
String [] info / / adicionales (al estilo SDES) Información de entidad
boolean registry_changed; / / establecer si los datos del registro ha cambiado
Hay banderas para indicar que el usuario quiere esta entidad a ser ocultado o
silenciado. Estas son utilizadas únicamente por el applet y no se envían a
través de la red, una discusión completa de ellos se encuentra más adelante en
este capítulo en la sección relativa a la aplicación cliente.
Hay una serie de métodos de acceso, que se omiten aquí por brevedad. El
constructor establece el tiempo de la última actualización a la hora actual e
inicializa la ubicación y orientación de la entidad:
cambiado = true;
location = update.getLocation ();
orientación update.getOrientation = ();
timestamp = update.getTimestamp ();
región update.getRegion = ();
size = update.getSize ();
if (update.isGone ())
= estado han ido;
algo más
estado = OK;
lastHeardFrom = System.currentTimeMillis ();
)
La clase LocalEntity
El LocalEntity clase representa entidades que se han creado en la máquina local. Estas
entidades son responsables de enviar las actualizaciones de movimiento por sí mismos para
el filtro de acogida. La dirección IP y el número de puerto que estas actualizaciones
deberán enviarse a la se almacenan como variables de clase, y hay una clase de método de
fijación de ellos. También hay una toma de datagrama que se utiliza para enviar las
actualizaciones, junto con un ByteArrayOutputStream y un DataOutputStream que se
utilizan para la construcción del mensaje:
paquete multianual;
importación java.io. *;
importación java.net .*;
48
int id;
Cadena CNAME;
Url cadena;
String [] info;
boolean is_avatar = true;
boolean is_persistent = false;
int text_id;
Cadena apodo;
int seqnum = 0;
Vec3 ubicación Vec3 = new ();
Nueva orientación de la rotación Rotación = ();
largo timestamp = System.currentTimeMillis ();
int región = 0;
size = 2 septies flotador;
boolean ido = false;
Hay de nuevo una serie de métodos de acceso, que no vamos a enumerar aquí para ahorrar
espacio.
dout.writeFloat (tamaño);
dout.writeShort (ido? 0x0001: 0x0000); / / banderas
DatagramPacket paquete = new
DatagramPacket (bout.toByteArray (),
bout.size (), la dirección, puerto);
+ + seqnum;
sincronizada (sockets) (
socket.send (paquete);
)
)
)
Todos los campos se actualizan cada vez que alguno de ellos los cambios. Este
no es el diseño más eficiente, pero el ancho de banda disponible en el
"ascendente" de lado (desde el cliente en el registro) es abundante en relación
con la "bajada" hacia el tráfico, así que no es demasiado malo.
Eso es todo lo que hay que LocalEntity la clase. Como la mayoría de los demás,
es muy simple.
La clase TextSender
paquete multianual;
InetAddress de acogida;
int puerto;
int text_ssrc;
int seqnum = 0;
DatagramSocket zócalo;
DatagramPacket paquete;
52
ByteArrayOutputStream combate;
DataOutputStream dout;
paquete multianual;
DatagramSocket zócalo;
DatagramPacket paquete;
byte [] buffer;
El texto de clase
paquete multianual;
importación java.io. *;
El tipo de campo es siempre cero, pero esto no es marcada. El tipo de carga útil
se verifica, sin embargo.
La Utilidad de las clases
55
Hay un par de clases pequeñas que proporcionan apoyo a los vectores en tres
dimensiones (similar a un SFVec3f VRML) y del eje de rotación de ángulo
(similar a un SFRotation VRML). El Vec3f clase tiene este aspecto:
paquete multianual;
importación java.io. *;
público Vec3 () ()
paquete multianual;
56
importación java.io. *;
Clases de excepción
Hay también, por supuesto, una serie de excepciones que se arrojaron bajo
diversas circunstancias:
También vamos a añadir una barra de estado que permanece en la pantalla, sin
importar que el panel se muestra. Esto será usado para mostrar información
crítica para el usuario.
Tenga en cuenta que estos ejemplos son sólo eso: ejemplos. Ninguno de los
URL corresponderá a los archivos.
La tarjeta de red
60
El perfil de la tarjeta
Además de las diversas tarjetas, hay un cuadro de diálogo que aparece cuando
el usuario lanza la primera aplicación multiusuario. La caja se muestra en la
Figura 18.18. El usuario que se espera de entrar en su nombre de usuario y
contraseña, luego pulsa el botón Conectar. La contraseña no se muestra como
se escribió.
El cliente de chat applet texto es muy simple. Se trata de una zona de salida
(que puede ser desplazado si es necesario) y un área de entrada. Ellos se
muestran en la Figura 18.19. Cada usuario de texto va precedido de su apodo
si ha configurado una, o por su ID de texto si no lo han hecho.
MyThread hilo;
62
Una variable no pierde de vista el hilo que está ejecutando, y hay una
referencia al mundo que está conectado a LocalEntity y el correspondiente al
usuario del avatar. Hay otras variables de instancia para el nombre del host y el
puerto, así como el nombre de usuario, contraseña y la información pública.
También hay una serie de variables que se utilizan para la interfaz con el
navegador de VRML, pero será un poco más adelante se describen en este
capítulo.
Hay varios elementos de la interfaz de usuario que deben ser definidas. Existe
la tarjeta del panel, así como una serie de controles que se colocan sobre las
diversas tarjetas de crédito:
Lista peopleList;
static final int SCROLLMAX = 50;
Scrollbar visualAcuitySlider, visualHorizonSlider;
NicknameField de texto, nameField, emailField, webpageField;
GeographyField de texto, phoneField, faxField;
PacketsField de texto, connectedField, bannerField, statusField;
También se definen dos vectores: uno para las URL de los avatares que
tenemos a nuestra disposición, y otra para la lista de personas que se firmó en:
Comunicación Inter-Applet
Init () Método
El siguiente paso es la creación de los diversos controles. Esto es tedioso, pero sencillo:
Ahora que la avatarList ha sido creado, podemos leer los parámetros que dan las
descripciones y las URL de los diversos avatares:
avatarList.makeVisible (0);
El siguiente paso es establecer todas las diferentes tarjetas. Esto es tedioso, por lo que no se
moleste lista aquí todo. Nos limitamos a usar el panel de personas como un ejemplo:
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (muteButton, gbc);
peoplePanel.add (muteButton);
gbc.gridy = 2;
gbc.anchor = GridBagConstraints.NORTH;
gbl.setConstraints (queryButton, GBC)
peoplePanel.add (queryButton);
La carrera () método es corto pero interesante. Se aparece el inicio de sesión de diálogo que
se ha descrito anteriormente, entonces se crea la interfaz del navegador y el mundo de
interfaz (es decir, el API para la multi-tecnología). También se inicia un par de hilos, una
para mantener el tiempo de conexión al día y uno para actualizar la lista de usuarios cada
pocos segundos:
Una vez que todo esté configurado, el run () método sólo se encuentra en un bucle que
actualiza las entidades, según sea necesario:
El Método updateEntity
Este método es llamado una vez para cada una de las entidades locales en nuestra base de
datos cada vez que a través del lazo.
try (
rep = new Cosa (navegador,
addAvatars, removeAvatars, url);)
de capturas (InvalidVrmlException ex) (
System.out.println ( "no puede agregar objeto:" + ex);
retorno;
)
de capturas (InvalidEventInException ex) (
System.out.println ( "no puede agregar objeto:" + ex);
retorno;
)
e.setRepresentation (REP);
e.unmarkNewURL ();
)
if (e.hasChanged ()) (
/ / Rep actualización con la localización y orientación
rep.setLocation (e.getLocation ());
rep.setOrientation (e.getOrientation ());
rep.setVisibility (! e.isHidden ());
e.markUnchanged ();
)
)
Si la entidad no tiene ninguna representación, o una nueva URL ha sido fijado por el
sistema multiusuario, es crear algo nuevo y configurarlo como la representación de esta
entidad. Reconocemos que hemos aceptado la nueva URL llamando la entidad
unmarkNewURL () método.
Este se encarga de la conmutación de una tarjeta a otra. El resto de los procesos método
sólo los diversos controles, tales como la "suspensión" en:
Los diferentes campos de texto que se utilizan para configurar el alias del usuario y la
información pública son manejados idéntica:
)
return true;
)
Hay una utilidad que proporciona el método de formato para la lista de usuarios:
El Método worldSetup
try (
73
if (mundo! = null) (
/ / Establecer el banner
bandera = world.getBanner ();
if (bandera == null)
bandera = "";
bannerField.setText (bandera);
/ / Crear nuestro avatar
try (world.identity (nombre de usuario, contraseña);)
catch (IOException ex) (
System.out.println ( "no podía IDENT:" ex);
)
de capturas (BadIdentityException ex) (
System.out.println ( "no podía IDENT:" ex);
)
try (
/ / Usamos el nombre de usuario como CNAME
/ / Para el avatar del usuario:
myEntity = new LocalEntity (Mundo, el nombre de usuario);
/ / También lo utilizan como apodo inicial:
myEntity.setNickName (nombre de usuario);
/ / Usar un avatar por defecto:
myEntity.setURL ((String) avatars.elementAt (0));
myEntity.updateRegistry ();
)
de capturas (PermissionDeniedException ex) (
System.out.println ( "no podía crear avatar:" ex);
)
catch (IOException ex) (
System.out.println ( "no podía crear avatar:" ex);
)
)
)
Lo último que worldSetup () hace es crear un avatar para el usuario. El nombre de usuario y
la contraseña de inicio de sesión de diálogo se utilizan para identificar al usuario en el
registro de acogida, el LocalEntity se crea utilizando el nombre de usuario como nombre
canónico. El nombre de usuario también se utiliza como el nombre inicial, aunque el
usuario puede cambiar esto en cualquier momento desde la tarjeta de perfil. El primer
74
avatar en la lista es seleccionado como el avatar por defecto, aunque el usuario también
puede cambiar esta opción más adelante si así lo desean (Avatar de la tarjeta).
Estos son sólo los cuadros de diálogo estándar. QueryDialog es usado para mostrar la
información detallada sobre un usuario que se produce cuando el botón de la Consulta
Popular se hace clic en la tarjeta. LoginDialog símbolo se utiliza para el usuario por su
nombre de usuario y contraseña cuando se inicia por primera vez la aplicación de:
gbc.gridy = 2;
etiqueta = new Etiqueta (info [2] == null? "<desconocido>": info [2]);
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 3;
etiqueta = new Etiqueta (info [3] == null? "<desconocido>": info [3]);
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 4;
etiqueta = new Etiqueta (info [4] == null? "<desconocido>": info [4]);
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridy = 5;
etiqueta = new Etiqueta (info [5] == null? "<desconocido>": info [5]);
gbl.setConstraints (etiqueta, gbc); añadir (etiqueta);
gbc.gridx = 0; gbc.gridy = 6;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.CENTER;
gbc.insets = new Insets (10, 10, 10, 10);
gbc.gridy = 6;
gbl.setConstraints (dismissButton, gbc); añadir (dismissButton);
pack ();
show ();
)
El Hilo TimeUpdater
El hilo TimeUpdater dice la hora actual, resta de ella el momento en que el cliente
conectado al Registro de acogida y de filtro de acogida, y convierte el resultado de horas y
minutos:
MultiUserClient cliente;
while (true) (
largo secondsConnected =
(System.currentTimeMillis ()
- Client.world.getConnectedTime ()) / 1000;
largo minutesConnected = secondsConnected/60;
largas horas minutesConnected = / 60;
largo minutesConnected minutos = 60%;
client.connectedField.setText (
horas + ":" + ((minutos <10)? "0": "")
+ Minutos);
client.packetsField.setText (
String.valueOf (client.world.getPacketCount ()));
intenta dormir (1000); de capturas (InterruptedException ex) ()
)
)
)
El Hilo PeopleReader
MultiUserClient cliente;
demás niños de la raíz de nodo se utiliza para contener las entidades que se
incorporen al mundo, algo así como una "Zona" nodo de Vida Mundos.
El browserSetup () Método
)
de capturas (InvalidNodeException e) (
System.out.println ( "No se pudo encontrar el nombre de nodo \" Root \
"");
retorno;
)
)
de capturas (InvalidEventInException e) (
System.out.println (
"No es posible acceder en eventIn nodo ObjGroup:"
+ E);
)
y se rinda.
nueva rotación (
valores [0], valores [1],
valores [2], valores [3]));
try (myEntity.sendUpdate ();)
catch (IOException e) ()
)
)
)
)
El getBrowser () Método
)
retorno del navegador;
)
Tenga en cuenta que este código asume que el primer objeto incrustado
(incrusta. GetSlot (0)) es el navegador de VRML. Tratamos de hasta cinco
segundos para encontrar el navegador incrustado.
La cosa de clase
Una cosa es una representación de una entidad en un mundo VRML. Cada Cosa
en VRML está representada por algo como lo siguiente:
Transformar (
los niños [
Switch (
whichChoice 0
elección [
... Las formas o inlines ...
]
)
]
)
InputField de texto;
Textarea outputArea;
TextReceiver textReceiver;
TextSender textSender;
);
información de retorno;
)
Init () Método
El inicio () Método
Una pequeña utilidad es de rutina, siempre que simplemente agrega una línea
de texto a la outputArea:
La acción () Método
return false;
)
)
No hay necesidad de hacerse eco del texto del usuario a la outputArea; esto se
hará por el filtro de acogida, que reenvía el mensaje a todos los clientes,
incluyendo el que lo envió. Hay un beneficio adicional para hacer esto: Se le da
al usuario una indicación de lo lento o rápido es la red.
El Hilo ReceiveText
El hilo que recibe mensajes de texto va por separado del cuerpo principal de la
TextClient applet. Tiene su sede en un bucle, a la espera de entrar en los
mensajes de la getMessage () TextReceiver método de la clase. Cuando un
mensaje llega, el texto se extrae de ID y espera que en el mundo local de la
base de datos de entidades. Si es encontrado, el apodo por objeto que se
recupera y se utiliza como el nombre de usuario para el mensaje de texto, de lo
contrario, el texto se utiliza en lugar ID:
)
if (nombre == null)
name = "entidad #" + TextID;
client.addLine (nombre + ":" + msg.getText ());
)
)
)
Juntando Todo
<body>
<embed src="empty.wrl" border=1 height=300 width=500>
</ applet>
Soporte para el filtrado de las regiones sería una característica muy útil para
agregar. El espacio particionado código presentado en el capítulo 21, que
podría integrarse con el cliente, y la lista de regiones visibles pueden ser
enviados al filtro de acogida.
Los paquetes más pequeños
Hay espacio para optimizar el uso del ancho de banda de red disponible. Por
ejemplo, el 12-byte encima de la cabecera RTP es bastante derroche, un mejor
diseño podría ser la de utilizar un formato más pequeño de paquetes entre el
cliente y el filtro de acogida y de convertir a partir de RTP y en el filtro de
acogida en el envío y recepción de paquetes más de la red multicast. La
entidad de 32-bit ID podría ser asignada a un solo byte, ya que es poco
probable que el cliente desea manejar más de 256 entidades simultánea. La
posición y la información de la rotación podría probablemente ser comprimido
y, por ejemplo, la distancia puede ser expresado como 16-bits de
compensaciones fijas "hitos".
La Vida Mundos propuesta aún está en evolución, pero una vez que se
estabiliza debemos ser capaces de añadir soporte para que esta multi-cliente.
El servidor de código, por supuesto, no deberían tener que cambiar en
absoluto.
Este sistema ha sido diseñado desde cero para apoyar diversos tipos de
streaming de datos, incluyendo sonido. Obviamente, el procesamiento de audio
en tiempo real es poco práctico en Java por razones de rendimiento. Sin
embargo, utilizando métodos nativos de Java con una envoltura alrededor de
ellas haría posible que un applet de audio para hablar con el multi-cliente.
Una vez que esto es posible, la aplicación de audio puede recoger el audio de
su SSRC paquetes RTP y buscar SSRC que en la entidad local de base de datos
exactamente como el texto de chat applet no se ve hasta cuando las entidades
a buscar sus apodos. Habida cuenta de la entidad de referencia, la aplicación
de audio puede encontrar la ubicación y orientación de la fuente de sonido a
94
En el siguiente capítulo, Justin nos mostrará lo que ocurre con todos nuestros
mensajes una vez que lleguen al servidor. Él nos mostrará cómo el filtro de
acogida y de registro de acogida y cómo se aplican los mecanismos de filtrado
de trabajo.