TCP handles are used to represent both TCP streams and servers.
include module type of Stream with type t := t
include module type of Handle with type t := t
val close : t -> Int_result.unit
Handles are closed automatically, if they are not longer referenced from the OCaml heap. Nevertheless, you should nearly always close them with
- if they wrap a file descriptor, you will sooner or later run out of file descriptors. The OCaml garbage collector doesn't give any guarantee, when orphaned memory blocks are removed.
- you might have registered some repeatedly called action (e.g. timeout, read_start,...), that prevent that all references get removed from the OCaml heap.
However, it's safe to write code in this manner:
let s = Uwt.Tcp.init () in let c = Uwt.Tcp.init () in Uwt.Tcp.nodelay s false; Uwt.Tcp.simultaneous_accepts true; if foobar () then (* no file descriptor yet assigned, no need to worry about exceptions inside foobar,... *) Lwt.return_unit (* no need to close *) else ...
If you want - for whatever reason - keep a file descriptor open for the whole lifetime of your process, remember to keep a reference to its handle.
val close_noerr : t -> unit
close_noerr return immediately (there are no useful error messages, beside perhaps a notice, that you've already closed that handle).
close_wait is only useful, if you intend to wait until all concurrent write and read threads related to this handle are canceled.
val is_active : t -> bool
Returns non-zero if the handle is active, zero if it's inactive. What "active" means depends on the type of handle:
Async.thandle is always active and cannot be deactivated, except by closing it with uv_close().
Udp.t, etc. handle - basically any handle that deals with i/o - is active when it is doing something that involves i/o, like reading, writing, connecting, accepting new connections, etc.
Rule of thumb: if a handle of type
Uwt.Foo.t has a uv_foo_start() function, then it's active from the moment that function is called. Likewise, uv_foo_stop() deactivates the handle again.
val ref' : t -> unit
Reference the given handle. References are idempotent, that is, if a handle is already referenced calling this function again will have no effect.
val unref : t -> unit
Un-reference the given handle. References are idempotent, that is, if a handle is not referenced calling this function again will have no effect.
val has_ref : t -> bool
Returns non-zero if the handle is referenced, zero otherwise.
val is_readable : t -> bool
val is_writable : t -> bool
val read_start : t -> cb:(Bytes.t uv_result -> unit) -> Int_result.unit
Read data from an incoming stream. The
~cb will be made several times until there is no more data to read or
read_stop is called.
val read_stop : t -> Int_result.unit
Stop reading data from the stream.
val read_stop_exn : t -> unit
There is currently no
uv_read function in libuv, just
uv_read_stop. This is a wrapper for your convenience. It calls read_stop internally, if you don't continue with reading immediately. Zero result indicates EOF.
In future libuv versions, there might be
uv_try_read functions (it was discussed several times). If these changes got merged,
Stream.read will wrap them - even if there will be small semantic differences.
It is currently not possible to start several read threads in parallel, you must serialize the requests manually. In the following example
t2 will fail with EBUSY:
let t1 = Uwt.Stream.read t ~buf:buf1 in let t2 = Uwt.Stream.read t ~buf:buf2 in (* ... *)
Calling the function with
~len:0 has a dubious, system dependent semantic.
val write_queue_size : t -> int
Returns the amount of queued bytes waiting to be sent
val try_write : ?pos:int -> ?len:int -> t -> buf:bytes -> Int_result.int
Write data to stream, but won't queue a write request if it can't be completed immediately.
val try_write_ba : ?pos:int -> ?len:int -> t -> buf:buf -> Int_result.int
val try_write_string : ?pos:int -> ?len:int -> t -> buf:string -> Int_result.int
write is eager - like the counterparts inside
Lwt_unix. It first calls
try_write internally to check if it can return immediately (without the overhead of creating a sleeping thread and waking it up later). If it can't write everything instantly, it will call
write_raw is exposed here mainly in order to write unit tests for it. But you can also use it, if you your
~buf is very large or you know for another reason, that try_write will fail.
val try_writev : t -> Iovec_write.t list -> Int_result.int
Windows doesn't support writing multiple buffers with a single syscall for some HANDLEs (e.g. it's supported for tcp handles, but not pipes). uwt then writes the buffers one by one
If the number of buffers is greater than IOV_MAX, libuv already contains the necessary workarounds
val writev : t -> Iovec_write.t list -> unit Lwt.t
See comment to
try_writev! This function will fail with
Unix.EOPNOTSUPP on Windows for e.g. pipe handles
val writev_emul : t -> Iovec_write.t list -> unit Lwt.t
writev, but if passing several buffers at once is not supported by the OS, the buffers will be written one by one. Please note that as a consequence you should not start several
writev_emul threads in parallel. The writing order would be surprising in this case. If you don't use windows, this function is identic to
val writev_raw : t -> Iovec_write.t list -> unit Lwt.t
val listen : t -> max:int -> cb:(t -> Int_result.unit -> unit) -> Int_result.unit
Start listening for incoming connections.
~max indicates the number of connections the kernel might queue, same as
listen(2). When a new incoming connection is received
~cb is called.
val listen_exn : t -> max:int -> cb:(t -> Int_result.unit -> unit) -> unit
Shutdown the outgoing (write) side of a duplex stream. It waits for pending write requests to complete.
val set_blocking : t -> bool -> Int_result.unit
Just don't use this function. It will only cause trouble.
include module type of Handle_ext with type t := t
val get_send_buffer_size : t -> Int_result.int
Gets the size of the send buffer that the operating system uses for the socket.
val get_send_buffer_size_exn : t -> int
val get_recv_buffer_size : t -> Int_result.int
Gets the size of the receive buffer that the operating system uses for the socket.
val get_recv_buffer_size_exn : t -> int
val set_send_buffer_size : t -> int -> Int_result.unit
Sets the size of the send buffer that the operating system uses for the socket.
val set_send_buffer_size_exn : t -> int -> unit
val set_recv_buffer_size : t -> int -> Int_result.unit
Sets the size of the receive buffer that the operating system uses for the socket.
val set_recv_buffer_size_exn : t -> int -> unit
include module type of Handle_fileno with type t := t
val fileno : t -> Unix.file_descr uv_result
val fileno_exn : t -> Unix.file_descr
uv_tcp_init_ex. A socket of the given type will be created immediately instead of lazy (as with
val init_ipv4_exn : unit -> t
val init_ipv6_exn : unit -> t
val opentcp : Unix.file_descr -> t uv_result
See comment to
val opentcp_exn : Unix.file_descr -> t
val bind : ?mode:mode list -> t -> addr:sockaddr -> unit -> Int_result.unit
val nodelay : t -> bool -> Int_result.unit
Enable TCP_NODELAY, which disables Nagle's algorithm.
val nodelay_exn : t -> bool -> unit
val enable_keepalive : t -> int -> Int_result.unit
enable_keepalive tcp delay enables keep-alive, the delay is the initial delay in seconds
val enable_keepalive_exn : t -> int -> unit
val disable_keepalive : t -> Int_result.unit
val disable_keepalive_exn : t -> unit
val simultaneous_accepts : t -> bool -> Int_result.unit
Enable / disable simultaneous asynchronous accept requests that are queued by the operating system when listening for new TCP connections.
This setting is used to tune a TCP server for the desired performance. Having simultaneous accepts can significantly improve the rate of accepting connections (which is why it is enabled by default) but may lead to uneven load distribution in multi-process setups.
val simultaneous_accepts_exn : t -> bool -> unit
initializes a new client, accepts and returns it.
This call is used in conjunction with
listen to accept incoming connections. Call this function after receiving a
listen callback to accept the connection.
listen callback is called it is guaranteed that this function will complete successfully the first time. If you attempt to use it more than once, it may fail. It is suggested to only call this function once per
listen callback call.
See comments to
val with_open : Unix.file_descr -> (t -> 'a Lwt.t) -> 'a Lwt.t
val accept_raw : server:t -> client:t -> Int_result.unit