PacketSocket.pm 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. ###############################################################################
  2. #
  3. # This Perl module provides a "packet socket" of the kind that
  4. # XML-RPC For C/C++ uses for its "packet stream" variation on XML-RPC.
  5. #
  6. # This module does not use the XML-RPC For C/C++ libraries. It is
  7. # pure Perl and layers on top of IO::Socket.
  8. #
  9. # By Bryan Henderson, San Jose CA 08.03.12.
  10. #
  11. # Contributed to the public domain by author.
  12. #
  13. ###############################################################################
  14. package IO::PacketSocket;
  15. use strict;
  16. use warnings;
  17. use Exporter;
  18. use Carp;
  19. use vars qw(@ISA $VERSION @EXPORT);
  20. use Errno qw(:POSIX);
  21. use English;
  22. use IO::Socket::INET
  23. $VERSION = 1.00;
  24. @ISA = qw(Exporter IO);
  25. my ($TRUE, $FALSE) = (1,0);
  26. my $ESC = chr(0x1B); # ASCII Escape
  27. my $startDelim = $ESC . 'PKT';
  28. my $endDelim = $ESC . 'END';
  29. my $escapedEsc = $ESC . 'ESC';
  30. sub createObject {
  31. my ($class, %args) = @_;
  32. my $errorRet;
  33. # Description of why we can't create the object. Undefined if
  34. # we haven't given up yet.
  35. my $packetSocket;
  36. $packetSocket = {};
  37. bless ($packetSocket, $class);
  38. if (defined($args{STREAMSOCKET})) {
  39. $packetSocket->{STREAMSOCKET} = $args{STREAMSOCKET};
  40. } else {
  41. $errorRet = "You must specify STREAMSOCKET";
  42. }
  43. $packetSocket->{RECEIVE_BUFFER} = '';
  44. if ($errorRet && !$args{ERROR}) {
  45. carp("Failed to create PacketSocket object. $errorRet");
  46. }
  47. if ($args{ERROR}) {
  48. $ {$args{ERROR}} = $errorRet;
  49. }
  50. if ($args{HANDLE}) {
  51. $ {$args{HANDLE}} = $packetSocket;
  52. }
  53. }
  54. sub new {
  55. my ($class, %args) = @_;
  56. $args{HANDLE} = \my $retval;
  57. $args{ERROR} = undef;
  58. $class->createObject(%args);
  59. return $retval;
  60. }
  61. sub escaped($) {
  62. my ($x) = @_;
  63. #-----------------------------------------------------------------------------
  64. # Return $x, but properly escaped to be inside a packet socket
  65. # packet.
  66. #-----------------------------------------------------------------------------
  67. $x =~ s{$ESC}{$escapedEsc}g;
  68. return $x;
  69. }
  70. sub unescaped($) {
  71. my ($x) = @_;
  72. #-----------------------------------------------------------------------------
  73. # Inverse of escaped()
  74. #-----------------------------------------------------------------------------
  75. $x =~ s{$escapedEsc}{$ESC}g;
  76. return $x;
  77. }
  78. sub send() {
  79. my($this, $payload) = @_;
  80. my $retval;
  81. my $packet = $startDelim . escaped($payload) . $endDelim;
  82. $retval = $this->{STREAMSOCKET}->send($packet);
  83. return $retval;
  84. }
  85. sub havePacket() {
  86. my ($this) = @_;
  87. return ($this->{RECEIVE_BUFFER} =~ m{$endDelim});
  88. }
  89. sub validatePacketStart($) {
  90. my ($packetR) = @_;
  91. my $delim = substr($$packetR, 0, 4);
  92. if ($startDelim !~ m{^$delim}) {
  93. die("Received bytes '$delim' are not in any packet. " .
  94. "Sender is probably not using a packet socket");
  95. }
  96. }
  97. sub recv() {
  98. my ($this, $payloadR) = @_;
  99. my $gotPacket;
  100. my $eof;
  101. my $escapedPacket;
  102. $gotPacket = $FALSE;
  103. $eof = $FALSE;
  104. while (!$gotPacket && !$eof) {
  105. validatePacketStart(\$this->{RECEIVE_BUFFER});
  106. $this->{STREAMSOCKET}->recv(my $buffer, 4096, 0);
  107. if ($buffer eq '') {
  108. $eof = $TRUE;
  109. } else {
  110. $this->{RECEIVE_BUFFER} .= $buffer;
  111. }
  112. validatePacketStart(\$this->{RECEIVE_BUFFER});
  113. if ($this->{RECEIVE_BUFFER} =~
  114. m{^($startDelim)(.*?)($endDelim)(.*)}s) {
  115. ($escapedPacket, $this->{RECEIVE_BUFFER}) = ($2, $3);
  116. $gotPacket = $TRUE;
  117. }
  118. }
  119. $$payloadR = $eof ? '' : unescaped($escapedPacket);
  120. }
  121. 1;