A Server.t
represents a TCP server listening on a socket.
include sig ... end
val sexp_of_t : ('address ‑> Base.Sexp.t) ‑> ('listening_on ‑> Base.Sexp.t) ‑> ('address, 'listening_on) t ‑> Base.Sexp.t
include sig ... end
val sexp_of_inet : inet ‑> Base.Sexp.t
include sig ... end
val sexp_of_unix : unix ‑> Base.Sexp.t
val invariant : (_, _) t ‑> unit
val listening_on : (_, 'listening_on) t ‑> 'listening_on
val listening_on_address : ('address, _) t ‑> 'address
val close : ?close_existing_connections:bool ‑> (_, _) t ‑> unit Async_extra__.Import.Deferred.t
close t
starts closing the listening socket, and returns a deferred that becomes
determined after Fd.close_finished fd
on the socket's fd
. It is guaranteed that
t
's client handler will never be called after close t
. It is ok to call close
multiple times on the same t
; calls subsequent to the initial call will have no
effect, but will return the same deferred as the original call.
With ~close_existing_connections:true
, close
closes the sockets of all existing
connections. close
does not (and cannot) stop the handlers handling the
connections, but they will of course be unable to write to or read from the socket.
The result of close
becomes determined when all the socket file descriptors are
closed and the socket's fd
is closed.
val close_finished : (_, _) t ‑> unit Async_extra__.Import.Deferred.t
close_finished
becomes determined after Fd.close_finished fd
on the socket's
fd
, i.e., the same deferred that close
returns. close_finished
differs from
close
in that it does not have the side effect of initiating a close.
type ('address, 'listening_on, 'callback) create_options
= ?max_connections:int ‑> ?max_accepts_per_batch:int ‑> ?backlog:int ‑> ?socket:([ `Unconnected ], 'address) Async_extra__.Import.Socket.t ‑> on_handler_error:[ `Raise | `Ignore | `Call of 'address ‑> exn ‑> unit ] ‑> ('address, 'listening_on) Where_to_listen.t ‑> 'callback ‑> ('address, 'listening_on) t Async_extra__.Import.Deferred.t
Options for server creation:
backlog
is the number of clients that can have a connection pending, as with
Unix.listen. Additional connections may be rejected, ignored, or enqueued
anyway, depending on OS, version, and configuration.
max_connections
is the maximum number of clients that can be connected
simultaneously. The server will not call accept
unless the number of clients is
less than max_connections
, although of course potential clients can have
a connection pending.
max_accepts_per_batch
is the maximum number of connections that the server will
retrieve per blocking Unix.accept call. Servers that must handle a large number
of connections tend to observe a stall in connection accept rates when under heavy
load. Increasing max_accepts_per_batch
will ameliorate this effect, increasing
connection accept rates and overall throughput at the cost of increased contention
for resources amongst connections. Servers that are under light load or ones that
only service a small number of connections at a time should see little to no
difference in behavior for different values of max_accepts_per_branch
.
Supplying socket
causes the server to use socket
rather than create a new
socket. In this usage, creation does not set Socket.Opt.reuseaddr
to true
; if
you want that, you must set reuseaddr
before creation.
on_handler_error
determines what happens if the handler throws an exception. If an
exception is raised by on_handler_error (either explicitly via `Raise
, or in the
closure passed to `Call
) no further connections will be accepted.
val create_sock : ('address, 'listening_on, 'address ‑> ([ `Active ], 'address) Async_extra__.Import.Socket.t ‑> unit Async_extra__.Import.Deferred.t) create_options
create_sock where_to_listen handler
starts a server listening to a socket as
specified by where_to_listen
. It returns a server once the socket is ready to
accept connections. The server calls handler address socket
for each client that
connects. If the deferred returned by handler
is ever determined, or handler
raises an exception, then socket
is closed.
The server will stop accepting and close the listening socket when an error handler
raises (either via `Raise
or `Call f
where f
raises), or if close
is
called.
val create : ?buffer_age_limit:Async_extra__.Import.Writer.buffer_age_limit ‑> ('address, 'listening_on, 'address ‑> Async_extra__.Import.Reader.t ‑> Async_extra__.Import.Writer.t ‑> unit Async_extra__.Import.Deferred.t) create_options
create where_to_listen handler
is a convenience wrapper around create_sock
that
pass a reader and writer for the client socket to the callback. If the deferred
returned by handler
is ever determined, or handler
raises an exception, then the
reader and writer are closed.
buffer_age_limit
passes on to the underlying writer option of the same name.
val listening_socket : ('address, _) t ‑> ([ `Passive ], 'address) Async_extra__.Import.Socket.t
listening_socket t
accesses the listening socket, which should be used with care.
An anticipated use is with Udp.bind_to_interface_exn. Accepting connections on
the socket directly will circumvent max_connections
and on_handler_error
,
however, and is not recommended.
val num_connections : (_, _) t ‑> int
val set_drop_incoming_connections : (_, _) t ‑> bool ‑> unit
set_drop_incoming_connections
configures whether each incoming connection will be
immediately dropped or not. This is a hack to effectively get a "pause listening"
feature. We can't reliably use backlog
and max_num_connections
to reject
incoming connections. For example, if we reach max_num_connections
, we won't call
accept
but OS might still establish TCP connection. The client will see the
connection as established but no data will be exchanged and we'd have to rely on TCP
retransmit timeouts to close the connection. In many cases we would prefer to
accept and then immediately close the connection. This is an intermediate solution
until we do a more principled solution (but much more complicated) when we close the
listening socket and then later bind
and listen
again when we decide to unpause
the server.
drop_incoming_connections
is set to false.