• Publicidad

Problema con threads al hacer un servidor

¿Ya sabes lo que es una referencia? Has progresado, el nível básico es cosa del pasado y ahora estás listo para el siguiente nivel.

Notapor explorer » 2009-03-25 12:46 @574

Si deben ir dos procesos haciendo cosas por separado, una forma fácil es hacer dos programas.

Uno es el servidor web, que su única misión es registrar eventos. Podría ser tan sencillo como crear un fichero dentro de un directorio temporal, y que en el nombre de ese fichero esté incluida la fecha y la IP desde donde se conecta (para asegurarnos que cada vez crea un fichero distinto).

Y luego, otro proceso, que cada X tiempo mira en ese directorio y realiza las tareas programadas, eliminando los ficheros que haya procesado (o todos si debe hacerlo así).

Consumo de recursos: esto se puede hacer en mi teléfono móvil. O un PDA. Vamos, solo es necesario un poco Perl, un sistema de ficheros y un sistema de comunicaciones :)

Seguro que hay más formas de hacerlo...
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Publicidad

Notapor creating021 » 2009-03-25 19:32 @855

Como no usas contenido HTML para comunicar a los usuarios, sugiero algo un poco diferente, usando un servidor nonblocking y sin usar fork :D o uno usando fork() pero sin usar threads...

Me explico:
Cuando el servidor corre, se queda esperando a que alguien se conecte.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use Socket;
#Aquí hay código que no quiero poner...
while ( accept(CLIENT, SERVER) ) { # Cuando se acepte la conexión.
    # Procesamos al cliente, ya sea vía fork() o
    # si es nonblocking que sería bueno en este caso.
}
 
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4

Dentro de ese while hacemos las operaciones HTTP y después podemos hacer la tarea que necesitemos, está será la función que nos dice si hay alguien conectado o no; de hecho podemos hacer que el cliente espere un poco.

Ahora no puedo poner el código de mi idea, pero si te interesa... saco el tiempo, claro que es de destacar que no usaré HTTP::Server::Simple, ya que lo que necesitas lo puedes hacer con IO::Socket más un poco de ingenio y un par de minutos o segundos usando Net::Server::NonBlocking.
Expect the worst, is it the least you can do?
Avatar de Usuario
creating021
Perlero frecuente
Perlero frecuente
 
Mensajes: 595
Registrado: 2006-02-23 16:17 @720
Ubicación: Frente al monitor

Notapor Moraita » 2009-03-26 03:00 @167

Hummmmm, sí, estoy de acuerdo que usando sockets también podría hacerse y quizá más sencillo; lo tendré en cuenta para cuando tenga ocasión de plantearlo.

Explorer, me gusta mucho tu idea. Algo así planteé yo al principio y me lo desecharon, no recuerdo muy bien porqué (teorías de arquitectos...).

En fin, volveré a atacar con la misma idea, sabiendo que no soy la única que lo plantea así, me da fuerza :D

Ya os contaré la solución final, de momento tengo que demostrarles que mi código es correcto y que aun así no chuta bien, después pasaré a la fase de vender otra solución...

Informaré de mi solución final.

¡¡Gracias a todos!!
Moraita
Perlero nuevo
Perlero nuevo
 
Mensajes: 36
Registrado: 2008-10-29 10:25 @475

Notapor explorer » 2009-03-26 03:41 @195

Si se trata de ahorrar recursos al máximo, ten en cuenta que ese pequeño sistema de ficheros podría ser incluso un disco RAM.

Vamos, que un Linux de 32Mb instalado en una llave USB podría mover lo que quieres...

Y una lástima que no lo tenga a mano, pero te aseguro que mi idea funcionaría en mi antiguo teléfono Nokia 6600, unido por cable de datos al ordenador.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor Moraita » 2009-03-27 08:55 @413

Bueno, finalmente modificando la posición de "lock" conseguí que me funcionara bien. Por lo visto la variable se bloquea durante todo el bloque donde está referenciada y eso hizo que se ralentizarán y se pegaran entre los dos threads (os envío código correcto para quien quiera...)

Ahora tengo un problemilla distinto.

Arranco el servidor (o sea ejecuto el programa y comienzan ambos threads, entre ellos el del servidor). Lo dejo ahí y al cabo de unas horas de no haber recibido requests lo vuelvo a mirar y el servidor ya se ha caído... ¿alguien sabe por qué?

Se supone que mi programa tiene que tener "siempre" el servidor encendido, no debería parar a no ser que haya un error de ejecución del segundo thread, que esto no ha pasado en ninguna ocasión ya que no había requests durante ese tiempo...

¿Alguna sugerencia o explicación de por qué?

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
################
#package server#
################
package MyWebServer;
use base qw(HTTP::Server::Simple::CGI);
sub handle_request {
        my ($self, $cgi) = @_;
        if ($signal eq 0){
                lock($signal);
                $signal = 1;
                print "signal server: $signal \n";
        }    
        $countSignals++;
        print "contador señales: $countSignals";
        my $timerequest = `date '+%d/%m/%y%H%M%S'`;    
        print "\n $timerequest receiving signal";                                      
}
1;

