---------------------------- NUEVO MENSAJE ------------------
Como pareces duro de mollera
un poco de pseudocódigo que te ayude a clarificar ideas... que no es todo tan abstracto...
Te pongo un simple ejemplo de lo que sería un servidor, un cliente y una sala...
El pseudocódigo de un servidor....
Clase Servidor
// Instancias de clases.
TablaHash Clientes Privado ConEventos
TablaHash ClientesOcultos Privado ConEventos // clientes privilegiados, admins
TablaHash Salas Privado ConEventos
Sala SalaRaiz Privado ConEventos
Cliente ClienteRaiz Privado ConEventos
Buleano Existe
Entero = Propiedad Sololectura NumClientes Publico
Numclientes = Clientes.Cantidad
Fin Propiedad
Entero = Propiedad SoloLectura NumclientesOcultos Publico // o admins
NumClientesOcultos = ClientesOcultos.Cantidad
Fin Propiedad
Entero = Propiedad SoloLectura NumSalas Publico
Numsalas = Salas.Cantidad
Fin Propiedad
Buleano = Propiedad SePermiteCrearSalas Oculto //lectura y escritura
// Esta función se invoca para crear el servidor, que luego es público y permite la conexión... crea además, la primera sala y el resto de objetos necesarios.
Funcion Nuevo(x,y,z) Oculta ' parámetros que requiriese para crear el servidor
Clientes = Nueva TablaHash
ClientesOcultos = Nueva TablaHash
Salas = NuevaTablaHash
SalaRaiz= Nueva Sala
ClienteRaiz = Nuevo Cliente(Alias, Invisible, Privilegios)
Salas.Add(SalaRaiz)
Clientes.Add(ClienteRaiz)
ClientesOcultos.Add(ClienteRaiz)
Existe = TRUE
Fin Funcion
// El ciente cuando se conecta al servidor, se añade a la lista de clientes.
// Y también se le añade a la salaRaiz, y se le devuelve una referencia a esa sala.
Sala = Funcion Conectar(Cliente Cli) Publico
Si SalaRaiz.AdmiteClientes = TRUE luego
//podría filtrarse aquí comparando con una lista de clientes/IPs, baneadas, pero quizás no sea muy efectiva...
Si SalaRaiz.Add(cli) = TRUE // solo debería devolver false si ya existe un cliente con el mismo alias.
Devolver SalaRaiz
Fin si
Fin si
Fin Funcion
Sala = Funcion CrearSala(Cliente Cli, String NombreSala, Entero MasUsers)
Si (SePermiteCrearSalas = TRUE) luego
Si (Clientes.Existe(cli) = TRUE ) luego // Todos los clientes, incluso los ocultos, constan en la colección Clientes.
Si (Salas.Existe(NombreSala) = FALSE) luego
Sala = Nueva Sala(Cli, Nombresala, MaxUsers, This)
Salas.Add(sala) //allí se dispara un evento de 'SalaCreada(Sala.nombre)' que le llega a todos los clientes.
Devolver Sala
Fin Si
Fin si
Fin si
// Ó si solo se permite a clientes ocultos (admins).
Si (ClientesOcultos.Existe(cli) = TRUE ) luego ...
....
// Ó si solo se permite a cliente con privilegios
Si (cli.Privilegios.Contiene("CreateSala")) luego
Si (Clientes.Existe(cli) = TRUE ) luego ...
....
Fin si
Fin Funcion
// Otras funciones que tenga el servidor... (especialmente resolver los eventos de las intancias de las clases: Clientes, ClientesOcultos, Salas, SalaRaiz, ClienteRaiz
Fin Clase
En alguna web (por ejemplo), cuando quien sea prende la máquina, ejecuta un programa que crea una instancia única del servidor.
Servidor WebServer = nuevo Servidor(x,y,z)
Y lo pone a la escucha, ahora cuando algún cliente ejecute su programa, ya en su programa reconoce la clase de tipo Servidor, y por tanto espera conectarse y obtener un referencia a la instancia... dejando aparte los detalles de la conexión (que se supone que es algo que dominas), y centrándonos en lo que importa, el cliente hac ela llamada.
Servidor ServerCliente = Lllamada(www... puerto... cliente...)
Si obtienes la referencia dle servidor, el programa cliente típicamente muestra un iconito verde de 'conectado'.
conectado significa que además el servidor conserva una referencia de tí como cliente, y que lo añade a una colección (típicamente una tabla hash).
Por tanto ya estás en la sala 'Raíz', la colección global de clientes conectados al servidor...
Pero el servidor, puede querer gestionar la clección en trozos más pequeños, pongamos que si hay 10.000 clientes conectados al servidor, no sería muy útil todos respondiendo al mismo tiempo en una sola sala.
entonces el servidor puede optar por crear auytomáticamente 'salas' cuando una se satura.
Así dentro d ela clase servidor, podría existir la siguiente funcion:
Sala = funcion CrearSalaAuto( String Nombre, cliente Propietario, Entero NumMaxUsers)
Sala s
Si Salas.Existe(Nombre) = FALSE) luego
s = Nueva sala(Nombre, Propietario, NumMaxUsers)
Salas.Add(s)
DispararEvento SalaCreada(s.Nombre)
Devolver s
Fin si
Fin funcion
Y la razón para crear una sala automática es repartir clientes, orque había chorrocientos... antes de ir a cambiar el código, dejamos como era el código previo, para esa función:
Sala = Funcion Conectar(Cliente Cli) Publico
Si SalaRaiz.AdmiteClientes = TRUE luego
Si SalaRaiz.Add(cli) = TRUE // solo debería devolver false si ya existe un cliente con el mismo alias.
Devolver SalaRaiz
Fin si
Fin si
Fin Funcion
Ahora vamos al código, modificando la función Conectar(), al servidor
Sala = Funcion Conectar(Cliente Cli) Publico
Si (SalaRaiz.AdmiteClientes = TRUE) luego
Si (SalaRaiz.Cantidad < 1500) luego
Si (SalaRaiz.Add(cli) = TRUE) // solo debería devolver false si ya existe un cliente con el mismo alias.
Devolver SalaRaiz
Fin si
Si no
Hacer
String Nombre = Random(Chars(8))
Sala s = CrearSalaAuto(Nombre, Clientes.Item(0) , 1501) //cliente.Item(0), se supone que es el creador del servidor... un admin.
Repetir mientras s.Nombre = "" // el nombre se crea al azar, si ya existe una con ese nombre falla, luego se reintenta. Lo razonable es que haya un ficherito con nombres de salas 'prefedinidas', que puedan tomarse en secuencia, manteniendo un puntero del último índice usado.
// los clientes entre 1000 y 1500 se mueven a la nueva sala recién creada (por saturación de la previa), cuando llegue de nuevo a los 1500, se volverá a crear otra sala con 500 clientes...
// También podría elegirse al azar a 500 clientes, pero esto podría dar lugar a que un cliente en repetidas ocasiones fuera movido a otra sala, Si los más antiguos, son los que permanecen ya saben que no serán movidos cuando haya un exceso de clientes en la sala, solo los más nuevos, y estos a su vez serán los más viejos en la siguiente sala... en definitiva, para el cliente esta simple opción parece menos molesta.
Bucle para k desde 1000 hasta Clientes.Cantidad
Cliente c = Clientes.Item(k)
Clientes.Remove(k)
// Se notifica (sólo) a éste cliente que fue desconectado de la sala
c.SalaDesconectada(SalaRaiz.nombre) //el porgrama cliente borrará el contenido de la ventana, lista de usuarios y mensajes.
s.Add(c) //en el método add se debe dispara un evento de 'usuario añadido', que notifica a todos los clientes de esa sala.
// se notifica (solo) a este cliente que fue conectado a otra sala.
c.SalaConectada(s, s.Nombre) // el programa cliente, toma la lista de clientes de la sala 's' y los lista y ahora los mensajes que esos usuarios publiquen se comparten solo entre los que tienen una referencia a la sala 's'
Fin bucle
Salas.Add(s, s.Nombre) // Añadimos la sala a la colección de salas.
// informamos a todos los clientes suscritos al servidor de que hay una nueva sala disponible.
DispararEvento SalaCreada(s.Nombre)
s.add(Cli) //el nuevo cliente que se acaba de conectar se añade a esta sala. y dispara un evneto que comunica a todios los clientes el alias del cliente recién conectado.
//Cli.SalaConectada(s, s.Nombre) en la devolución de esta función haría lo mismo que en ese método.
Devolver s //el cliente ahora recibe la sala 's', su programa luego de recibirlo, leerá los clientes conectados a ella y losmensajes de todos ellos se escribirán ahora ahí
Fin si
Fin si
Fin Funcion
Date cuenta que omito algunas comprobaciones, para no hacer el pseudocódigo demasiado enfangoso en detalles menores obvios ... es decir el nuevo lciente que se conecta debe verificarse si ya hay uno conectado con ese alias en la sala, y denegarle el acceso en tal caso con un código de error, que significa que 'ese alias ya existe en la sala (incluso mejor si se compara con la lista global.
Un método más propicio, sería no mover los clientes de la colección global (salaRaiz), sino que cuando, tras crear el Servidor se crea la salaRaiz, donde estén todos los clientes y una primera sala, donde se añaden los primeros 1000 clientes, cuando se llegue a 1000, se crea otra sala, y los siguientes se conectan a esa nueva sala... y todos siguen constando en la SALARaiz, para verificar que no hay repetido un alias en todo el servidor, esa sala entonces debería ser privada, nadie debe verla, salvo los admin.
Es decir cuando un cliente se conecta, primero se mira si se puede añadir a la salaRaiz, si no está allí su alias se añade, y luego se le añade a la sala 'actual' , o una al azar si se crean varias nada más generar el servidor, como ya sabemos que fue añadido a la salaraíz, su alias no está repetido, luego se podrá añadir a otra sala sin problemas.
Veamos ahora una función más del servidor... cambiar de sala.
Dado que el cliente tiene una referencia del Servidor, tiene acceso a la colección Salas (en modo lectura)... luego se puede listar cuando un cliente se conecta, pero cuando se crea una sala nueva, se notifica con un evento...(como se vió en la modificación de la función anterior).
Entero = Funcion ListarSalas(Out Listastring() NombreSalas)
Dimensionar NombreSalas(0 a Salas.Cantidad-1)
Bucle para k por cada sala s en Salas
NombreSalas(k) = s.Nombre
Fin bucle
Devolver NombreSalas.Length // ó Salas.Cantidad
Fin funcion
Ahora otra función (del servidor) para cambiar de sala, a petición del Cliente:
Buleano = Funcion CambiarDeSala(String Nombre, Cliente Cli)
Sala s
Si (Salas.Existe(nombre) = TRUE)
Cli.Sala.Desconectar //Esto provoca un evento clienteDesconectado en dicha sala, que informa a todos los clientes d ela sala de la desconexión (solo informa del Alias)
// Sala.Desconectar invocaría internamente a Clientes.Remove(Cli) Dentro de la instancia Sala
s = Salas.GetSala(Nombre) //El servidor tiene público la colección Salas, y esta función está dentor de la clase Servidor...
s.Add(Cli)
Devolver TRUE
Fin si
Fin funcion
Ahora veamos como sería una clase Cliente (no confundir con el programa Cliente, el programa tiene admeás una instancia de esta clase Cliente, que identifica al usuario y le permite hacer ciertas cosas):
Un cliente simplificado:
Tiene un alias, una conexión con el servidor, una función publicar y una referencia
Clase Cliente
Sala Sala ConEventos Oculto
Servidor Conex ConEventos Privado
String Propiedad Alias <---- público
String Propiedad HashId <----- privado, se genera automáticamente cuando se crea la instancia de la clase cliente.
String Propiedad SoloLectura Privilegios Oculto
Buleano Propiedad SoloLectura Oculto Publico
Funcion Nuevo(String Nombre, Out Buleano Ok) Publico // el que se crea con un nuevo cliente genérico.
Alias = Nombre
HashId= CrearHash(Alias, TimeNow, NumRandom(100.000.00, 999.999.999))
Privilegios = DefaultPrivilegios
Conex = ConectarAServidor(Los datos de conexión)
Si Conex.Existe Luego OK = TRUE
Fin Funcion
Funcion Nuevo(String Nombre, Buleano Visible, String Privileg) Privado // el que se crea con un admin, o el cliente creador del servidor que lo arranca y crea salas, etc...
Alias = Nombre
Oculto = Visible
HashId= CrearHash(Alias, TimeNow, NumRandom(100.000.00, 999.999.999))
Privilegios = Privileg
// El cliente que crea esta instancia, tiene otro medios para realizar la conexión al servidor, porque es el que arranca el servidor y crea la NUEVA instancia del mismo.
Fin Funcion
Funcion CambiarPrivilegios(String Privileg) Oculto
Privilegios = Privileg
Sala.VerificarPrivilegios(Privileg)
Fin Funcion
// Evento que salta cuando se desconecta mediante Sala.Desconectar
Funcion Sala_Desconectado(String Nombre)
Borrar lista de clientes (usuarios),
Borrar mensajes de la pizarra (textbox u otro objeto)
//Obtener la lista de salas
ListaString() Nombres = Conex.ListarSalas(NumSalas)
Bucle para k desde 0 a NumSalas-1
ListboxSalas.Add(Nombres.Nombre) // añade a un listbox, la lista de salas
Fin bucle
Fin Funcion
// Este podría ser otro evento de la clase Sala, que se dispara cuando se añade un cliente a la sala
Funcion Sala_ClienteConectado(String Alias)
ListboxUsers.Add(Alias)
Textbox.Add(Alias, " se añadió a la sala", Time.Tostring)
Fin funcion
// Este podría ser otro evento de la clase Sala, que se dispara cuando se desconecta un cliente de la sala
Funcion Sala_ClienteDesconectado(String Alias)
ListboxUsers.Remove(Alias)
Textbox.Add(Alias, " se fue de la sala", Time.Tostring)
Fin funcion
// este podría ser un evento de la instancia del servidor...
Funcion Conex_SalaCreada(Sala s)
ListboxSalas.Add(s.Nombre)
Textbox.Add("Se creó una nueva sala", s.Nombre, Time.Tostring)
Fin funcion
Fin clase
Finalmente un ejemplo de lo que podria ser la clase Sala...
Clase Sala
TablaHash Clientes Oculto
Cliente Propietario Oculto // la idea es que solo el propietario o un admin, pudiera eleiminar la sala.
Servidor Conex
Entero MaxUsers Oculto
String NombreSala Publico
Entero = Propiedad SoloLectura NumClientes Publico
Numclientes = Clientes.Cantidad
Fin Propiedad
Evento ClienteConectado(String Alias)
Evento ClienteDesconectado(String Alias)
Evento NuevoMensaje(String Mensaje, string Alias, String hora, etc...)
// Todos los clientes suscritos a esta instancia recibirán el evento y ninguno más que estos.
Buleano = Funcion Publicar(String Mensaje, String Hora, etc...)
DispararEvento NuevoMensaje(Mensaje, Alias, Hora, etc...)
Fin Funcion
Sala = Funcion Add(Cliente NewCli) Publico
Si (Clientes.Cantidad < MaxUsers) luego
Si (Clientes.Existe(NewCli.Alias, NewCli.HashId) = FALSE) luego
Clientes.Add(NewCli)
DispararEvento ClienteConectado(NewCli.Alias)
Fin si
Fin si
Fin funcion
Buleano = Funcion Remove(Cliente Cli)
Si (Clientes.Existe(Cli.Alias, Cli.HashId) = TRUE) luego
Clientes.Remove(Cli)
DispararEvento ClienteDesconectado(Cli.Alias)
Devolver TRUE
Fin si
Fin funcion
Sala = Funcion Nuevo(Cliente Propietario, String Nombre, Entero NumMaxUsers, Servidor Conexion)
Si (Conexion.Clientes.Existe(Propietario.Alias, Propietario.HashId) = TRUE) luego
Conex = Conexion
MaxUsers = NumMaxUsers
Clientes = Nueva TablaHash(NumMaxUsers)
Clientes.Add(Propietario)
Nombresala = Nombre
Devolver This
Fin si
Fin Funcion
...más funciones, propiedas, etc.. por ejemplo cambiar privilegios en la sala a un cliente...
Fin Clase
Es un código al vuelo, así rápido así que estará lleno de imprecisiones erroes, y diferencias (en un sitio llamo a un evento de una manera y luego en su clase la nombro de otra), es razonable escribiendolo sobre la marcha...
...espero sin embargo que sirva para que te quede claro varias cosas:--- Que una sala, es tan solo una colección de clientes suscritos a una misma instancia, y que por tanto comparten los eventos de esa única instancia entre ellos.
--- Que el servidor mantiene la lista de salas, que es simplemente otra colección de instancias de sala.
--- que el servidor puede crear una sala, o varias, incluso de modo automártico si una sala se satura pongamos al 75% de su capacidad, desplazando (también automáticamente) usuarios a la otra sala recién creada.
--- Que el servidor puede permitir a un cliente crear una sala, bajo demanda (petición). aunque lo normal es que lo creen los admins o usuarios con privilegios.
--- Que los privilegios, aunque no se ha hablado apenas de ellos, son una simple lista de textos, algo como: "CreateSala, MoveToAnySala, ConnecToAnySala, DisconnectToSala, ListUsersInSala, ListSalasInServer, ChangePrivileges, etc..." Incluso un textomás abreviado con mnemónicos si lo prefieres: "cre, mov, con, dis, liu, lis, cha, etc..."
...y bueno si no te enteras, pués ya más no se puede hacer....