4 La Clase OH323Tut
4.1 Declaración
Todo programa basado en la librería PWLib debería tener una instancia de una clase descendiente de PProcess o alternativamente de PServiceProcess (clase que en sí ya es un descendiente de PProcess). En nuestra aplicación tutorial, esta clase es denominada OH323Tut. Esta clase está declarada en el archivo oh323tut.h (líneas 33-42). El método virtual Main() (línea 40) es el lugar donde ponemos el código que, de otra manera (en un programa en C/C++ que no utilizara PWLib), lo pondríamos en la función main() del programa.
La declaración de OH323Tut es muy simple. Detrás del método Main(), hay un constructor, un destructor, el método printHelp() (declarado en la línea 41) que despliega información sobre el uso del programa, y el macro PCLASSINFO. El objetivo de este macro (línea 35) es el de insertar en tiempo de ejecución (run-time) métodos de tipeo (typing) que son obligatorios para cada miembro de la jerarquía de la clase PWLib. Si estás interesado, el macro está definido en el archivo pwlib/include/ptlib/object.h.
4.2 Implementación
El constructor de OH323Tut está en el archivo oh323tut.cxx en la línea 58. La única tarea de este constructor es la de pasar parámetros al constructor de la clase padre (PProcess). Los parámetros son, en este orden: el autor de la aplicación, el nombre de la aplicación, los números menores y mayores de versión de la aplicación, el código de status (los posibles valores son AlphaCode, BetaCode, y ReleaseCode), y el número de construcción (build). Utilizamos los últimos 4 parámetros utilizando constantes que definimos en el archivo pconf.h (líneas 50-53).
Realizando una búsqueda en el código fuente de oh323tut encontrarás que ninguno de los archivos contiene, explícitamente, la función main(). La función main() es generada por el macro PCREATE_PROCESS (archivo oh323tut.cxx, línea 56). En plataformas Unix, el macro es definido de la siguiente manera:
#define PCREATE_PROCESS(cls) \ int main(int argc, char ** argv, char ** envp) \ { PProcess::PreInitialise(argc, argv, envp); \ static cls instance; \ return instance._main(); \ }
El código insertado por el macro es ligeramente diferente en cada plataforma soportada por PWLib, sin embargo la variante de código en Unix mostrado anteriormente te dará una idea de que está sucediendo realmente en este contexto. La función main() simplemente realiza cierta inicialización, creando una instancia de la clase cls (OH323Tut en nuesto caso) y llamando al método _main() de esta instancia. El método _main() a su vez llama al método virtual Main(). Este macro te ayuda a crear programas multi-plataforma de una manera más simple -- utilizar una sola línea es más elegante que varios #ifdef-s.
Continuamos con la descripción del método Main() de OH323Tut (archivo oh323tut.cxx, líneas 92-168). Pasaremos por alto las líneas 94-97 (retornaremos a ellas luego) y discutiremos el "parsing" de los argumentos de la línea de comando.
En la línea 98, creamos una instancia de la clase PConfigArgs:
PConfigArgs args(GetArguments());
Este es un "dialecto" de programación utilizado en varios
programas PWLib. GetArguments()
es un método de PProcess que retorna la lista de argumentos
(parámetros) ingresados al programa mediante la línea de comando.
El comando subsiguiente, en la línea 100, le dice al objeto args qué opciones de la línea de comando necesitamos "parsear". Cada opción es ingresada en su formato corto y largo, ambos sin el signo '-' por delante. Así, por ejemplo, la subcadena "h-help." significa que la solicitud de ayuda podría realizarse ya sea por -h otcz_tt1( --help) desde la línea de comando. Las subcadenas que describen las opciones que precisan de una cadena de opción adicional (p.e. -f otcz_tt1(--file) debe ser seguida por una cadena adicional que describe el nombre de un archivo) terminan con ":", p.e. "f-file:". Las subcadenas que describen opciones sin una cadena de opción adicional terminan con un punto, p.e. "h-help.". Nótese que los argumentos 't' y 'o' sólo son incluídos si el programa se compila con la opción de tracing (mensajes de depuración) habilitada (la cual está habilitada por defecto para los ejecutables de tipo opt y debug ).
La única opción obligatoria de la línea de comando es -f (otcz_tt1( bien --file)). Dada esta situación, las líneas 116 - 120 despliegan la ayuda y terminan el programa si la opción 'f' es omitida o si 'h' ('-help') está presente.
Las líneas 122-126 definen el nivel de tracing (mensajes de depuración) y, opcionalmente, el archivo donde se almacenarán estos mensajes. Las líneas 128-156 procesan las opciones individuales de la línea de comando. Utilizamos una instancia de la clase ProgConf (declarada en el archivo pconf.h, líneas 32-47) para almacenar la información obtenida desde la línea de comando.
Las tres opciones 'n', 'g', y 'G' se relacionan con el registro de el extremo con un gatekeeper. Primero configuraremos progConf.gkMode para ProgConf::RegisterWithGatekeeper - este es el comportamiento por defecto en caso de que ninguna de las tres opciones del gatekeeper sean utilizadas (línea 130). El orden en el cual las opciones son procesadas es importante (líneas 132-145). Si el usuario del programa introduce ambas opciones -n y -g/-G, entonces -n no es considerado (override). Tanto 'g' como 'G' asignan a progConf.gkMode el valor de ProgConf::RegisterWithGatekeeper y ambas requieren una cadena de opción adicional la cual es almacenada en progConf.gkAddr o bien progConf.gkId, respectivamente.
La línea 147 almacena el nombre del archivo en progConf.fileName. Nótese que no necesitamos verificar la presencia de la opción 'f' - ya lo hemos hecho en la línea 116. La línea 149 verifica la presencia de la opción 'p'. Si la opción está presente, el puerto de escucha es asignado a progConf.port. La línea 152 realiza una verificación simple de la validez del número de puerto- si el número es cero, cambiamos su valor al valor por defecto.
La línea 155 verifica la presencia de la opción 'u' (alias de usuario). El usuario puede proporcionar varios alias en la línea de comando y el PString retornado por la expresión args.GetOptionString(u'') contendrá cada alias en una línea separada. El método Lines() transforma la cadena a un arreglo (array) de alias individuales (líneas) y asignamos el arreglo a progConf.userAliases.
La línea 159 crea una instancia de la clase MyEndPoint. Si la inicialización del extremo (línea 161) es exitosa, la ejecución del hilo principal (main thread) de la aplicación es bloqueada en la línea 164. El objeto terminationSync es una instancia de la clase PSyncPoint (véase la línea 36). De hecho, PSyncPoint es un semáforo que inicialmente está bloqueado, de tal manera que el hilo (thread) que llama el método Wait() es bloqueado hasta que otro hilo llame al método Signal().
Deseamos que la aplicación se detenga cuando el usuario presionte Ctrl-C. Para lograr esto, utilizamos el objeto terminationSync junto a la manipulación de la señalización en Unix . El manejador de señales es definido en las líneas 38-51 y lo registramos para SIGINIT y SIGTERM en las líneas 94-97. Cuando signalHandler() recibe cualquiera de las dos señales, simplemente llama a terminationSync.Signal(). Este desbloquea al hilo (thread) de ejecución que estaba en espera en la línea 164. Cuando el método Main() termina, todas las variables creadas automáticamente por el método son liberadas de la memoria. Esto incluye al objeto endpoint - su destructor cierra todas las conexiones activas (si es que existe alguna), y realiza todos los procesos necesarios para que endpoint sea finalizado y liberado. Describiremos la clase endpoint en la siguiente sección..
Siguiente: 5 H.323 Endpoint (Extremo)