Archivo

Archivo para la categoría ‘Comunicación’

C.I. XIV: Arduino 1.0 disponible, Ubuntu Operation NightLight, gobernadores de CPU, Como funciona el Servicio FTP y más

Domingo, 4 de Diciembre de 2011 Gaspar Fernández 2 comentarios

Estos son los enlaces que he visto interesantes durante la semana:

  • Arduino IDE 1.0 disponible: muchos de los que programamos en esta plataforma lo agradecerán. Sobre todo porque en la librería Ethernet han implementado DHCP, lo cual es muy interesante. Aunque el entorno, como IDE, le siguen faltando muchas opciones.
  • Uno de los actos de presentación del Nokia Lumia (Nuevo Teléfono con Windows Phone) era una presentación sobre el edificio en Londres donde trabaja Canonical. Bueno, estos hicieron un contraataque titulado Ubuntu Operation NightLight, no tan grande como podemos esperar, pero ¡ están ahí !
  • Impresiones de Linux Mint 12: Lisa: Tengo ganas de probarlo, mientras, vamos a ver qué nos depara esta nueva versión.
  • Tipos de gobernadores de las CPUs: Es un tema interesante que muchos lo dejamos tal y como está, pero podemos controlarlo.
  • El captcha de Adafruit: Nada, sólo por el Captcha habrá que darle una oportunidad a la tienda. Muy curioso para los que os guste la electrónica. Lo descubrí gracias a @fransolano.
  • ¿Cómo funciona el servicio FTP?. Buena explicación de cómo funciona (varias partes)
  • Notificaciones de tuenti en Linux: Si eres Linuxero y usas tuenti, te interesa ver esto.

Actualizado a 5 / 12 / 2011 : Añadida la mención a @fransolano en el enlace de Adafruit.

Arduino: Usando el serial para comunicarnos con el ordenador

Viernes, 5 de Agosto de 2011 Gaspar Fernández Sin comentarios

Es interesante ver cómo los programas que corren con Arduino pueden hacer uso del puerto serie para comunicarnos con el ordenador. No sólo vale para hacer la programación del chip, sino que podemos intercambiar información, variar el comportamiento del programa… o simplemente tener un canal para depurar nuestro programa mostrando el estado de las variables en cualquier momento.

La configuración es muy sencilla, sólo necesitamos especificar la velocidad del puerto serie cuando queramos iniciar la comunicación. La velocidad más alta que he podido alcanzar es de 230400bps, lo que vienen a ser unos 28Kb/s; aunque, mejor no subir de 115200bps, o unos 14Kb (porque a 230400 la transmisión empieza a ser inestable y a veces se pierden bits).

Lo más normal es querer iniciar la comunicación al principio y cesarla al final del programa por lo que nuestro Serial empezará en el setup. Vamos a hacer un pequeño ejemplo de comunicación a través del USB del Arduino:
serial.h:

1
2
3
4
5
6
/* En este archivo veremos la configuración de nuestro proyecto */

/* Velocidad del puerto serie */
/* #define SERIAL_SPEED 230400 */
/* Es una buena velocidad para el serial del Arduino Diecimila */
#define SERIAL_SPEED 115200

serial1.pde:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// serial1
// Hace un eco con el puerto serie del Arduino
#include "serial.h"

void setup()
{
Serial.begin(SERIAL_SPEED);
}

void loop ()
{
int recibe;
if(Serial.available())
{
recibe=Serial.read();
// Escribimos byte a byte
Serial.print(recibe, BYTE);
}

}

Si compilamos/programamos y ejecutamos un terminal serie, a mí me gusta cutecom, es simple y tiene entorno gráfico. Si nosotros escribimos por la entrada, el programa símplemente repite lo que decimos:
Cutecom

Por otra parte, el uso del Serial no es bloqueante, es decir, podemos realizar otras tareas mientras esperamos que lleguen datos por el puerto. Esto puede ser muy útil ya que nos permite poder modificar el comportamiento del circuito mientras estemos conectados al serial, o la interacción con otros aparatos que también se conecten al puerto serie mientras tenemos tiempo de CPU para otros cálculos.

Podemos comprobarlo aquí:
serial.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* En este archivo veremos la configuración de nuestro proyecto */

