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

Hi monks,

I am trying to learn how to write perl modules (this is my first one) and have run into a bit of a problem. Below is the module I am writing (it's spose'd to interface with a Festival server). It's all working execpt for the speak sub, specifically the syswrite command. I keep getting an error "broken pipe" but dont know how to resolve this.
Can anyone give me any hints?
Cheers,
Reagen

package Voice; # ::: External modules used by this module ::: # use IO::Socket; ################################################# # Method: Voice->new # Description: Object Constructor method ################################################# sub new { # bless a new object shift; my $self = {}; bless($self); # Set params passed for the object my ($server, $port) = ('localhost', 1314); # ::: Set Object Parameters ::: # $self->{ERROR} = undef; # error (duh!) $self->{SERVER} = $server; # festival server $self->{PORT} = $port; # festival port $self->{PRO} = 'tcp'; # connection protocol $self->{CONN} = undef; # connection object # return the object return $self; } # end-new # ----------------------------------------------- ################################################# # Method: Voice->open # Description: open the festival connection ################################################# sub open { # set as method my $self = shift; # create a connection to the festival server using IO::Socket::INET if ($self->{CONN} = IO::Socket::INET->new( Proto => $self->{PRO}, Pe +erAdr => $self->{SERVER}, PeerPort => $self->{PORT} )) { } # end-if else { # set the error string $self->{ERROR} = "Could not open connection to the festival server +!"; } # end-else } # end-open # ----------------------------------------------- ################################################# # Method: Voice->speak # Description: say the sentence ################################################# sub speak { # set as method my $self = shift; # read in the sentence my ($sentence) = @_; my $buffer = "(SayText \"" . $sentence . "\")\n"; # say the sentence if ( syswrite($self->{CONN}, $buffer, length($buffer) ) ) { } # end-if else { # set the error string $self->{ERROR} = "Could not say sentence!"; } # end-else } # end-prepare # ----------------------------------------------- ################################################# # Method: Voice->close # Description: close the festival connection ################################################# sub close { # set as method my $self = shift; # close the IO::Socket::INET connection shutdown($self->{CONN}, 2); close($self->{CONN}); } # end-close # ----------------------------------------------- 1;

Replies are listed 'Best First'.
Re: Festival Module
by mifflin (Curate) on Sep 23, 2004 at 22:47 UTC
    Try checking to see what syswrite returns. Maybe your call is not writing all of buffer. If so syswrite will return how much it actually wrote. You could then used the offset parament of syswrite to continue to write the rest of the buffer. I have a similar app that writes data to a socket connection. Here is how I implement it...
    # # Sub used to write data to FDMS. # Writing requires that the first 6 bytes are NBxxxx # where xxxx is the total number of bytes to write plus the NBxxxx. # sub write { my ($this, $buffer) = @_; my $thisSub = (caller(0))[3]; my $socket = $this->{socket}; my $select = $this->{select}; my $verbose = $this->{verbose}; my $timeout = $this->{writeTimeout}; $buffer = sprintf('NB%04d%s', length($buffer) + 6, $buffer); my $length = length($buffer); my $offset; my $bytes; while ($length > 0) { if ($select->can_write($timeout)) { $bytes = $socket->syswrite($buffer, $length, $offset); croak "Unable to write: $!" unless defined $bytes; $offset += $bytes; $length -= $bytes; print "$thisSub($bytes) = $buffer\n" if $verbose; } else { croak 'timeout on write'; } } }

    UPDATE:
    try putting in a signal handler for SIGPIPE to see if you can get around the problem.
    Something like...
    $SIG{PIPE} = sub { die "SIGPIPE\n"; };
    ...just to see if it is happening.

    UPDATE:
    See this post...
    script dies with 'broken pipe'-message, even in eval
      thanks for your input. I've tried the $SIG{PIPE} and it is dying so it is happening. I've tried to simplify it into the following:
      Module (Voice.pm):
      package Voice; use IO::Socket; sub new { shift; my $self = {}; bless($self); return $self; } # end-new sub talk { $handle = IO::Socket::INET->new(Proto => "tcp", PeerAdr => "localhost", PeerPort => 1314) || die($!); print $handle "(SayText \"Testing.\")" || die($!); } # end-talk 1;
      Script (test.cgi):
      #!/usr/bin/perl use CGI qw(:header); print header; use Voice; my $voice = new Voice; $voice->talk; exit;
      When I run test.cgi through a web browser I get nothing (even if the festival server is not running). When I run it from the command line, I get "broken pipe" regardless of whether the festival server is running or not.
      The following script works just fine:
      #!/usr/bin/perl use IO::Socket; $handle = IO::Socket::INET->new(Proto => "tcp", PeerAddr => "localhost", PeerPort => 1314) || die($!); print $handle "(SayText \"Testing.\")"; exit;
      Being a newbie at perl modules my guess is that I've done something wrong in writing the module but I really dont know.
        UPDATED from original post.

        I tried you're simpler test script with $voice->talk, and it wouldn't even work without cgi. If you find the answer, let us know. I'm interested in learning about using sockets in modules, and there seems to be a trick to it, which eludes me. Like you it works from a conventional script. I did get it working from a simple procedural style module, but as soon as I put the $socket into a $self->{CONN} form, it gives a broken pipe. It has to be they way the socket gets stored in the scalar. Either a reference to the scalar needs to be used, or the socket needs to be stored as a full fledged hash? I just started reading my Object Oriented Perl, but hav'nt got far enough yet. :-)

        package Voice2; use IO::Socket; $handle = IO::Socket::INET->new(Proto => "tcp", PeerAddr => "localhost", PeerPort => 1314) || die($!); sub talk { print $handle "(SayText \"Testing.\")"; } 1;
        Test script
        #!/usr/bin/perl use lib '.'; use Voice2; Voice2::talk; exit;

        I'm not really a human, but I play one on earth. flash japh
