Permalink
Browse files

Document Reflex::Connector and Reflex::Listener.

  • Loading branch information...
1 parent 9a7526b commit 64695f714b2a5968186a5713b227ea7520501ad1 @rcaputo committed Apr 21, 2010
Showing with 265 additions and 6 deletions.
  1. +51 −0 eg/eg-38-promise-client.pl
  2. +135 −5 lib/Reflex/Connector.pm
  3. +79 −1 lib/Reflex/Listener.pm
@@ -0,0 +1,51 @@
+# A TCP echo client that looks like it's blocking when it's not.
+
+use lib qw(../lib);
+use Reflex::Timer;
+use Reflex::Connector;
+use Reflex::Stream;
+use Reflex::Callbacks qw(cb_coderef);
+use ExampleHelpers qw(eg_say);
+
+# Run a timer so we can prove the client isn't blocking.
+my $ticker = Reflex::Timer->new(
+ interval => 0.001,
+ auto_repeat => 1,
+ on_tick => cb_coderef { eg_say("tick...") },
+);
+
+# Begin connecting to eg-34-tcp-server-echo.pl.
+my $connector = Reflex::Connector->new(remote_port => 12345);
+
+# Wait for the connection to finish.
+my $event = $connector->wait();
+
+# Failure? Ok, bye.
+if ($event->{name} eq "failure") {
+ eg_say("connection error $event->{arg}{errnum}: $event->{arg}{errstr}");
+ exit;
+}
+
+# Otherwise success.
+eg_say("Connected.");
+
+# Start a stream to work with it.
+my $stream = Reflex::Stream->new(
+ handle => $event->{arg}{socket},
+ rd => 1,
+);
+
+# Say hello.
+$stream->put("Hello, world!\n");
+
+# Handle a response.
+$event = $stream->wait();
+if ($event->{name} eq "data") {
+ eg_say("Got echo response: $event->{arg}{data}");
+}
+else {
+ eg_say("Unexpected event: $event->{name}");
+}
+
+# Bored now.
+exit;
@@ -11,7 +11,7 @@ use Socket qw(SOL_SOCKET SO_ERROR inet_aton pack_sockaddr_in);
has remote_addr => (
is => 'ro',
isa => 'Str',
- default => 'localhost',
+ default => '127.0.0.1',
);
# TODO - Make it an integer. Coerce from string by resolving
@@ -58,7 +58,10 @@ sub BUILD {
errfun => "connect",
},
);
+
$self->wr(0);
+ $self->handle(undef);
+
return;
}
}
@@ -69,6 +72,7 @@ sub on_handle_writable {
# Not watching anymore.
$self->wr(0);
+ $self->handle(undef);
# Throw a failure if the connection failed.
$! = unpack('i', getsockopt($args->{handle}, SOL_SOCKET, SO_ERROR));
@@ -89,12 +93,138 @@ sub on_handle_writable {
event => "success",
args => {
socket => $args->{handle},
- errnum => ($!+0),
- errstr => "$!",
- errfun => "connect",
},
);
}
1;
-# TODO - Document.
+
+__END__
+
+=head1 NAME
+
+Reflex::Connector - Connect to a server without blocking.
+
+=head1 SYNOPSIS
+
+This is an incomplete excerpt from Reflex::Client. See that module's
+source for a more complete example.
+
+ package SomeKindaClient;
+ use Moose;
+ extends 'Reflex::Connector';
+
+ sub on_connector_success {
+ my ($self, $args) = @_;
+
+ # Do something with $arg->{socket} here.
+ }
+
+ sub on_connector_failure {
+ my ($self, $args) = @_;
+ warn "$args->{errfun} error $args->{errnum}: $args->{errstr}\n";
+ $self->stop();
+ }
+
+Reflex objects may also be used in condvar-like ways. This excerpts
+from eg/eg-38-promise-client.pl in the distribution.
+
+ my $connector = Reflex::Connector->new(remote_port => 12345);
+ my $event = $connector->wait();
+
+ if ($event->{name} eq "failure") {
+ eg_say("connection error $event->{arg}{errnum}: $event->{arg}{errstr}");
+ exit;
+ }
+
+ eg_say("Connected.");
+ # Do something with $event->{arg}{socket}.
+
+=head1 DESCRIPTION
+
+Reflex::Connector performs a non-blocking connect() object on a plain
+socket. It extends Reflex::Handle to wait for the connection without
+blocking the rest of a program.
+
+By default, it will create its own TCP socket. A program can provide
+a specially prepared socket via the inherited "handle" attribute.
+
+Two other attributes, "remote_addr" and "remote_port" specify where to
+connect the socket.
+
+This connector was written with TCP in mind, but it's intended to also
+be useful for other connected sockets.
+
+=head2 Attributes
+
+Reflex::Connector supplies its own attributes in addition to those
+provided by Reflex::Handle.
+
+=head3 remote_addr
+
+The "remote_addr" attribute specifies the address of a remote server.
+It defaults to "127.0.0.1".
+
+=head3 remote_port
+
+The "remote_port" attribute sets the port of the server to which it
+will attempt a connection. The remote port may be an integer or the
+symbolic port name from /etc/services.
+
+=head2 Methods
+
+Reflex::Connector inherits its methods from Reflex::Handle. It
+doesn't add new methods at this time.
+
+=head2 Events
+
+Reflex::Connector emits some events, which may be mapped to a
+subclass' methods, or to handlers in a container object. Please see
+L<Reflex> and L<Reflex::Callbacks> for more information.
+
+=head3 failure
+
+Revlex::Connector emits a "failure" event if it can't establish a
+connection. Failure events include a few, fairly standard parameters:
+
+=over 2
+
+=item * socket - Undefined, since a connection could not be made.
+
+=item * errnum - The numeric value of $! at the time of error.
+
+=item * errstr - The string value of $! at the time of error.
+
+=item * errfun - A brief description of the function call that failed.
+
+=back
+
+=head3 success
+
+The "success" event is emitted if a connection has been established.
+It will return a "socket", the value of which is the connected socket.
+
+=head2 EXAMPLES
+
+L<Reflex::Client> extends Reflex::Connector to include a
+Reflex::Stream when the socket is connected.
+
+eg/eg-38-promise-client.pl shows how to use Reflex::Connector in a
+condvar-like fashion.
+
+=head1 SEE ALSO
+
+L<Reflex>
+L<Reflex::Client>
+
+L<Reflex/ACKNOWLEDGEMENTS>
+L<Reflex/ASSISTANCE>
+L<Reflex/AUTHORS>
+L<Reflex/BUGS>
+L<Reflex/BUGS>
+L<Reflex/CONTRIBUTORS>
+L<Reflex/COPYRIGHT>
+L<Reflex/LICENSE>
+L<Reflex/TODO>
+
+=cut
@@ -34,4 +34,82 @@ sub on_handle_readable {
}
1;
-# TODO - Document.
+
+__END__
+
+=head1 NAME
+
+Reflex::Listener - Generate connected client sockets from a listening server socket.
+
+=head1 SYNOPSIS
+
+ # This is an incomplete excerpt from eg/eg-34-tcp-server-echo.pl.
+
+ package TcpEchoServer;
+
+ use Moose;
+ extends 'Reflex::Listener';
+ use Reflex::Collection;
+ use EchoStream;
+
+ has clients => (
+ is => 'rw',
+ isa => 'Reflex::Collection',
+ default => sub { Reflex::Collection->new() },
+ handles => { remember_client => "remember" },
+ );
+
+ sub on_listener_accepted {
+ my ($self, $args) = @_;
+ $self->remember_client(
+ EchoStream->new(
+ handle => $args->{socket},
+ rd => 1,
+ )
+ );
+ }
+
+ sub on_listener_failure {
+ my ($self, $args) = @_;
+ warn "$args->{errfun} error $args->{errnum}: $args->{errstr}\n";
+ }
+
+=head1 DESCRIPTION
+
+Reflex::Listener extends Reflex::Handle. It watches listening server
+sockets for new client connections. When they arrive, it accept()s
+them and emits them in "accepted" events.
+
+=head2 Attributes
+
+Reflex::Listener inherits its attributes from Reflex::Handle. It sets
+the rd() attribute to true by default---so listeners start up ready to
+accept connections.
+
+=head2 Methods
+
+Reflex::Listener inherits its methods from Reflex::Handle. It doesn't
+add new methods at this time.
+
+=head2 EXAMPLES
+
+eg/eg-34-tcp-server-echo.pl in Reflex's distribution implements a
+simple TCP server using Reflex::Listener. The SYNOPSIS for this
+module is an excerpt from that example.
+
+=head1 SEE ALSO
+
+L<Reflex>
+L<Reflex::Handle>
+
+L<Reflex/ACKNOWLEDGEMENTS>
+L<Reflex/ASSISTANCE>
+L<Reflex/AUTHORS>
+L<Reflex/BUGS>
+L<Reflex/BUGS>
+L<Reflex/CONTRIBUTORS>
+L<Reflex/COPYRIGHT>
+L<Reflex/LICENSE>
+L<Reflex/TODO>
+
+=cut

0 comments on commit 64695f7

Please sign in to comment.