/* Velocidad del puerto serie */
/* #define SERIAL_SPEED 230400 */
/* Es una buena velocidad para el serial del Arduino Diecimila */
/* #define SERIAL_SPEED 115200 */
/* Para ver cómo el led de transmisión parpadea, transmitimos más lento */
#define SERIAL_SPEED 1200
/* Led que queremos que parpadee mientras se ejecuta el programa */
#define STATUS_LED   11
/* Led que indica transferencia de datos */
#define TRANSF_LED   10
/* Retardo introducido para ver el estado del led */
#define BLINK_DELAY  500
/* Bytes que tenemos que recibir antes de que el led de transferencia parpadee */
#define BLINK_RECEPTION 50

serial1.pde:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// serial1
// Hace un eco con el puerto serie del Arduino

#include "serial.h"

// Almacenamos el estado como variable global
int estado=LOW;
int estado_transf=LOW;
// Almacenamos también el número de milisegundos anterior
unsigned long momento_anterior=0;
unsigned long bytes_recibidos=0;
void setup()
{
Serial.begin(SERIAL_SPEED);
// Queremos que un led parpadee mientras trabajamos
pinMode(STATUS_LED, OUTPUT);
// Queremos salida por el led de transferencia
pinMode(TRANSF_LED, OUTPUT);
}
<p style="text-align: center;">void loop ()
{
int recibe;
unsigned long momento_actual=millis();
// No bloqueante, si hay algo para leer entramos, si no, no.
if(Serial.available())
{
recibe=Serial.read();
// Escribimos byte a byte
Serial.print(recibe, BYTE);
++bytes_recibidos;
if (bytes_recibidos%BLINK_RECEPTION==0)
{
estado_transf=!estado_transf;
digitalWrite(TRANSF_LED, estado_transf);
}
}
// No usamos delay para el parpadeo porque nos entorpece la comunicación con el serial
if (momento_actual-momento_anterior&gt;=BLINK_DELAY)
{
// Cambiamos el estado siguiente. Si era HIGH (1) ahora será
// LOW (0). He leído en algún lado que el valor de HIGH no
// siempre es 1; pero en este caso sí es así.
estado=!estado;
// Escribimos el estado actual del led
digitalWrite(STATUS_LED, estado);
// Establecemos el momento anterior como actual.
momento_anterior=momento_actual;
}
}

Comunicación por puerto serie y led

Recopilación de soluciones para los retos de #tuentiContest . Challenge #17

Miércoles, 29 de Junio de 2011 Gaspar Fernández Sin comentarios

Últimamente he hablado acerca del I concurso de programación de Tuenti. Un concurso de programación Online que se llevó acabo durante la semana pasada (del 13 al 20 de Junio, muy mala fecha).

Podéis ver los enunciados de todos los problemas, con ejemplos sobre la entrada y salida (aunque a veces no hay que hacerles mucho caso) en la web oficial del concurso, pero en Vidas Concurrentes lo encontramos todo en español.

Challenge #17 : The ¿? Porblem

También conocido como “Just do it.” por las palabras de su enunciado. Lo que no nos decían es que nos mandaban una imagen PNG en base64 por la entrada estándar. Si la abríamos como texto, después de muchos datos binarios podíamos leer un texto: “There is more data in this image than meets the eye“, pensemos ahora en esteganografía, por ahí iban los tiros.
Soluciones:

Si no estás en la lista y quieres plantear tu solución, deja un comentario con tu link !

Actualización 2011/07/03 01:49 : Añadida solución de @frisco82
Actualización 2011/07/03 01:59 : Añadida mi solución (@blakeyed)
Actualización 2011/07/03 13:52 : Añadida solución de @Rosapolis

Recopilación de soluciones para los retos de #tuentiContest . Challenge #2

Miércoles, 22 de Junio de 2011 Gaspar Fernández 2 comentarios

Últimamente he hablado acerca del I concurso de programación de Tuenti. Un concurso de programación Online que se llevó acabo durante la semana pasada (del 13 al 20 de Junio, muy mala fecha).

Podéis ver los enunciados de todos los problemas, con ejemplos sobre la entrada y salida (aunque a veces no hay que haerles mucho caso) en la web oficial del concurso, pero en Vidas Concurrentes lo encontramos todo en español.

