chickadee » socket » socket-i/o-ports

socket-i/o-ports soprocedure

Constructs an input port I and an output port O associated with the connected socket so, returning (values I O). This procedure works on both stream and datagram sockets.

To enable output buffering on stream socket ports, see the parameter socket-send-buffer-size. Setting it to a value of 1024 bytes is reasonable.

Below is a fairly involved explanation of input and output buffering and chunking, as well as recommendations for use with datagrams.

socket-i/o-ports is normally used with stream sockets. Input data is always buffered in a buffer of size (socket-receive-buffer-size). Whenever the buffer is empty, socket-receive! is called once to read up to that many bytes. Output data is sent with socket-send-all, so it may be divided into chunks of size (socket-send-size). If output is unbuffered, socket-send-all is called as soon as data is written to the port.

If output is buffered by setting (socket-send-buffer-size) to N, then N characters are buffered before sending the data. Note that only multiples of the buffer size are sent (any overage is kept in the buffer). For example, if the buffer can hold 512 bytes and contains 500 bytes, writing 526 more bytes brings the total unsent size to 1026 bytes. 1024 bytes (2 blocks) are written out in a single call to socket-send-all and the last 2 bytes are retained in the buffer.

When the output buffer size and chunk size are both set, it is recommended to make the chunk size a multiple of the buffer size; for example, buffer size = 1024, chunk size = 8192. If not aligned, extraneous small packets may be sent. Buffer size is almost always less than or equal to chunk size. If greater, it should be a multiple of the chunk size. Using powers of 2 for both satisfies all cases.

Note that socket-i/o-ports can also be used to create ports on connected datagram sockets. Input is always buffered and a single chunk of up to size (socket-receive-buffer-size) is read into the buffer whenever the buffer is empty. (If the datagram is smaller than the buffer, repeated reads are not performed; rather, the buffer is used until exhausted again. Any datagram exceeding the buffer size will be truncated.) Output is divided into chunks of size (socket-send-size), as in socket-send-all -- this is useful for placing a maximum cap on datagram size transmitted. Finally, output buffering may be enabled, which behaves the same as with TCP ports; characters are buffered and sent in blocks of (socket-send-buffer-size) bytes. Again, to avoid excessive transmission, the chunk size should be a multiple of the buffer size or vice versa.

For example, to accept up to 4K datagrams, buffer 128 characters at a time and send 128-, 256-, 384- or 512-byte datagrams at a time:

(parameterize ((socket-receive-buffer-size 4096)
               (socket-send-buffer-size 128)
               (socket-send-size 512))
  (define so (socket-connect/ai
              (address-information host port type: sock/dgram)))
  (define-values (i o) (socket-i/o-ports so))
  ;; a useful example would be nice