######
#main#
######

my $httpPublisher = threads->new(\&listenToSignal);
my $httpConsumer = threads->new(\&updateCube);

my @returned_data_http = $httpPublisher->join;
my @returned_data_update = $httpConsumer->join;

#################
#subroutines for threads#
#################


sub listenToSignal{
        MyWebServer->new(8084)-> run();

}#end sub

sub updateCube{
        $updateCube = 0;
        ############
        #control of signal#
        ############
        while ('para siempre') {
                if ($signal > 0){
                        $updateCube = 1;
                        lock($signal);
                        $signal = 0;    
                        print "signal update cube: $signal \n";
                }
                if ($updateCube == 1){ 
                        $updateCube = 0;       
                                  # do work.....}

                }
}
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4
Moraita
Perlero nuevo
Perlero nuevo
 
Mensajes: 36
Registrado: 2008-10-29 10:25 @475

Notapor creating021 » 2009-03-27 16:03 @710

Le falta trabajo, y el contador tiene problemas debido al fork(), pero con un poco de trabajo se logra:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/env perl
use strict;

package MyServer;
use strict;
use Socket;
use POSIX;

our $MaxClients = 50;
our $Clientes   = 0; # Clientes en linea...
our $eol        = "\015\012";

$SIG{CHLD} = \&ExitChild;

sub ExitChild {
    my $pid;
    while ( ( $pid = waitpid( -1, WNOHANG ) ) > 0 ) {
        $Clientes--;
    }
    $SIG{CHLD} = \&ExitChild;
}

sub new {
    my ( $pkg, $tarea ) = @_;
    my $self = {};
    socket( my $server, AF_INET, SOCK_STREAM, getprotobyname('tcp') )
        or die "Socket error: $!\n";
    setsockopt( $server, SOL_SOCKET, SO_REUSEADDR, 1 )
        or die "setsockopt error: $!\n";

    bind( $server, sockaddr_in( 80, INADDR_ANY ) ) or die "bind: $!\n";
    listen( $server, ( SOMAXCONN ) ) or die "listen: $!\n";

    $self->{Socket} = $server;
    $self->{Tarea}  = $tarea;
    bless $self, $pkg;
    return $self;
}

sub run {
    my $self = shift;
    while ( accept(CLIENT, $self->{Socket}) ) {
        $Clientes += 1;
        if ( $Clientes == $MaxClients ) { # Si exede los 50 usuarios...
            print CLIENT ( "HTTP/1.1 509 Bandwidth Limit Exceeded" . $eol );
            print CLIENT ( "DATE: " . scalar( gmtime() ) . " GMT$eol" );
            print CLIENT ( "Connection: close" . $eol );
            print CLIENT $eol;
            print CLIENT "Bandwidth limit exceeded";
            shutdown( CLIENT, 2 ); # no acepta al cliente.
        }

        my $pid = fork(); # Si, decidi hacerlo con fork...
        if ( not $pid ) {
            #Timeout
            POSIX::sigaction( SIGALRM,
            POSIX::SigAction->new( sub { shutdown( CLIENT, 2 ); exit; } ) )
                or die "Error setting SIGALRM: $!\n";
            alarm( 20 );

            select( ( select(CLIENT), $| = 1 )[0] );

            while ( my $cl = <CLIENT> ){print $cl; last if $cl eq $eol;}
            alarm 120; # timeout 2 min.
            print CLIENT ( "HTTP/1.1 200 OK" . $eol );
            print CLIENT ( "DATE: " . scalar( gmtime() ) . "GMT$eol" );
            print CLIENT ( "Content-Type: text/html" . $eol );
            print CLIENT ( "Content-Length: 25" . $eol );
            print CLIENT ( "Connection: Keep-Alive" . "$eol$eol" ); # Para saber si interesado.
            print CLIENT ( "Hello Dave, I am HAL 9000" . $eol );
            $self->{Tarea}() or print "$@\n";
        }
    }
}

package main;
sub Tarea {
    print "Usuarios conectados: $MyServer::Clientes\n";
}
my $servidor = MyServer->new( \&Tarea );
$servidor->run();
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4

EDITO:
Hay otro problema, con éste y cualquier otro servidor... resulta que todos los browsers hacen dos peticiones, el index y el favicon.ico.
Croe que lo más facil va a ser usar un servidor como lighttp o OpenAngle y un CGI.
Expect the worst, is it the least you can do?
Avatar de Usuario
creating021
Perlero frecuente
Perlero frecuente
 
Mensajes: 595
Registrado: 2006-02-23 16:17 @720
Ubicación: Frente al monitor

Anterior

Volver a Intermedio

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 9 invitados