Challenge #2 : TLang

Se trata de implementar un parser que suma, reste y multiplique, con los símbolos ^= ,^# 2 2 y ^@, eso sí, el final de la operación siempre lo marcará un $. Por otra parte, hay símbolos, como ^@ (resta) que pueden ir sólo con un número, en lugar de con varios. Debemos leer de la entrada las operaciones a realizar y devolver el número de salida.

Soluciones:

Si no estás en la lista y quieres plantear tu solución, deja un comentario con tu link !

Actualización: 2011/06/22 13:54 : Error tipográfico
Actualización: 2011/06/22 20:00 : Añadida solución de @Puigcerber
Actualización: 2011/06/28 07:58 : Añadida solución de @theom3ga
Actualización: 2011/07/02 22:30 : Añadida solución de @frisco82
Actualización: 2011/07/03 13:30 : Añadida solución de @Rosapolis
Actualización: 2011/07/08 12:37 : Añadida solución de captain_regex

Finalizó el Tuenti Contest, ¿qué os pareció?

Miércoles, 22 de Junio de 2011 Gaspar Fernández Sin comentarios

img

Empezó siendo un comentario de mi novia, ya que al entrar en Tuenti, unos días antes de que se llevara a cabo aparecía al entrar, y yo, que como veis me encanta este mundo, me apunté. Al principio fue con la intención de coger ejemplos para las clases particulares, que muchas veces me encuentro con alguna duda puntual de un alumno y me quedo sin ejemplos.

Lo que pretendía, ingenuo de mí, era saltarme todos o casi todos los retos, copiar las preguntas y los resultados, para hacerlos tranquilamente en cuanto tuviera algo más de tiempo. Ya investigaría si me viene un algoritmo raro y los desarrollaría tranquilamente para este blog.

Por parte de la organización del concurso se facilitaban una serie de programas para probar los programas realizados y para enviarlos, es normal que poner espacios de más o textos que no son exactos a la solución esperada sean rechazados, es un sistema automático, esto ha sido una de las quejas que he visto por ahí, es verdad que podrían haber hecho un trim() para los espacios del final, pero bueno, nos lo han dejado a nosotros. Además, una vez subías una respuesta o te saltabas un reto, no había vuelta atrás.

Pero cuando el día antes de empezar publican un ejercicio de prueba de “lo que íbamos a encontrarnos”, me puse a hacerlo durante el desayuno, me pareció muy curioso lo que planteaban, me gustó la sensación al ver los OK! al probar mi código.

Al día siguiente, entré para ver cómo era todo, para no hacer muchos retos y bajarme contenidos, pero vi el segundo, y me puse a hacerlo, aunque desde el punto de vista de un profesor de programación, intentando utilizar órdenes básicas y que no asusten mucho a estudiantes y en C, y así el tercero, y el cuarto, y cuando menos me lo esperaba me di cuenta de que llevaba 7 retos hechos y tenía que dejarlo durante un rato.

Pero bueno, más allá del pique que me produjo el desarrollo de los problemas encontré varios inconvenientes:

  • Las pruebas que proponían en la página web del concurso no se parecían en nada a las pruebas de la fase de test y éstas tampoco se parecían a las pruebas que se realizaban a la hora de subir el código, cada vez eran pruebas más duras, y algún programa que hice aunque superó la prueba del test, no superó la prueba al subir.
  • Cada vez explicaban menos qué había que hacer y las pruebas propuestas eran incompletas o erróneas, lo cual dificultaba un poco la realización de los retos, aunque sí, por otra parte, agudizaba nuestro pensamiento ingeniero.
  • La comunicación por parte del usuario de Twitter de Tuenti (@TuentiEng) era más bien poca. Aunque comprendo que tampoco se puede contestar a todo y a todos.
  • Los últimos retos eran frustrantes, en el 17, por ejemplo, sólo decía “Just do it” e imposibles, bueno, no tanto, porque hay personas que los superaron.

Un detalle curioso es que la comunicación por parte de Tuenti se hacía a través de Twitter y, aunque puede parecer contradictorio, yo creo que tiene toda la lógica del mundo, hay más personas con Twitter que con Tuenti, y por Twitter la comunicación es más fluida, aunque también había un muro en la red social organizadora pero no tenía ni la décima parte de actividad.

