123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- ###############################################################################
- #
- # This Perl module provides a "packet socket" of the kind that
- # XML-RPC For C/C++ uses for its "packet stream" variation on XML-RPC.
- #
- # This module does not use the XML-RPC For C/C++ libraries. It is
- # pure Perl and layers on top of IO::Socket.
- #
- # By Bryan Henderson, San Jose CA 08.03.12.
- #
- # Contributed to the public domain by author.
- #
- ###############################################################################
- 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); # ASCII Escape
- my $startDelim = $ESC . 'PKT';
- my $endDelim = $ESC . 'END';
- my $escapedEsc = $ESC . 'ESC';
- sub createObject {
- my ($class, %args) = @_;
- my $errorRet;
- # Description of why we can't create the object. Undefined if
- # we haven't given up yet.
- 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) = @_;
- #-----------------------------------------------------------------------------
- # Return $x, but properly escaped to be inside a packet socket
- # packet.
- #-----------------------------------------------------------------------------
- $x =~ s{$ESC}{$escapedEsc}g;
- return $x;
- }
- sub unescaped($) {
- my ($x) = @_;
- #-----------------------------------------------------------------------------
- # Inverse of escaped()
- #-----------------------------------------------------------------------------
- $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;
|