Re: Broken Pipe error when talking to Festival server
by zentara (Cardinal) on Sep 24, 2004 at 21:13 UTC
    Hi, well I played abit with it, and got something that works. :-) Now I'm not an expert on OO programming, so just take this as is. From what I can observe, there is somekind of problem when you you set $self->{CONN} = undef in new(), and then try to redefine it in open. So I removed the open() method, and it works from the commandline. In your cgi, there may be additional complications because of the server. Anyways, here is a working module and test script.
    package Voice; use strict; use warnings; require IO::Socket; ################################################# # Method: Voice->new # Description: Object Constructor method ################################################# sub new { # bless a new object shift; my $self = {}; bless($self); # Set params passed for the object my ( $server, $port ) = ( 'localhost', 1314 ); # ::: Set Object Parameters ::: # $self->{ERROR} = undef; # error (duh!) $self->{SERVER} = $server; # festival server $self->{PORT} = $port; # festival port $self->{PRO} = 'tcp'; # connection protocol $self->{CONN} = IO::Socket::INET->new( Proto => "tcp", PeerAddr => "localhost", PeerPort => 1314) || die($!); # return the object return $self; } # end-new # ----------------------------------------------- ################################################# # Method: Voice->speak # Description: say the sentence ################################################# sub speak { # set as method my $self = shift; # read in the sentence my ($sentence) = @_; # my $buffer = "(SayText \"" . $sentence . "\")\n"; my $buffer = "(SayText \"" . $sentence . "\")"; #print "$buffer\n"; # say the sentence if ( syswrite( $self->{CONN}, $buffer, length($buffer) ) ) { } # end-if else { # set the error string $self->{ERROR} = "Could not say sentence!"; } # end-else } # end-prepare # ----------------------------------------------- ################################################# # Method: Voice->close # Description: close the festival connection ################################################# sub close { # set as method my $self = shift; # close the IO::Socket::INET connection shutdown( $self->{CONN}, 2 ); close( $self->{CONN} ); } # end-close # ----------------------------------------------- 1;
    #test script
    #!/usr/bin/perl use warnings; use strict; use lib '.'; use Voice; my $voice = Voice->new; $voice->speak( 'foobar foobar foobar' ); $voice->close;

    I'm not really a human, but I play one on earth. flash japh
Re: Broken Pipe error when talking to Festival server
by mifflin (Curate) on Sep 24, 2004 at 19:30 UTC
    Try looking at the CPAN module that connects to a Festival server.
    http://search.cpan.org/~rcaley/speech_pm_1.0/Speech/Festival.pm
    If you are still interesting in writing your own you can look into how it was implemented to see were you might be going wrong.