Por último me gustaría felicitar a todos aquellos que habéis completado el concurso (yo me quedé por el 20, aunque el 19 no me terminó de salir bien, lo mio no es la criptografía, aunque poco a poco iré aprendiendo) y destacar aspectos muy positivos de este concurso:

  • He aprendido cosas muy interesantes, nos piden implementar problemas que no se suelen implementar en una carrera universitaria, y si se han llegado a implementar, las pruebas que debían pasar los algoritmos, no son tan duras en muchos casos.
  • Tengo una comunidad en Twitter más amplia con nuevos contactos muy interesantes, sobre todo de participantes.
  • La experiencia ha sido muy positiva. Participé activamente en el concurso y eso que sólo iba a seguirlo por encima para copiar los enunciados.

Actualización: Una cosa más, estoy preparando un post recopilatorio con todas las soluciones que encuentre.

Creando un servidor que acepte múltiples clientes simultáneos en C

Domingo, 6 de Febrero de 2011 Gaspar Fernández Sin comentarios

Para hacer una prueba de esto, crearemos un servidor al que nos podremos conectar por telnet y pedir cierta información a través de comandos. El ejemplo soporta los siguientes comandos (en mayúsculas):

  • DATE: Pide la fecha al servidor
  • TIME: Pide la hora al servidor
  • HOLA: Saluda y me dice mi IP
  • EXIT: Cierra el cliente actual
  • CERRAR: Cierra el servidor.
  • Cualquier otra cosa se manda repetida (ECHO)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/**
*************************************************************
* @file servtcp.c
* @brief Breve descripción
* Ejemplo de un cliente TCP usando threads
*
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* @version 0.1Beta
* @date 13 ene 2011
* Historial de cambios:
*   20110113 - Versión inicial
*
*
*************************************************************/


#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <netinet/in.h>
#include <resolv.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

/** Puerto  */
#define PORT       7000

/** Número máximo de hijos */
#define MAX_CHILDS 3

/** Longitud del buffer  */
#define BUFFERSIZE 512

