Posted to tcl by Stu at Wed Aug 22 23:32:54 GMT 2012view pretty
TIP: Title: Datagram I/O in Tcl Author: Alexandre Ferrieux, Colin McCormack Type: Project Tcl-Version: 8.7 Created: 17-Aug-2012 ~ Abstract This TIP adds support for Datagram I/O in Tcl (starting with UDP), with a pure event approach to packet reception (as opposed to hijacking the existing channel infrastructure). ~ Rationale UDP support is a long-awaited feature in Tcl. Some extensions have traditionally supported it through the channel abstraction. This is of course doable, but there is a nonnegligible cost in both complexity and performance due to the impedence mismatch between the "fluid" model behind channels and the "granular" world of packets or datagrams (where boundaries are significant). Another discrepancy is the existence of (per-packet) metadata (like the source address and port of an incoming UDP packet), which does not fit nicely in the (per-connection) options of the channel API (via fconfigure). Once this mismatch is acknowledged, it is easy to identify a better home for packet I/O in Tcl: let it be a direct event source (for the receive side), just like Tk or Snack. Indeed, hooking a callback for packet reception is a natural fit with Tcl's event-driven tradition, while preserving packet boundaries and easily exposing per-packet metadata. Sending is trivially handled by a direct per-packet call (but not disguised as a [puts]). Again, this naturally allows for boundary preservation and metadata specification. The script API put forth in this TIP is also designed for easy generalization to other kinds of packet I/O. Though only UDP is supported in the reference implementation, other protocols may be registered, and selected with the ''protocol'' paramater. Future additions may include: - raw IP sockets - USB - Bluetooth ~ Overall Specification The new '''dgram''' command creates a "datagram endpoint" of the given protocol: > '''dgram''' ''protocol'' ?''options''? Following the traditions of Tk and Snack, it returns a Tcl command, which takes subcommands implementing the needed verbs. An endpoint is thus created by: > set d ['''dgram''' ''protocol'' ?-''option'' ''value''? ...] The returned command lives by default in the ::tcl::dgram namespace; however, the generic ''-name'' option allows it to take a (possibly fully qualified) user-provided name: > dgram ''protocol'' -name foo ... creates (and returns) the command ::foo to hold the endpoint. Once created, its configuration can be tweaked by: > $d configure -''option'' ''value'' and retrieved with > set value [$d configure -''option''] To close the endpoint, use: > $d close or, as in Tk, destroy the command: > rename $d {} or the whole namespace. Other verbs, and the detailed list of options, are protocol-specific. From now on, the specification concentrates on the UDP case. The rationale for *not* generalizing much is the feeling that the costs of parameter variability would largely outweigh the tiny amount of code sharing. ~ The UDP case With UDP, addresses and ports may only be specified on creation: > set d ['''dgram udp''' ?-localaddr ''address''? ?-localport ''port''?] They default to INADDR_ANY and 0, respectively. In case of port 0, after creation of the endpoint the actual port chosen by the OS can be retrieved with [$d configure]: > set d [dgram udp] > puts "Local port is: [$d configure -localport]" One can also specify remote address and port, meaning to connect() the underlying UDP socket: > set d ['''dgram udp''' ?-remoteaddr ''address'' -remoteport ''port''?] These options suffer no default, and must be simultaneously present or absent. The semantics, as is well known, is to tell the kernel (a) to forbid sending to any other destination, and (b) to discard all incoming packets sent by another source. All the other options may be specified on creation or through [configure]. Sending is done with, unsurprisingly, the 'send' verb: > $d '''send''' ''payload'' ?''destaddr'' ''destport''? Its blocking semantics is that of the underlying protocol: it typically returns immediately, though the hardware may buffer the data for some time, and delivery is not guaranteed. The destination parameters can, unsurprisingly, be omitted if the endpoint has been created with the -remoteaddr/port options (connected mode). Receiving is done with the -listener callback: > $d '''configure -listener''' ''command_prefix'' Subsequently, when an incoming packet hits Tcl in the event loop, the ''command_prefix'' is called with the endpoint identifier, payload and metadata: > ''command_prefix'' $d ''payload'' ''metadata_dict'' where ''payload'' is the byte array of the UDP payload, and ''metadata_dict'' is a dict containing at least three options: > '''-remoteaddr''' ''address'' > '''-remoteport''' ''port'' > '''-localaddr''' ''address'' When ''command_prefix'' is the empty string, the notifier gives up interest in the underlying UDP socket; this allows to keep the port bound while letting the OS buffer any incoming packets (up to a configurable limit) without any script-level handling, while leaving the event loop active. This is similar to setting a fileevent to {}. ~ Advanced UDP options (TODO: multicast, ttl, buffersize ...) ~ Copyright This document has been placed in the public domain.