Jun
29

En los últimos artículos sobre las Macros en C/C++ hemos explicado de una forma un tanto rápida todo el potencial que presenta esta herramienta si sabemos utilizarla con un nivel medio-alto en determinadas situaciones.

Uno de los problemas que se nos presentan en ocasiones a la hora de trabajar en un lenguaje como C es que, a pesar de que contamos con depuradores de código, no siempre tenemos uno a mano, por lo que si a la vez que documentamos podemos mostrar a nuestro capricho y en función de la situación un log de lo que está sucediendo durante su ejecución, podremos hacernos una idea bastante aproximada de si todo va bien o no.

Tras esta pequeña reflexión me lanzo a compartir con vosotros un pequeño fichero .h que nos facilitará enormemente la tarea en una 20 líneas y que he utilizado ya en el desarrollo de varios programas:


#include <stdio.h>
//#define DEBUG 0
#define DEBUG 1
#ifndef __LOG_H
#define __LOG_H
 #if DEBUG == 1
 #define log(format, args...) 
		fprintf (stderr, "%s - %i: ", __FILE__, __LINE__);
		fprintf (stderr, format "n", ##args);
 #define warning(format, args...) 
			log("/!\ "format" /!\", ##args)
#define error(format, args...) 
		log("(X) "format" (X)", ##args)
 #else
#define log(format, args...) ;
#define warning(format, args...) ;
#define error(format, args...) ;
 #endif
#endif

Este fichero tendremos que incluirlo en nuestro programa escrito en C mediante una directiva #include:

#include "log.h"

en este caso he llamado al fichero que contiene esas 20 líneas de código.

De manera que si tenemos declarado DEBUG con un valor de 0 no se mostrará salida alguna y no sólo eso, sino que las líneas desaparecerán (es un claro ejemplo de uso de macros, ya que en nuestro programa en producción puede que deseemos tener más velocidad de procesado y no nos interese interactuar con salidas ni llamar a funciones que no hagan nada), mientras que si tiene un valor de 1, escupirá toda la información que deseemos por, en este caso, la salida de error de nuestro programa.

Si ejecutamos el siguiente programa en C:


#include "log.h"
int main(int argc, char** argv){

log("asdf%i", 1)

log("asdf")

warning("esto es una alerta")

warning("esto es una alerta%i", 1)

error("esto es un error")

error("esto es un error%i", 1)

}

obtendremos la siguiente salida:

bottle.c – 4: asdf1

bottle.c – 5: asdf

bottle.c – 6: /! esto es una alerta /!

bottle.c – 7: /! esto es una alerta1 /!

bottle.c – 8: (X) esto es un error (X)

bottle.c – 9: (X) esto es un error1 (X)

La ausencia de puntos y coma después de llamar las macros no es un error, es que simplemente no hacen falta.

Finalmente y a modo aclaratorio, las constantes utilizadas en el fichero log.h __FILE__ y __LINE__ son dos directivas de preprocesador que nos indican en qué lína y fichero de código tenemos la llamada a ese log.

Categoría C/C++, Producción propia, Programación | 1 Comentario »
Jun
22

Como último artículo de la serie, vamos a adentrarnos un poco más en la declaración de elementos similares a las funciones con macros , para ser más exactos, con sus argumentos y posibilidades:

Convertir a String

En ocasiones no es de un gran interés tratar de la misma manera todos los argumentos que le pasamos a este tipo de funciones de preprocesador.

En el caso concreto de que deseemos tratar el argumento como un String constante, habremos de precederlo con el símbolo ‘#‘, demnaera que estamos convirtiendo en constantes dichos parámetros, pero veamos un ejemplo que he sacado de gnu.org, ya que me parece bastante ilustrativo.

#define WARN_IF(EXP) 
     do { if (EXP) 
             fprintf (stderr, "Warning: " #EXP "n"); } 
     while (0)
     WARN_IF (x == 0);

Este pedazo de código nos dará una salida como la siguiente:

do { if (x == 0) fprintf (stderr, “Warning: ” “x == 0″ “n”); } while (0);

Es decir, el argumento EXP se sustituye como argumento en el primer caso y como un string en el segundo.

Una de las grandes ventajas de la conversión a String de este tipo de macros, es que no sólo añade dobles comillas, sino que escapa los caracteres necesarios para que la cadena se muestre tal y como es, a no ser que esta se encuentre dentro de una cadena de texto ya o sea un carácter como ‘n‘.

Finalmente puede resultarnos de utilidad concatenar un par de tokens dados dentro de una macro; para ello podremos hacer uso de ‘##

Parámetros indefinidos

Algo que puede resultarnos muy interesante e incluso increíble cuando empezamos a utilizar C, es que, si bien nos han enseñado a que las funciones tienen un número definido de parámetros, ¿cómo es posible la existencia de funciones como printf?

Al igual que para las funciones, existe una forma de hacer que una macro acepte un número no definido de parámetros, y este método aparece en forma de __VA_ARGS__. Este token nos permitirá sustotuir en nuestro macro los ‘‘ por los valores que se le han pasado a la función.

#define log(...) printf(__VA_ARGS__)

Si queremos ser más descriptivos con los parámetros podemos sustituir ‘‘ por ‘<nombre>…‘, por ejemplo:

#define log(args...) printf(args)

Si queremos especificar un número mínimo de argumentos, podemos hacerlo de la siguiente manera:

#define log(formato, args...) printf(formato, args)

pero esto hace que sea obligatorio, en todo momento, si no pasar un segundo argumento, sí escribir la coma después del primero. Para solucionar este problema, de nuevo acudimos al manual de gnu.org que nos dice que echemos mano de ‘##‘, de manera que podamos omitir, si lo deseamos el segundo parámetro y la coma de después del primero al llamar a la macro:

#define log(formato, args...) printf(formato, ##args)
Categoría C/C++, Manuales | Sé el primero en comentar!
Jun
15

En el pasado y primer artículo de esta serie, describimos a grosso modo el funcionamiento de la directiva de preprocesador #define; siendo una de las características más importantes a tener en cuenta el hecho de que a la hora de compilar el programa no estamos trabajando con variables con un valor, sino con los valores directamente. De este modo estaremos ahorrando memoria y dotando de legibilidad a nuestro código en determinadas ocasiones.

Hoy vamos a ver cómo simular funciones con el sistema de Macros. Antes de nada, hay que aclarar que: si bien para constantes, por convenio, se utilizan letras mayúsculas en todo momento, para esta especie de funciones no es necesario hacer esto, de manera que podemos continuar con el sistema de nomenclaturas que hemos estado llevando hasta el momento (como recomendación, yo usaría todo en minúsculas menos la primera letra de cada palabra exceptuando la de la primera, Ex: hallarMaximo(…)).

Supongamos que contamos con dos funciones en nuestras librerías:

int rand(void);
int fastRand(void);

Ambas funciones llevan a cabo la misma tarea, sólo que utilizan diferentes métodos para realizarla, de hecho, la segunda que es mucho más eficiente que la primera (como puede entenderse por el nombre), la acabamos de implementar y, a pesar de que la entrada y la salida es la misma, la primera ha sido utilizada cientos de veces en nuestro código, y cambiarla implicaría un repaso exhaustivo del código fuente y, en caso de tener una API pública, implicaría cambiarla y notificárselo a todos nuestros usuarios.

En lugar de esto, podemos crear una macro que se encargue de realizar dicha sustitución durante el proceso de precompilado:

#define rand() fastRand()

de manera que, cada vez que llamemos a la función rand() en nuestro código, a la hora de compilarlo, es como si estuviésemos llamando a la función fastRand().

Pero el poder de esta herramienta no se para con estas características, sino que nos permite definir algo parecido a funciones de forma completa. Con esto quiero decir que podemos definir macros que a la hora de editar el código y utilizarlos, podemos asumir que son funciones. Pongamos un ejemplo:

#define max(x, y) ((x > y) ? x : y)

una vez hehcha esta definición, nosotros, desde nuestro código podremos llamar a esta “función” evitando que se gasten, entre otras cosas niveles de pila, ya que el código es sustituido durante el proceso de preprocesado, de manera que si escribimos en nuestro código:

int a = 2;
int b = 4;
int z = max(a, b);

el código que realmente se estará ejecutando una vez compilado será:

int a = 2;
int b = 4;
int z = ((a > b) ? a : b);

Uno de los grandes problemas de utilizar esta técnica para sustituir a las funciones es que, como podemos observar es que, a la hora de llamar a la macro, no tenemos comprobación del tipado de los datos que le pasamos, pudiendo obtener así, resultados inesperados o incluso errores de estabilidad del programa. Hay que ser conscientes en todo momento de que los argumentos, a la hora de llamar a una macro que se comporta por una función, estarán separados por comas.

Finalmente, a la hora de llamar a una macro, si no le pasamos el número de argumentos necesarios, obtendremos un error de preprocesador, pero en caso de que le pasemos argumentos vacíos, estos se sustiuirán por argumentos vacíos sin dar error durante el proceso de preprocesado.

Categoría C/C++, Manuales | Sé el primero en comentar!
Jun
1

Todo programador de C y C++ que se precie debe estar más o menos familiarizado con las directivas de preprocesador en general y mas concretamente, debería estarlo con las llamadas Macros. Una macro es un trozo de código, usualmente en la zona de cabecera o en los propios ficheros de cabecera (*.h).

Las macros se definen mediante la directiva #define; veamos un ejemplo:

#define PI 3.14159

Esta macro se encarga de asociar el valor 3.14159 con el valor PI mediante la directiva #define, es decir, la estructura sería algo así como lo siguiente:

#define <alias> <valor>

dónde <alias> es el nombre que queremos utilizar para referirnos al término que ocuparía el lugar de <valor>. Ésta característica de C se utiliza sobre todo para facilitar el uso de variables que durante todo el programa van a tener el mismo valor, ya sea por el hecho de que son constantes, como por ejemplo el valor del número pi (3.14159), el valor de la gravedad terrestre g (9.81) o cualquier otra constante o porque simplmente son eso, constantes que no variarán, como por ejemplo el tamaño máximo de un array.

Un aspecto relativamente importante de las macros es que, por convenio, y para facilitar la lectura de las mismas en programas, se suelen utilizar letras mayúsclas, de manera que si nos encontramos con:

int main(int argc, char** argv){

printf("%f, %f", r, PI);

return 0;

}

podríamos concluir que la letra r se corresponde con una variable, ya que está está (o empieza) con minúsculas, mientras que PI es un valor constante. Ambos valores han de estar definidos con anterioridad.

Veamos un ejemplo práctico de uso:


#include <stdio.h>

#define PI 3.14159

int main(int argc, char** argv){

double radio = 25.684;

double area = PI * radio * radio;

return 0;

}

La directiva #define no sólo nos servirá para definir valores de variables, sino que también podremos utilizarlo para declarar valores de arrays:

#define CONTENIDO 0, 
                     1, 
                     2
int main(int argc, char** argv){
int valores[] = { CONTENIDO };
return 0;
}

Como habréis podido comprobar detrás de las dos primeras líneas de código aparece una barra inclinada (), esto quiere decir que la declaración de la macro va a continuar en la línea siguiente, lo cual nos facilitará enormemente la legibilidad de nuestro código.

¿Por qué nos convendría usar macros?

El uso de macros puede parecer un poco absurdo de buenas a primeras, ya que podemos utilizar funciones (ya veremos más adelante el porqué de esta afirmación) o variables para conseguir los mismos resultados. La diferencia entre usar unas u otras es que al usar macros, el código del <alias> se sustituye por el del <valor> durante el proceso de pre-compilación, de manera que a la hora de compilarlo, el código queda sustituido y al ejecutarlo no tendremos que malgastar ciclos de CPU realizando cálculos como este.

¿Podemos invalidar algo declarado con un #define?

Efectivamente podemos hacerlo mediante el uso de la directiva de preprocesador:

#undef <alias>
Categoría C/C++ | Sé el primero en comentar!
Jun
20

Con este artículo termina la serie de artículos en las que describimos los principios básicos que hay que tener en cuenta para realizar un chat y en los que también se describe como hacer una pequeña aproximación a uno de estos programas que a muchos de nosotros nos acompañan en nuestro día a día.

En el artículo de hoy vamos a tratar el único tema que nos falta, el cual es el programa cliente que nos servirá a los usuarios para comunicarnos con el servidor y así, poder comunicarnos con el resto de usuarios que se encuentren conectados en un momento dado.

Si bien el cliente se basta de un sólo ejecutable, hoy vamos a tratar dicho ejecutable y otro que nos permita, mediante la redirección de descriptores de ficheros, añadirle una interfaz gráfica sencilla.

cli.c

Nos encontramos ante uno de los ficheros con la lógica esencial del cliente del chat, vamos a analizarlo paso a paso:


#include <stdio.h>
#include <stdlib.h>
// librería para manejo de strings
#include <string.h>
// librería para el uso de los sockets y las correspondientes constantes
#include <sys/socket.h>
// librería para el uso de la constante IPPROTO_TCP, in_addr, ...
#include <netinet/in.h>
// librería que nos permite hacer uso de la variable errno
#include <errno.h>
// librería de base de datos de red
#include <netdb.h>
// librería para el uso de primitivas unix
#include <unistd.h>
// librería para obtener acceso a variables como pid
#include <sys/types.h>
// librería para el manejo de señales
#include <signal.h>
// librería para mostrar la traza del programa
#include "trace.h"
// librería para gestionar los paquetes enviados y recibidos
#include "sms.h"
// librería con las flags que utilizaremos
#include "flags.h"
// incuimos la librería de tipos de datos
#include "type.h"
// incuimos la librería de herramientas para los echo
#include "tools.h"
// incluimos las librerías que nos permitirán hacer uso de SSL
#include "ssl.h"

#define DIM 1024 // definimos el tamaño de los array de textos

#define     READ    STDIN_FILENO
#define     WRITE    STDOUT_FILENO

Incluimos los archivos de cabecera o librerías necesarias para la ejecución del chat y realizamos las definiciones de tamaños, y ya puestos, definimos los descriptores de ficheros de entrada y salida con el nombre de las constantes genéricas en lugar de utilizar los números 0 para lectura y 1 para escritura.

Continue reading “Crea tu propio chat – Cliente” »

Categoría C/C++, Java, Linux, Producción propia, Programación | Sé el primero en comentar!
Jun
19

Hasta ahora hemos estado hablando de funcionalidades genéricas del chat, requisitos, …pues bien hoy vamos a analizar todos los ficheros de los que se compone el servidor y no han sido comentados con anterioridad; al igual que en el artículo anterior, vamos a comentar el cometido de cada una de las funciones de manera genérica, y únicamente nos adentraremos en el contenido de estas en la función de manejo de los sockets y en las que tengan alguna curiosidad que comentar.

Database.c

Éste es un archivo de código fuente que sólo será incluido en el programa del servidor (en realidad también va a ser incluido en el programa que nos ayudará a crear la base de datos desde cero, pero la importancia de ese ejecutable es mínima y la comentaremos más adelante.

En este caso hemos decidido hacer uso del sistema de bases de dados SQLite dado que es una base de datos ligera portable y con características más que suficientes para utilizarla en nuestra pequeña aplicación.

sqlite3* db_open(char*);

El cometido de esta función es simple, simplemente abrirá la base de datos situada en el fichero que se le ha de pasar como argumento.

int db_prepare(sqlite3**);

Esta función no se utilizará en el servidor, pero será la que utilizará la aplicación que creará la base de datos en caso de que esta no exista, y su única función es la de crear las tablas necesarias en la base de datos así como el usuario administrador del chat.

int db_exec(sqlite3**, char*, int);

Esta función se encargará de ejecutar una sentencia SQL y de actuar en consecuencia dependiendo del flag que se le pase como último argumento.

int db_userExists(char*, sqlite3**);

Esta función comprueba si un usuario existe en la base de datos.

int db_checkUser(char*, char*, sqlite3**);

Esta función comprueba si un usuario asociado a una contraseña existe en la base de datos.

int db_addUser(char*, char*, int, sqlite3**);

Crea un nuevo usuario.

int db_deleteUser(char*, sqlite3**);

Elimina un usuario de la base de datos.

int db_listUser(int, sqlite3**);

Lista los usuarios en la base de datos

void db_close(sqlite3**);

Cierra la base de datos y todos los descriptores asociados a ella.

int db_addLog(sms, sqlite3**);

Añade a la tabla de logs un mensaje.

int db_getLog(int, sqlite3**);

Lista el contenido del log de la base de datos

int db_getLogPar(int, sqlite3**, char*);

Tiene un comportamiento similar al de la función anterior, sólo que esta admite parámetros para personalizar la búsqueda, como por ejemplo buscar entre dos fechas dadas.

static int callback(void*, int, char**, char**);

Función que será llamada para cada una de las filas obtenidas de una sentencia SELECT. Continue reading “Crea tu propio chat – Servidor” »

Categoría C/C++, Java, Linux, Producción propia, Programación | Sé el primero en comentar!
Jun
18

Llevamos unos días comentando las características que ha de tener un chat y de cuales son las estructuras básicas en las que podríamos basarnos para crear nuestro propio chat, pues bien, en este artículo vamos a comentar las funciones auxiliares de las que tendremos que hacer uso en un momento u otro en un chat que cumpla una serie de mínimos.

A diferencia de lo que hemos hecho en el artículo anterior, en este, no vamos a comentar el código a menos que se vea necesario y que sea difícil de entender sin una mínima explicación, sino que vamos a analizar las funciones de cada uno de los ficheros de código fuente de funciones auxiliares.

type.c

En este archivo nos encontramos con una serie de funciones que nos permitirán tratar fácilmente tipos de datos como enteros, strings,… quizá el nombre del archivo no sea el más adecuado, pero es lo que hay.

int isNum(char[]);

Ésta es una de las funciones más sencillas y a la vez más útiles de las que podemos encontrarnos, y si bien es bastante probable que la implementación no sea ninguna maravilla, funciona, que para el caso es lo que nos importa, y su cometido principal es determinar si el String que se le pasa como argumento es un número entero o no lo es.

char* ltrim(char*);

char* rtrim(char*);

char* trim(char*);

Estas tres funciones las vamos a comentar juntas, ya que su cometido es muy similar, y es que la primera elimina los espacios en blanco sobrantes a la izquierda del array de caracteres que se le pasa por argumento, la segunda hace lo propio pero por la derecha, y la tercera función llama a las dos anteriormente definidas para, eliminar los espacios en blanco sobrantes a ambos lados de la cadena que se le pasa como argumento.

int addslahses(char*, int, char*);

Esta función es una burda imitación del addslashes de PHP y otros lenguajes para escapar una serie de caracteres antes de almacenar los datos en la base de datos. En este caso concreto la función sólo escapa el carácter de “comilla simple” (). Es una función que debería ser mejorada.

void md5_sum(unsigned char*, int, int, char*);

Por último en este archivo tenemos una función muy interesante, y es que es una función que dado un array de caracteres, es capaz de calcular su suma MD5, algo muy útil para comprobar que los datos transmitidos no han sido corrompidos o, simplemente para crear un sencillo sistema de cifrado de contraseñas en la base de datos y que no puede ser descifrado.

tools.c

En este archivo se definen una serie de funciones que nos servirán para tratar con la consola o bash desde la que ejecutamos nuestro programa.

void echo_on(void);

Esta función habilita el echo por pantalla de todo aquello que tecleemos en la pantalla.

void echo_off(void);

A la inversa que la función descrita anteriormente, ésta se encarga de hacer que lo que se teclee en la bash justo después de haber sido llamada, no se haga echo por pantalla.

void make_daemon(void);

Por último tenemos una función bastante útil y que nos va a permitir desligar el programa en ejecución de la consola y hacer que se ejecute como un demonio según el concepto Unix.

Recuerda que si quieres echarle un vistazo al código completo del chat (tanto cliente como servidor), puedes dirigirte al primer artículo de la serie de artículos que tratan el tema del chat.

Categoría C/C++, Java, Linux, Producción propia, Programación | 1 Comentario »
Jun
17

Para continuar con la serie de artículos de como crear un pequeño chat, vamos a analizar ahora los archivos de cabecera que no están, por unas razones u otras, asociados con ningún archivo *.c; para cada uno de ellos analizaremos el porqué de su existencia y demás temas.

Dado que en el artículo anterior hemos explicado como hacer que un archivo de cabecera sólo se añada una vez en nuestro proyecto (evitando posibles errores de redefiniciones, …), en este artículo vamos a obviar esas partes en la explicación de  los archivos de código fuente.

Código – flags.h:

Éste es un fichero en el que no hay código, no tienen nada especial para explicar, lo único digno de mención de este fichero es que, gracias a él, seremos capaces de identificar fácilmente los tipos de mensajes, clientes,…

Código – socket.h:

Este archivo sí que tiene un poco más de “chicha” como quien dice, en este archivo de cabecera se describen, además de una serie de constantes para identificar el tipo de usuario, el tamaño de algunos campos y variables, se definen una serie de nuevos tipos de datos, el tipo de datos user y el tipo de datos room.


typedef struct usr{
 char name[NAME_LEN];    // nombre del usuario asociado al socket
 int sock;               // información referente al socket de conexión
 SSL *ssl;
 int prov;               // Flag que indica si es provisional o no la conexión
 int rol;                // flag que nos indicará el rol que le hemos asignado
 int room;               // número de la sala a la que está conectado
 }user;

En esta estructura almacenaremos el nombre del usuario asociado a un determinado socket, el socket en cuestión, un objeto de dato que nos permitirá hacer uso de métodos para la comunicación mediante SSL, el indicador de si el usuario se ha identificado, el rol que éste tiene y en qué sala de chat se encuentra.


typedef struct r{
 char name[DIM];
 }room;

Nos encontramos ante un nuevo, pequeño y extensible tipo de datos, y es que gracias a él podemos mantener un registro de las salas de chat activas.

Cabe mencionar que si bien podría ser interesante haber declarado el objeto directamente con un

typedef char name[DIM] room;

se ha hecho así para permitir posibles extensiones de la estructura.

Código – sms.h:

Por último vamos a analizar el contenido de sms.h. Este archivo contiene la definición de una de la estructura que es crucial para el desarrollo del chat, y es que es la estructura que se va utilizar para transmitir entre el cliente y el servidor y viceversa los mensajes.


typedef struct message{
 char text[SMS_LEN];     //mensaje
 time_t time;            //hora del mensaje
 char name[NAME_LEN];    //nombre del que envía el mensaje
 char to[NAME_LEN];       // nombre del destinatario
 int flag;               // flags para comunicar acciones a reaizar
 }sms;

Como puede verse fácilmente, en esta estructura se envía el contenido del mensaje, la fecha y hora en la que se envía el mensaje, el nombre del usuario que envía el mensaje, el nombre del destinatario del mensaje, en caso de que este sea un mensaje privado y un campo flag con el que seremos capaces de indicar que tipo de mensaje es el que se está transmitiendo, esto nos servirá por ejemplo para utilizar la misma estructura para hablar con varios usuarios, un único usuario o incluso directamente con el servidor.

Recuerda que si quieres echarle un vistazo al código completo del chat (tanto cliente como servidor), puedes dirigirte al primer artículo de la serie de artículos que tratan el tema del chat.

Categoría C/C++, Java, Linux, Producción propia, Programación | Sé el primero en comentar!
Jun
16

Como hemos comenzado a hacer en el artículo anterior, vamos a continuar comentando los ficheros que lo componen uno a uno, en este artículo vamos a comentar el fichero trace.h, éste es un archivo de cabecera que define una serie de macros muy simples que nos permitirán, en tiempo de compilación determinar cuál será el comportamiento de log en cada momento.

En este caso la macro definida tiene tres posibles comportamientos:

  • No hacer nada.
  • Imprimir por la salida estándar el mensaje especificado.
  • Imprimir en el mensaje en cuestión en el log del sistema para poder tener constancia de qué está pasando en cada momento en el chat

Código – trace.h:


#ifndef __TRACE_H
 #define __TRACE_H

Éste es un archivo de cabecera, por lo que sólo nos interesa incluirlo una vez en nuestro proyecto, para ello comprobamos si una constante ha sido previamente definida, en este caso esa constante se llama __TRACE_H, si esta está definida previamente, no hacemos nada, mientras que si no lo está, procedemos a definirla e incluir los datos de la cabecera.


#ifndef DEBUG
 #define DEBUG 0
 #endif // DEBUG defined

Dado que para determinar el comportamiento de nuestra macro hemos de tener definida la constante DEBUG, comprobamos si está definida, y si no, le damos un valor cualquiera en este caso, un valor para que las macros se sustituyan por un “;“, es decir que tengan un comportamiento nulo.


#if DEBUG == 1
 // librería para el uso de primitivas unix
 #include <unistd.h>

 /**
 * PDEBUG(x)
 * x string
 */

 #define PDEBUG(x) 
 printf("%s", x); 
 sync();

En el caso de que la variable DEBUG valga 1, lo que haremos es imprimir por la salida estándar los mensajes de log, esto es especialmente útil para tener constancia durante el desarrollo de qué es lo que está ocurriendo en cada instante de la ejecución.

La función sync(); se encarga de limpiar los bufferes y escribir el contenido de forma definitiva el contenido en el dispositivo en cuestión. OJO al utilizar esta función, hace que también se sincronice el contenido de los discos duros, lo que puede hacer que simplemente, nos carguemos las cabeceras del mismo.


#elif DEBUG == 2
 #include <syslog.h>

 /**
 * PDEBUG(x)
 * x string
 */
 // cat /var/log/syslog
 #define PDEBUG(x) 
 syslog(LOG_DEBUG, "%s", x);

En el caso de que DEBUG tenga un valor igual a 2,  vamos a imprimir el contenido de la macro en el log del sistema con la función syslog();

 #else
 #define PDEBUG(x) ;
 #endif    // DEBUG == 1
#endif    // __TRACE_H

Y por último definimos el comportamiento por defecto de la macro, que es vaciarlo todo.

Recuerda que si quieres echarle un vistazo al código completo del chat (tanto cliente como servidor), puedes dirigirte al primer artículo de la serie de artículos que tratan el tema del chat.

Categoría C/C++, Java, Linux, Producción propia, Programación | Sé el primero en comentar!
Jun
15

Continuando con la serie de artículos con los que vamos a explicar como hacer un chat muy simple, vamos a comenzar, con este artículo como el primero a comentar el contenido de cada uno de los archivos que hemos visto en el artículo anterior (Construye tu propio chat – Estructura).

En este artículo vamos a comentar el contenido de los ficheros de especificación y compilación del programa:

README:

Archivo en el que se especifica el funcionamiento del chat a grandes rasgos y comentando todas las funcionalidades del mismo.

El objetivo de este programa consite en el desarrollo de un chat mediante el uso de sockets
de internet TCP, por lo que en este caso seremos capaces de comunicarnos mediante
una conexión fiable entre el cliente y el servidor, siendo en este caso el cliente
un cliente múltiple que deberá recibir la información correspondiente a los mensajes
que han enviado el resto de clientes.

MANUAL DE USO Continue reading “Crea tu propio chat – Especificación” »

Categoría C/C++, Java, Linux, Producción propia, Programación | Sé el primero en comentar!