int AtiendeCliente(int socket, struct sockaddr_in addr);
int DemasiadosClientes(int socket, struct sockaddr_in addr);
void error(int code, char *err);
void reloj(int loop);

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

    int socket_host;
    struct sockaddr_in client_addr;
    struct sockaddr_in my_addr;
    struct timeval tv;      /* Para el timeout del accept */
    socklen_t size_addr = 0;
    int socket_client;
    fd_set rfds;        /* Conjunto de descriptores a vigilar */
    int childcount=0;
    int exitcode;

    int childpid;
    int pidstatus;

    int activated=1;
    int loop=0;
    socket_host = socket(AF_INET, SOCK_STREAM, 0);
    if(socket_host == -1)
      error(1, "No puedo inicializar el socket");
   
    my_addr.sin_family = AF_INET ;
    my_addr.sin_port = htons(PORT);
    my_addr.sin_addr.s_addr = INADDR_ANY ;

   
    if( bind( socket_host, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1 )
      error(2, "El puerto está en uso"); /* Error al hacer el bind() */

    if(listen( socket_host, 10) == -1 )
      error(3, "No puedo escuchar en el puerto especificado");

    size_addr = sizeof(struct sockaddr_in);


    while(activated)
      {
    reloj(loop);
    /* select() se carga el valor de rfds */
    FD_ZERO(&rfds);
    FD_SET(socket_host, &rfds);

    /* select() se carga el valor de tv */
    tv.tv_sec = 0;
    tv.tv_usec = 500000;    /* Tiempo de espera */
   
    if (select(socket_host+1, &rfds, NULL, NULL, &tv))
      {
        if((socket_client = accept( socket_host, (struct sockaddr*)&client_addr, &size_addr))!= -1)
          {
        loop=-1;        /* Para reiniciar el mensaje de Esperando conexión... */
        printf("\nSe ha conectado %s por su puerto %d\n", inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
        switch ( childpid=fork() )
          {
          case -1:  /* Error */
            error(4, "No se puede crear el proceso hijo");
            break;
          case 0:   /* Somos proceso hijo */
            if (childcount<MAX_CHILDS)
              exitcode=AtiendeCliente(socket_client, client_addr);
            else
              exitcode=DemasiadosClientes(socket_client, client_addr);

            exit(exitcode); /* Código de salida */
          default:  /* Somos proceso padre */
            childcount++; /* Acabamos de tener un hijo */
            close(socket_client); /* Nuestro hijo se las apaña con el cliente que
                         entró, para nosotros ya no existe. */

            break;
          }
          }
        else
          fprintf(stderr, "ERROR AL ACEPTAR LA CONEXIÓN\n");
      }

    /* Miramos si se ha cerrado algún hijo últimamente */
    childpid=waitpid(0, &pidstatus, WNOHANG);
    if (childpid>0)
      {
        childcount--;   /* Se acaba de morir un hijo */

        /* Muchas veces nos dará 0 si no se ha muerto ningún hijo, o -1 si no tenemos hijos
         con errno=10 (No child process). Así nos quitamos esos mensajes*/


        if (WIFEXITED(pidstatus))
          {

        /* Tal vez querremos mirar algo cuando se ha cerrado un hijo correctamente */
        if (WEXITSTATUS(pidstatus)==99)
          {
            printf("\nSe ha pedido el cierre del programa\n");
            activated=0;
          }
          }
      }
    loop++;
    }

    close(socket_host);

    return 0;
}

    /* No usamos addr, pero lo dejamos para el futuro */
int DemasiadosClientes(int socket, struct sockaddr_in addr)
{
    char buffer[BUFFERSIZE];
    int bytecount;

    memset(buffer, 0, BUFFERSIZE);
   
    sprintf(buffer, "Demasiados clientes conectados. Por favor, espere unos minutos\n");

    if((bytecount = send(socket, buffer, strlen(buffer), 0))== -1)
      error(6, "No puedo enviar información");
   
    close(socket);

    return 0;
}

int AtiendeCliente(int socket, struct sockaddr_in addr)
{

    char buffer[BUFFERSIZE];
    char aux[BUFFERSIZE];
    int bytecount;
    int fin=0;
    int code=0;         /* Código de salida por defecto */
    time_t t;
    struct tm *tmp;

    while (!fin)
      {

    memset(buffer, 0, BUFFERSIZE);
    if((bytecount = recv(socket, buffer, BUFFERSIZE, 0))== -1)
      error(5, "No puedo recibir información");

    /* Evaluamos los comandos */
    /* El sistema de gestión de comandos es muy rudimentario, pero nos vale */
    /* Comando TIME - Da la hora */
    if (strncmp(buffer, "TIME", 4)==0)
      {
        memset(buffer, 0, BUFFERSIZE);

        t = time(NULL);
        tmp = localtime(&t);

        strftime(buffer, BUFFERSIZE, "Son las %H:%M:%S\n", tmp);
      }
    /* Comando DATE - Da la fecha */
    else if (strncmp(buffer, "DATE", 4)==0)
      {
        memset(buffer, 0, BUFFERSIZE);

        t = time(NULL);
        tmp = localtime(&t);

        strftime(buffer, BUFFERSIZE, "Hoy es %d/%m/%Y\n", tmp);
      }
    /* Comando HOLA - Saluda y dice la IP */
    else if (strncmp(buffer, "HOLA", 4)==0)
      {
        memset(buffer, 0, BUFFERSIZE);
        sprintf(buffer, "Hola %s, ¿cómo estás?\n", inet_ntoa(addr.sin_addr));
      }
    /* Comando EXIT - Cierra la conexión actual */
    else if (strncmp(buffer, "EXIT", 4)==0)
      {
        memset(buffer, 0, BUFFERSIZE);
        sprintf(buffer, "Hasta luego. Vuelve pronto %s\n", inet_ntoa(addr.sin_addr));
        fin=1;
      }
    /* Comando CERRAR - Cierra el servidor */
    else if (strncmp(buffer, "CERRAR", 6)==0)
      {
        memset(buffer, 0, BUFFERSIZE);
        sprintf(buffer, "Adiós. Cierro el servidor\n");
        fin=1;
        code=99;        /* Salir del programa */
      }
    else
      {    
        sprintf(aux, "ECHO: %s\n", buffer);
        strcpy(buffer, aux);
      }

    if((bytecount = send(socket, buffer, strlen(buffer), 0))== -1)
      error(6, "No puedo enviar información");
      }

    close(socket);
    return code;
}

void reloj(int loop)
{
  if (loop==0)
    printf("[SERVIDOR] Esperando conexión  ");

  printf("\033[1D");        /* Introducimos código ANSI para retroceder 2 caracteres */
  switch (loop%4)
    {
    case 0: printf("|"); break;
    case 1: printf("/"); break;
    case 2: printf("-"); break;
    case 3: printf("\\"); break;
    default:            /* No debemos estar aquí */
      break;
    }

  fflush(stdout);       /* Actualizamos la pantalla */
}

void error(int code, char *err)
{
  char *msg=(char*)malloc(strlen(err)+14);
  sprintf(msg, "
Error %d: %s\n", code, err);
  fprintf(stderr, msg);
  exit(1);
}

La clave para poder atender varios clientes simultáneos es bifurcar (fork()) el programa cuando se conecta un nuevo cliente, de esta forma, el diálogo con el cliente se lleva a cabo desde un nuevo proceso mientras que el proceso padre (el primero) sigue escuchando en el puerto esperando conexiones.

Utilizamos select() para no detener la ejecución del programa cuando estamos esperando conexiones; tal vez el programa tenga que hacer algo mientras nadie está conectándose (y es que el programa principal permanecerá aquí la mayor parte del tiempo), por ejemplo lo que hacemos aquí es controlar los hijos que se mueren y ver cómo se han muerto. select() establece un tiempo máximo de espera (500000 microseguntos en este caso, ver la variable tv); si pasado ese tiempo el descriptor socket_host no ha cambiado, seguiremos la ejecución normal del programa, pero si durante ese tiempo cambia ejecutaremos accept().
La función select() funciona con grupos de descriptores y por eso utilizamos las macros FD_ZERO() para limpiar el grupo y FD_SET() para añadir un descriptor al grupo.
El primer parámetro de select() debe ser el descriptor de fichero más grande a comprobar+1; como sólo tenemos un descriptor (socket_host) debemos añadir este al grupo, y pasarle a select en su primer parámetro socket_host+1.

Cuando llega un cliente, como dijimos antes, se abrirá un proceso hijo que lo procesará con una de estas dos funciones: AtiendeCliente() o DemasiadosClientes() dependiendo de si el número máximo de clientes ha sido alcanzado o no. Además, la salida del proceso hijo se obtendrá de la salida de estas funciones, siendo el valor de salida clave 99 el que necesita el proceso padre para cerrar terminar el proceso servidor, es decir, si un hijo sale con el valor de retorno 99 provocará el cierre del servidor. Este valor se maneja debajo del switch(); primero con waitpid() esperamos la muerte de alguno de nuestros hijos aunque con el modificador WNOHANG provocamos que si no se ha muerto ninguno, no vamos a esperar, el programa seguirá su ejecución.

Desde AtiendeCliente() veremos un método muy rudimentario para implementar los comandos, aunque para un ejemplo y 4 comandos nos vale perfectamente.

DemasiadosClientes() devolverá únicamente un mensaje al cliente que se acaba de conectar avisando de que hay demasiados clientes conectados.

Reloj() es un pequeño reloj en modo texto que va girando, a cada iteración del bucle principal, está aquí para que nosotros veamos que el servidor está haciendo algo. Utiliza códigos ANSI, para volver a escribirse en el lugar que estaba, y fflush() para volcar el texto a pantalla antes de nada (puede ser que se acumulen varios mensajes y entonces el dibujado sería incorrecto).

Para probar este programa, debemos compilar con:

$ gcc -o servtcp servtcp.c

y ejecutarlo; y, en un terminal aparte, conectar con telnet, Si, por ejemplo lo queremos hacer todo desde el mismo ordenador; si el servidor está en un ordenador diferente y nos queremos conectar a él cambiamos “localhost” por la IP o el host del ordenador donde se esté ejecutando el cliente:

$ telnet localhost 7000

C.I. VII : Be Linux, esteganografía, Joins SQL, Other OS, Telégrafo twitter, y más

Martes, 1 de Febrero de 2011 Gaspar Fernández 1 comentario

He decidido abreviar lo de Curioso e Interesante (C.I.), y presento algunos enlaces que tenía pendientes de estos días:

Intercambiar mensajes en red local [ solución rápida con netcat y kdialog ]

Sábado, 23 de Octubre de 2010 Gaspar Fernández Sin comentarios

A veces tenemos la necesidad de, en la propia red local intercambiar algún mensaje de texto, ya sea una dirección web o algo así. Muchas veces se opta por enviar un e-mail, aunque aquí traigo una propuesta interesante. No para uso diario, pero para uso puntual cuando no tenemos nada a mano, o para estudiar el método.

Actualmente usa netcat y KDE (para la petición de datos de forma gráfica).

Con netcat sólo bastaría. En un ordenador ponemos:

$ netcat -l -p 6517

Actuaría como servidor escuchando por el puerto 6517 (he usado este puerto, porque creo que no hay ningún servicio definido en este lugar), luego en otro ordenador podemos escribir:

$ netcat [IP/host del primer ordenador] 6517

Con lo que conectaríamos con el otro ordenador. Es una conexión no cifrada, con lo que en este punto no estamos hablando de seguridad, sólo de un “apaño”.

Pero bueno, aunque sea un apaño no tiene por qué ser demasiado feo, así que vamos a acompañarlo de unos diálogos (con kdialog) y a hacer unos scripts que nos permitan iniciar netcat de forma fácil (incluso vincularlo a una tecla de nuestro sistema).

[ enviamensaje ]

1
2
3
4
5
host=`kdialog --inputbox "A que host quieres enviar un mensaje"`
mensaje=`kdialog --inputbox "Escribe el mensaje"`

# Puerto 6517 sin asignar
echo "$mensaje" | netcat -c $host 6517

Desde este archivo preguntamos al usuario el host con el que quiere hablar, y luego se le pide introducir el mensaje, que se mandará por netcat ( -c vale para que la conexión se cierre cuando se termine de enviar el mensaje )

[ recibemensaje ]

1
2
3
4
5
6
7
8
9
10
while read input
do
        kdialog --textinputbox "Alguien te manda algo" "$input" 2>&1 1>/dev/null;
        if [[ "$?" != "0" ]]
        then
                break;
        fi
done

echo "FIN DEL PROGRAMA"

Con este script escribiremos en un diálogo con kdialog el texto recibido por el mensaje. netcat nos pasará por su salida estándar el contenido del mensaje, así que tendremos que leerlo con read

Si aceptamos la ventana dejaremos de esperar mensajes.

[ servidormensajes ]

1
2
3
4
5
6
7
8
9
while (( 1 ))
do
        netcat -l -p 6517 -e ./recibemensaje
        kdialog --yesno "Desconectar servidor?"
        if [[ "$?" == "0" ]]
        then
                break;
        fi
done

Desde aquí ponemos el socket en escucha ( -l = listen ) en el puerto 6517 y lo que recibamos se lo pasamos al script recibemensaje; una vez que esto termine nos preguntará si queremos cerrar el servidor o estamos esperando mensajes de alguien más.

Ahora sólo tenemos que vincular a alguna tecla los script servidormensajes y enviamensaje con lo que podremos comunicarnos con las personas de nuestra red local sin problema. El programa también funcionará por Internet, si tenemos los puertos necesarios abiertos y redireccionados en el router (si usamos router) o nuestro firewall no restringe las conexiones por el puerto 6517 (aunque podemos cambiarlo).

Login a Facebook y acceso a aplicación automatizado

Miércoles, 11 de Agosto de 2010 Gaspar Fernández Sin comentarios

A medida que va creciendo Facebook, los desarrolladores van siendo más imaginativos a la hora de crear aplicaciones que trabajen sobre esta red. Actualmente existen clientes para Facebook que no necesitan que estemos dentro de la página, y lo que presento a continuación es un pequeño ejemplo de todo ello.

Este script es parte investigación / parte navegación y lectura de foros en los que no he encontrado la respuesta exacta a mis necesidades. El problema está en que, a pesar de la existencia de Facebook Connect, si queremos automatizar tareas en Facebook tenemos que estar identificados en el sistema y entonces es cuando podemos hacer la tarea en cuestión.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php

$face_cookie='/tmp/fcbk_cookie';
$useragent="Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3";
$login_email='*************';
$login_pass='**************';

function curl_load_url($url, &$error, $postfields=false)
{
  global $useragent, $face_cookie;
  $cu = curl_init();
  curl_setopt($cu, CURLOPT_URL, $url);
  curl_setopt($cu, CURLOPT_HEADER, 0);
  curl_setopt($cu, CURLOPT_FOLLOWLOCATION, 1);
  curl_setopt($cu, CURLOPT_SSL_VERIFYPEER, false);
  curl_setopt($cu, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($cu, CURLOPT_USERAGENT, $useragent);
  curl_setopt($cu, CURLOPT_COOKIEJAR, $face_cookie);
  curl_setopt($cu, CURLOPT_COOKIEFILE, $face_cookie);
  if ($postfields)
    {
      curl_setopt($cu, CURLOPT_POSTFIELDS, $postfields);
      curl_setopt($cu, CURLOPT_POST, 1);
    }

  $cont=curl_exec($cu);
  $error=curl_errno($cu);

  curl_close($cu);

  return $cont;
}

function fb_login($email, $passwd)
{
  $error=false;
  curl_load_url('http://login.facebook.com/login.php', $error);

  $ch_test='&euro;,&acute;,€,´,水,Д,Є';
  $postfields='cuarset_test='.urlencode($ch_test).'&locale=es_ES&email='.urlencode($email).'&pass='.urlencode($passwd).'&pass_placeholder=&cuarset_test='.urlencode($ch_test);
  $cont=curl_load_url('https://login.facebook.com/login.php?login_attempt=1', $error, $postfields);

 /* Si hay un error de cURL lo decimos */
 if ($error)
   echo 'Error (cURL): '.$error."\n";

 /* Esta palabra *login_attempt* aparece en los intentos de login de Facebook */
 /* es raro que en la página principal salga. */
 /* Podemos también, verificar si está el enlace a editaccount.php para decir */
 /* que hemos sido identificados correctamente */
 if (strpos($cont, 'login_attempt')!==false)
/*  if (strpos($cont, 'editaccount.php')===false) */
   {
     echo 'Error (Facebook): E-mail o contraseña incorrecto';
     $err=1;
   }
 /* Si no hay errores nos daremos por identificados, aunque si el password es incorrecto */
 return (!$err);
}

if (fb_login($login_email,$login_pass)){
  $error=false;
  $cont = curl_load_url('http://apps.facebook.com/minutofilosofico/', $error);
  // Mostramos el contenido de la página
  echo $cont;
}
?>

¿Qué aplicaciones prácticas puede tener esto? Podemos hacer fácilmente un programa de escritorio para manejar Facebook, enviarnos por e-mail o un sms automáticamente si alguien que esperamos nos escribe, subir fotos de forma más rápida con un script en nuestro ordenador… o publicar un mensaje a la hora que hayamos especificado.

Como el ejemplo está en cURL será fácil portarlo a otro lenguaje con el que os sintáis más cómodos.

Extrayendo datos de direcciones web (C++ sin cURL)

Jueves, 29 de Julio de 2010 Gaspar Fernández Sin comentarios

url

En ocasiones, estamos inmersos en un proyecto que requiere conexión con un servidor.

Soy consciente de que existen muchas librerías que nos garantizan el acceso, y tienen muchas opciones aunque, a veces, no necesitamos tanto, sólo un simple acceso y un intercambio sencillo de información; del mismo modo, no nos apetece engordar demasiado nuestro ejecutable, o hacer que nuestro programa dependa de alguna librería externa.

Con esa intención, hice esta clase para C++ y que utilicé para uno de mis pequeños proyectos
hace unos años. Tengo que advertir que es una clase muy simple, y por eso también es válida para los que se quieran iniciar en programación de sockets.

Se puede descargar mysocktar (3.7Kb)

Para compilar el ejemplo:

$ g++ -c MySock.cpp
$ g++ -o ej_mysock ej_mysock.cpp MySock.o

Aún quedan cosas por hacer, sin perder el objetivo de una clase ligera, muy ligera:

  • Hacer documentación compatible con Doxygen
  • Mejor soporte para cabeceras HTML
  • Extracción de tags HTML
  • Soporte para contraseñas

Foto: Diego_3336 (Flickr)

Visita otras webs de la red