suhijo has asked for the wisdom of the Perl Monks concerning the following question:

I have two problems, first I need to open a websocket and register each socket into a struct so when an event from Asterisk AMI occurs, send some message to each one of the connected sockets from my @users array but when I open the socket and send the message the connection close.

Second problem is I cannot receive data after websocket is open, a javascript code sends a simple string and I don't know how to open it, instead I receive html headers if I print $data inside sub incoming. Something I must be doing wrong.

I have tried using other libraries, like Net::WebSocket::Server or Net::WebSocket::EV but doesn't suit me well, first one never return after start and second have no idea how to catch the ip, port, etc.

Here is the code, it is a test, not fancy or anything just testing if the solution is possible

use IO::Socket; use EV; use Data::Dumper; use Asterisk::AMI; use Digest::SHA1 qw(sha1 sha1_hex sha1_base64); use Encode qw(decode encode); use threads; use threads::shared; use warnings; $| = 1; my $connection_count = 0; my $h; my $fh; our @users : shared; my $astman = Asterisk::AMI->new(PeerAddr => '127.0.0.1', PeerPort => '5038', Username => 'admin', Secret => 'emel1t0', Events => 'on', Handlers => { default => \&eventhandler } ); die "Unable to connect to asterisk" unless ($astman); sub eventhandler { my ($ami, $event) = @_; if($event->{'Event'} eq "Dial"){ if($event->{'SubEvent'} eq "Begin"){ print "inicio,$event->{'CallerIDNum'},$event->{'Connect +edLineName'},$event->{'UniqueID'}\n"; buscar_exten(10000); } if($event->{'SubEvent'} eq "End"){ print "Fin,$event->{'UniqueID'},$event->{'DialStatus'}\ +n"; } } } my $server = new IO::Socket::INET(LocalPort => 50080, Type => SOCK_STR +EAM,Listen => SOMAXCONN, Reuse => 1, Proto => 'tcp'); my $w = EV::io $server, EV::READ, \&incoming; EV::loop; sub incoming { my $w=shift; $fh=$w->fh->accept or die; my $fileno = fileno $fh; push (@users, $fileno); my $cladd=$fh->peerhost(); my $clport=$fh->peerport(); my $claddr=$fh->peeraddr(); print "cladd: $cladd clport :$clport claddr:\n"; printf "$cladd: new socket connection #%d (%d)\n", ++$connection +_count, +scalar keys %$h; $fh->recv($data, 1024); $data =~ /Sec-WebSocket-Key: (\S+)/; $str = $1; print "key is1 $str|\n"; my $str = sha1_base64($str . "258EAFA5-E914-47DA-95CA-C5AB0D +C85B11"); send($fh, qq{HTTP/1.1 101 Switching Protocols\r\nConnection: + Upgrade\r\nUpgrade: websocket\r\nSec-Websocket-Accept: $str=\r\n\r\n +}, 0); $h->{$fh} = EV::io $fh, EV::READ, \&cliente; } sub cliente { my $c=shift; my $fh2=$c->fh; my $bytes_read=sysread($fh2, my $bytes, 9_999_999); return if (not exists $h->{$fh}); if (($bytes eq 'q') || ($bytes_read == 0)) { close($fh2); undef $c; delete $h->{$fh2}; printf "socket connection terminated by %s (%d (%d) sockets ++remaining)\n", $bytes_read == 0 ? 'peer' : 'server', --$connection_c +ount, scalar keys %$h; } } sub buscar_exten{ $exten=$_[0]; print "buscando si <$exten> esta logueada-\n"; foreach my $b (@users) { open my $c, ">&=$b" ; send($c,"mandando mensaje 1000\r\n",0); } print "}\n"; }

Replies are listed 'Best First'.
Re: Perl an array of sockets
by NetWallah (Canon) on Nov 20, 2017 at 22:27 UTC
    I have not used EV event loops, but this code looks suspicious:
    sub incoming { my $w=shift; $fh=$w->fh->accept or die; <<<<<<<
    You are using a GLOBAL $fh , then attaching another watcher to it that calls back "sub cliente()".

    Try deleting the global "my $fh", and making it local to sub incoming().

                    All power corrupts, but we need electricity.

Re: Perl an array of sockets
by holli (Abbot) on Nov 20, 2017 at 19:45 UTC
    a javascript code sends a simple string and I don't know how to open it, instead I receive html headers
    That's not really surprising. I assume your script runs in a browser and thus speaks HTTP to your sockets, so that's what you get. A raw HTTP request. You really should not try to implement HTTP yourself. If you don't know how to use a specific module, we can help you with that.

    Edit: Sometimes it helps if you try to explain your actual problem and not asking how to fix a (possibly broken) attempt to solve it.


    holli

    You can lead your users to water, but alas, you cannot drown them.

      Perhaps people can help me with this code... it's much simpler than previous but I am unable to get ip, port and won't send message or close socket... seems like does nothing... maybe I am not saving socket correctly or not referencing correctly.

      use EV; use Asterisk::AMI; use Data::Dumper; use Net::WebSocket::EV; use HTTP::Server::EV; use Digest::SHA1 qw(sha1_base64); use threads; use threads::shared; use strict; $|++; my ($rsv,$opcode,$msg, $status_code); our @users : shared; my @users =(); my $cgi ; my $astman = Asterisk::AMI->new(PeerAddr => '127.0.0.1', PeerPort => '5038', Username => 'admin', Secret => 'emel1t0', Events => 'on', Handlers => { default => \&eventhandler } ); die "Unable to connect to asterisk" unless ($astman); sub eventhandler { my ($ami, $event) = @_; if($event->{'Event'} eq "Dial"){ if($event->{'SubEvent'} eq "Begin"){ &buscar_exten($event->{'ConnectedLineName'}); } if($event->{'SubEvent'} eq "End"){ &buscar_exten($event->{'ConnectedLineName'}); } } } HTTP::Server::EV->new->listen(50080, sub { $cgi = $_[0]; $cgi->header({ STATUS => '101 Switching Protocols', Upgrade => "websocket", Connection => "Upgrade", "Sec-WebSocket-Accept" => scalar sha1_base64( $cgi->{headers}{"Sec-WebSocket-Key"} . "2 +58EAFA5-E914-47DA-95CA-C5AB0DC85B11" ).'=', }); $cgi->{self} = $cgi; $cgi->{buffer}->flush_wait(sub { $cgi->{websocket} = Net::WebSocket::EV::Server->new({ fh => $cgi->{buffer}->give_up_handle, on_msg_recv => sub { ($rsv,$opcode,$msg,$status_code) = @_; my @dlogin=split(":",$msg); if(@dlogin[1] eq ""){ print "no autenticado\n"; } else{ push (@users, $cgi->{websocket}) +; } }, on_close => sub { my($code) = @_; $cgi->{self} = undef; $cgi = undef; }, buffering => 1, }); }); }, { threading => 0 }); sub buscar_exten{ my $exten=@_[0]; my $href; my $role; my $some; print "buscando si <$exten> esta logueada-\n"; foreach my $fn (@users) { open my $fh, ">&=$fn" ;# or warn $! and die; send($fh,"evento llamada $exten\n",0) ; print "}\n"; } } EV::loop;
        I'm quite unclear what you mean by
        unable to get ip, port and won't send message or close socket.
        What is the output when you run this? Is there an Asterisk server listening at the port/ip you provided?


        holli

        You can lead your users to water, but alas, you cannot drown them.