NAME¶
POE::Wheel::SocketFactory - non-blocking socket creation
SYNOPSIS¶
See "SYNOPSIS" in POE::Component::Server::TCP for a much simpler
version of this program.
#!perl
use warnings;
use strict;
use IO::Socket;
use POE qw(Wheel::SocketFactory Wheel::ReadWrite);
POE::Session->create(
inline_states => {
_start => sub {
# Start the server.
$_[HEAP]{server} = POE::Wheel::SocketFactory->new(
BindPort => 12345,
SuccessEvent => "on_client_accept",
FailureEvent => "on_server_error",
);
},
on_client_accept => sub {
# Begin interacting with the client.
my $client_socket = $_[ARG0];
my $io_wheel = POE::Wheel::ReadWrite->new(
Handle => $client_socket,
InputEvent => "on_client_input",
ErrorEvent => "on_client_error",
);
$_[HEAP]{client}{ $io_wheel->ID() } = $io_wheel;
},
on_server_error => sub {
# Shut down server.
my ($operation, $errnum, $errstr) = @_[ARG0, ARG1, ARG2];
warn "Server $operation error $errnum: $errstr\n";
delete $_[HEAP]{server};
},
on_client_input => sub {
# Handle client input.
my ($input, $wheel_id) = @_[ARG0, ARG1];
$input =~ tr[a-zA-Z][n-za-mN-ZA-M]; # ASCII rot13
$_[HEAP]{client}{$wheel_id}->put($input);
},
on_client_error => sub {
# Handle client error, including disconnect.
my $wheel_id = $_[ARG3];
delete $_[HEAP]{client}{$wheel_id};
},
}
);
POE::Kernel->run();
exit;
DESCRIPTION¶
POE::Wheel::SocketFactory creates sockets upon demand. It can create
connectionless UDP sockets, but it really shines for client/server work where
establishing connections normally would block.
PUBLIC METHODS¶
new¶
new() creates a new POE::Wheel::SocketFactory object. For sockets which
listen() for and
accept() connections, the wheel will generate
new sockets for each accepted client. Socket factories for one-shot sockets,
such as UDP peers or clients established by
connect() only emit a
single socket and can be destroyed afterwards without ill effects.
new() always returns a POE::Wheel::SocketFactory object even if it fails
to establish the socket. This allows the object to be queried after it has
sent its session a "FailureEvent".
new() accepts a healthy number of named parameters, each governing some
aspect of socket creation.
Creating the Socket
Socket creation is done with Perl's built-in
socket() function. The
new() parameters beginning with "Socket" determine how
socket() will be called.
SocketDomain
"SocketDomain" instructs the wheel to create a socket within a
particular domain. Supported domains are "AF_UNIX",
"AF_INET", "AF_INET6", "PF_UNIX",
"PF_INET", and "PF_INET6". If omitted, the socket will be
created in the "AF_INET" domain.
POE::Wheel::SocketFactory contains a table of supported domains and the
instructions needed to create them. Please send patches to support additional
domains, as needed.
Note: "AF_INET6" and "PF_INET6" are supplied by the Socket
module included in Perl 5.8.0 or later. Perl versions before 5.8.0 should not
attempt to use IPv6 until someone contributes a workaround.
IPv6 support requires a Socket module that implements
getaddrinfo() and
unpack_sockaddr_in6(). There may be other modules that perform these
functions, but most if not all of them have been deprecated with the advent of
proper core Socket support for IPv6.
SocketType
"SocketType" supplies the
socket() call with a particular
socket type, which may be "SOCK_STREAM" or "SOCK_DGRAM".
"SOCK_STREAM" is the default if "SocketType" is not
supplied.
SocketProtocol
"SocketProtocol" sets the
socket() call's protocol. Protocols
may be specified by number or name. "SocketProtocol" is ignored for
UNIX domain sockets.
The protocol defaults to "tcp" for INET domain sockets. There is no
default for other socket domains.
Setting Socket Options
POE::Wheel::SocketFactory uses
ioctl(),
fcntl() and
setsockopt() to set socket options after the socket is created. All
sockets are set non-blocking, and bound sockets may be made reusable.
Reuse
When set, the "Reuse" parameter allows a bound port to be reused
immediately. "Reuse" is considered enabled if it contains
"yes", "on", or a true numeric value. All other values
disable port reuse, as does omitting "Reuse" entirely.
For security purposes, a port cannot be reused for a minute or more after a
server has released it. This gives clients time to realize the port has been
abandoned. Otherwise a malicious service may snatch up the port and spoof the
legitimate service.
It's also terribly annoying to wait a minute or more between server invocations,
especially during development.
Bind the Socket to an Address and Port
A socket may optionally be bound to a specific interface and port. The
"INADDR_ANY" address may be used to bind to a specific port across
all interfaces.
Sockets are bound using
bind(). POE::Wheel::SocketFactory parameters
beginning with "Bind" control how
bind() is called.
BindAddress
"BindAddress" sets an address to bind the socket's local endpoint to.
"INADDR_ANY" will be used if "BindAddress" is not
specified.
"BindAddress" may contain either a string or a packed Internet address
(for "INET" domain sockets). The string parameter should be a dotted
numeric address or a resolvable host name. Note that the host name will be
resolved with a blocking call. If this is not desired, use
POE::Component::Client::DNS to perform a non-blocking name resolution.
When used to bind a "UNIX" domain socket, "BindAddress"
should contain a path describing the socket's filename. This is required for
server sockets and datagram client sockets. "BindAddress" has no
default value for UNIX sockets.
BindPort
"BindPort" is only meaningful for "INET" domain sockets. It
contains a port on the "BindAddress" interface where the socket will
be bound. It defaults to 0 if omitted, which will cause the
bind() call
to choose an indeterminate unallocated port.
"BindPort" may be a port number or a name that can be looked up in the
system's services (or equivalent) database.
Connectionless Sockets
Connectionless sockets may interact with remote endpoints without needing to
listen() for connections or
connect() to remote addresses.
This class of sockets is complete after the
bind() call.
Connecting the Socket to a Remote Endpoint
A socket may either listen for connections to arrive, initiate connections to a
remote endpoint, or be connectionless (such as in the case of UDP sockets).
POE::Wheel::SocketFactory will initiate a client connection when
new() is
capped with parameters that describe a remote endpoint. In all other cases,
the socket will either listen for connections or be connectionless depending
on the socket type.
The following parameters describe a socket's remote endpoint. They determine how
POE::Wheel::SocketFactory will call Perl's built-in
connect() function.
RemoteAddress
"RemoteAddress" specifies the remote address to which a socket should
connect. If present, POE::Wheel::SocketFactory will create a client socket
that attempts to collect to the "RemoteAddress". Otherwise, if the
protocol warrants it, the wheel will create a listening socket and attempt to
accept connections.
As with the bind address, "RemoteAddress" may be a string containing a
dotted quad or a resolvable host name. It may also be a packed Internet
address, or a UNIX socket path. It will be packed, with or without an
accompanying "RemotePort", as necessary for the socket domain.
RemotePort
"RemotePort" is the port to which the socket should connect. It is
required for "INET" client sockets, since the remote endpoint must
contain both an address and a port.
The remote port may be numeric, or it may be a symbolic name found in
/etc/services or the equivalent for your operating system.
Listening for Connections
Streaming sockets that have no remote endpoint are considered to be server
sockets. POE::Wheel::SocketFactory will
listen() for connections to
these sockets,
accept() the new clients, and send the application
events with the new client sockets.
POE::Wheel::SocketFactory constructor parameters beginning with
"Listen" control how the
listen() function is called.
ListenQueue
"ListenQueue" specifies the length of the socket's
listen()
queue. It defaults to "SOMAXCONN" if omitted.
"ListenQueue" values greater than "SOMAXCONN" will be
clipped to "SOMAXCONN". Excessively large "ListenQueue"
values are not necessarily portable, and may cause errors in some rare cases.
Emitting Events
POE::Wheel::SocketFactory emits a small number of events depending on what
happens during socket setup or while listening for new connections.
See "PUBLIC EVENTS" for more details.
SuccessEvent
"SuccessEvent" names the event that will be emitted whenever
POE::Wheel::SocketFactory succeeds in creating a new socket.
For connectionless sockets, "SuccessEvent" happens just after the
socket is created.
For client connections, "SuccessEvent" is fired when the connection
has successfully been established with the remote endpoint.
Server sockets emit a "SuccessEvent" for every successfully accepted
client.
FailureEvent
"FailureEvent" names the event POE::Wheel::SocketFactory will emit
whenever something goes wrong. It usually represents some kind of built-in
function call error. See "PUBLIC EVENTS" for details, as some errors
are handled internally by this wheel.
event¶
event() allows a session to change the events emitted by a wheel without
destroying and re-creating the wheel. It accepts one or more of the events
listed in "PUBLIC EVENTS". Undefined event names disable those
events.
event() is described in more depth in POE::Wheel.
getsockname¶
getsockname() behaves like the built-in function of the same name. It
returns the local endpoint information for POE::Wheel::SocketFactory's
encapsulated listening socket.
getsockname() allows applications to determine the address and port to
which POE::Wheel::SocketFactory has bound its listening socket.
Test applications may use
getsockname() to find the server socket after
POE::Wheel::SocketFactory has bound to INADDR_ANY port 0.
Since there is no event fired immediately after a successful creation of a
listening socket, applications can use
getsockname() to verify this.
use Socket 'unpack_sockaddr_in';
my $listener = POE::Wheel::SocketFactory->new(
BindPort => 123,
SuccessEvent => 'got_client',
FailureEvent => 'listener_failed',
Reuse => 'on',
);
my ($port, $addr) = unpack_sockaddr_in($listener->getsockname);
print "Socket successfully bound\n" if $port;
ID() returns the wheel's unique ID. The ID will also be included in every
event the wheel generates. Applications can match events back to the objects
that generated them.
pause_accept¶
Applications may occasionally need to block incoming connections.
pause_accept() pauses the event watcher that triggers
accept().
New inbound connections will stack up in the socket's
listen() queue
until the queue overflows or the application calls
resume_accept().
Pausing
accept() can limit the amount of load a server generates. It's
also useful in pre-forking servers when the master process shouldn't accept
connections at all.
pause_accept() and
resume_accept() is quicker and more reliable
than dynamically destroying and re-creating a POE::Wheel::SocketFactory
object.
resume_accept¶
resume_accept() resumes the watcher that triggers
accept(). See
"pause_accept" for a more detailed discussion.
PUBLIC EVENTS¶
POE::Wheel::SocketFactory emits two public events.
SuccessEvent¶
"SuccessEvent" names an event that will be sent to the creating
session whenever a POE::Wheel::SocketFactory has created a new socket. For
connectionless sockets, it's when the socket is created. For connecting
clients, it's after the connection has been established. And for listening
servers, "SuccessEvent" is fired after each new client is accepted.
Common SuccessEvent Parameters
In all cases, $_[ARG0] holds the new socket's filehandle, and $_[ARG3] contains
the POE::Wheel::SocketFactory's ID. Other parameters vary depending on the
socket's domain and whether it's listening or connecting. See below for the
differences.
INET SuccessEvent Parameters
For INET sockets, $_[ARG1] and $_[ARG2] hold the socket's remote address and
port, respectively. The address is packed; see "inet_ntoa" in Socket
if a human-readable IPv4 address is needed. "getnameinfo" in
Socket::GetAddrInfo provides numeric addresses for IPv4 and IPv6 addresses.
sub handle_new_client {
my $accepted_socket = $_[ARG0];
my $peer_host = inet_ntoa($_[ARG1]);
print(
"Wheel $_[ARG3] accepted a connection from ",
"$peer_host port $peer_port\n"
);
spawn_connection_session($accepted_handle);
}
UNIX Client SuccessEvent Parameters
For UNIX client sockets, $_[ARG1] often (but not always) holds the server
address. Some systems cannot retrieve a UNIX socket's remote address. $_[ARG2]
is always undef for UNIX client sockets.
UNIX Server SuccessEvent Parameters
According to
Perl Cookbook, the remote address returned by
accept() on UNIX sockets is undefined, so $_[ARG1] and $_[ARG2] are
also undefined in this case.
FailureEvent¶
"FailureEvent" names the event that will be emitted when a socket
error occurs. POE::Wheel::SocketFactory handles "EAGAIN" internally,
so it doesn't count as an error.
"FailureEvent" events include the standard error event parameters:
$_[ARG0] describes which part of socket creation failed. It often holds a Perl
built-in function name.
$_[ARG1] and $_[ARG2] describe how the operation failed. They contain the
numeric and stringified versions of $!, respectively. An application cannot
merely check the global $! variable since it may change during event dispatch.
Finally, $_[ARG3] contains the ID for the POE::Wheel::SocketFactory instance
that generated the event. See "ID" and "ID" in POE::Wheel
for uses for wheel IDs.
A sample FailureEvent handler:
sub handle_failure {
my ($operation, $errnum, $errstr, $wheel_id) = @_[ARG0..ARG3];
warn "Wheel $wheel_id generated $operation error $errnum: $errstr\n";
delete $_[HEAP]{wheels}{$wheel_id}; # shut down that wheel
}
SEE ALSO¶
POE::Wheel describes the basic operations of all wheels in more depth. You need
to know this.
Socket::GetAddrInfo is required for IPv6 work. POE::Wheel::SocketFactory will
load it automatically if it's installed. SocketDomain => AF_INET6 is
required to trigger IPv6 behaviors. AF_INET6 is exported by the Socket module
on all but the oldest versions of Perl 5. If your Socket doesn't provide
AF_INET6, try installing Socket6 instead.
The SEE ALSO section in POE contains a table of contents covering the entire POE
distribution.
BUGS¶
Many (if not all) of the croak/carp/warn/die statements should fire back
"FailureEvent" instead.
SocketFactory is only tested with UNIX streams and INET sockets using the UDP
and TCP protocols. Others should work after the module's internal
configuration tables are updated. Please send patches.
AUTHORS & COPYRIGHTS¶
Please see POE for more information about authors and contributors.