|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package IO::PacketSocket; |
|
|
|
|
|
use strict; |
|
|
use warnings; |
|
|
use Exporter; |
|
|
use Carp; |
|
|
use vars qw(@ISA $VERSION @EXPORT); |
|
|
use Errno qw(:POSIX); |
|
|
use English; |
|
|
use IO::Socket::INET |
|
|
|
|
|
|
|
|
$VERSION = 1.00; |
|
|
@ISA = qw(Exporter IO); |
|
|
|
|
|
my ($TRUE, $FALSE) = (1,0); |
|
|
|
|
|
my $ESC = chr(0x1B); |
|
|
|
|
|
my $startDelim = $ESC . 'PKT'; |
|
|
my $endDelim = $ESC . 'END'; |
|
|
my $escapedEsc = $ESC . 'ESC'; |
|
|
|
|
|
|
|
|
sub createObject { |
|
|
my ($class, %args) = @_; |
|
|
|
|
|
my $errorRet; |
|
|
|
|
|
|
|
|
|
|
|
my $packetSocket; |
|
|
|
|
|
$packetSocket = {}; |
|
|
|
|
|
bless ($packetSocket, $class); |
|
|
|
|
|
if (defined($args{STREAMSOCKET})) { |
|
|
$packetSocket->{STREAMSOCKET} = $args{STREAMSOCKET}; |
|
|
} else { |
|
|
$errorRet = "You must specify STREAMSOCKET"; |
|
|
} |
|
|
|
|
|
$packetSocket->{RECEIVE_BUFFER} = ''; |
|
|
|
|
|
if ($errorRet && !$args{ERROR}) { |
|
|
carp("Failed to create PacketSocket object. $errorRet"); |
|
|
} |
|
|
if ($args{ERROR}) { |
|
|
$ {$args{ERROR}} = $errorRet; |
|
|
} |
|
|
if ($args{HANDLE}) { |
|
|
$ {$args{HANDLE}} = $packetSocket; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sub new { |
|
|
|
|
|
my ($class, %args) = @_; |
|
|
|
|
|
$args{HANDLE} = \my $retval; |
|
|
$args{ERROR} = undef; |
|
|
|
|
|
$class->createObject(%args); |
|
|
|
|
|
return $retval; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sub escaped($) { |
|
|
my ($x) = @_; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$x =~ s{$ESC}{$escapedEsc}g; |
|
|
|
|
|
return $x; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sub unescaped($) { |
|
|
my ($x) = @_; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$x =~ s{$escapedEsc}{$ESC}g; |
|
|
|
|
|
return $x; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sub send() { |
|
|
my($this, $payload) = @_; |
|
|
|
|
|
my $retval; |
|
|
|
|
|
my $packet = $startDelim . escaped($payload) . $endDelim; |
|
|
|
|
|
$retval = $this->{STREAMSOCKET}->send($packet); |
|
|
|
|
|
return $retval; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sub havePacket() { |
|
|
|
|
|
my ($this) = @_; |
|
|
|
|
|
return ($this->{RECEIVE_BUFFER} =~ m{$endDelim}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sub validatePacketStart($) { |
|
|
|
|
|
my ($packetR) = @_; |
|
|
|
|
|
my $delim = substr($$packetR, 0, 4); |
|
|
|
|
|
if ($startDelim !~ m{^$delim}) { |
|
|
die("Received bytes '$delim' are not in any packet. " . |
|
|
"Sender is probably not using a packet socket"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sub recv() { |
|
|
my ($this, $payloadR) = @_; |
|
|
|
|
|
my $gotPacket; |
|
|
my $eof; |
|
|
my $escapedPacket; |
|
|
|
|
|
$gotPacket = $FALSE; |
|
|
$eof = $FALSE; |
|
|
|
|
|
while (!$gotPacket && !$eof) { |
|
|
validatePacketStart(\$this->{RECEIVE_BUFFER}); |
|
|
|
|
|
$this->{STREAMSOCKET}->recv(my $buffer, 4096, 0); |
|
|
|
|
|
if ($buffer eq '') { |
|
|
$eof = $TRUE; |
|
|
} else { |
|
|
$this->{RECEIVE_BUFFER} .= $buffer; |
|
|
} |
|
|
|
|
|
validatePacketStart(\$this->{RECEIVE_BUFFER}); |
|
|
|
|
|
if ($this->{RECEIVE_BUFFER} =~ |
|
|
m{^($startDelim)(.*?)($endDelim)(.*)}s) { |
|
|
|
|
|
($escapedPacket, $this->{RECEIVE_BUFFER}) = ($2, $3); |
|
|
|
|
|
$gotPacket = $TRUE; |
|
|
} |
|
|
} |
|
|
|
|
|
$$payloadR = $eof ? '' : unescaped($escapedPacket); |
|
|
} |
|
|
|
|
|
1; |
|
|
|