From: Icarus Sparry
Subject: SBCL, udp sockets and sendto
Date: 
Message-ID: <pan.2004.12.05.08.00.24.277402@icarus.freeuk.com>
I would like to be able to send UDP datagrams to a number of destinations,
and receive replies from a single IP address and port. The normal way to
do this is to use 'sendto', but the sb-bsd-sockets library for sbcl does
not appear to support this. There have been a few requests over the years
to do this, but I have not been able to get google to show me an answer.

There are notes that imply that it would like to support socket-send, with
some optional arguments to make it choose sendto rather than send. My
attempt does not do this.

Here is my attempt. It does work, in the sense that it allows me to send
the packets. It could do with some improvements. It is written by
examining existing code, and making obvious changes. It would be nice if
it was written from a state of deep understanding.

(require :sb-grovel)
(require :SB-BSD-SOCKETS)
(cl:in-package #:SOCKINT)
(cl:declaim (cl:inline SENDTO))
(sb-grovel::define-foreign-routine ("sendto" SENDTO)
				   INTEGER
				   (SOCKFD INTEGER)
				   (BUF C-STRING)
				   (LEN INTEGER)
				   (FLAGS INTEGER)
				   (MY-ADDR (* T))
				   (ADDRLEN INTEGER))

(cl:in-package "SB-BSD-SOCKETS")
(defgeneric socket-sendto (socket buffer &rest address)
  (:documentation "Send buffer to address via socket"))

(defmethod socket-sendto ((socket socket) buffer &rest address)
  "sends the string BUFFER to ADDRESS using SOCKET"
  (with-sockaddr-for (socket sockaddr address)
    (let ((r (sockint::sendto (socket-file-descriptor socket)
			      buffer
			      (length buffer)
			      0
			      sockaddr
			      (size-of-sockaddr socket))))
      ;; put in some stuff here to handle errors
      r)))

#|
(defun tostring (l)
  "convert a vector into a string"
  (let ((r (make-string (length l))))
    (dotimes (i (length l))
      (setf (schar r i) (code-char (aref l i))))
    r))

(defvar s (make-instance 'inet-socket :type :datagram :protocol :udp))
(socket-bind s #(0 0 0 0) 32145)
(socket-sendto s "Hello" #(127 0 0 1) 45612)
|#

As it is, it only allows me to send strings (because I have specified the
BUF argument to be a C-STRING), but they are lisp strings, so do not
terminate at embedded NULL characters. It would be nice to be able to send
a vector of unsigned bytes directly.

I have read the FFI part of the SBCL manual, and looked at the recvbuf
code, but I can not see any good way of doing it.

As a question of style, should I specify that socket-sendto only takes UDP
sockets, rather than general sockets?

Is sb-grovel the right way to go, or should one use something else?

From: Christophe Rhodes
Subject: Re: SBCL, udp sockets and sendto
Date: 
Message-ID: <sq653hq8cx.fsf@cam.ac.uk>
Icarus Sparry <······@icarus.freeuk.com> writes:

> As it is, it only allows me to send strings (because I have specified the
> BUF argument to be a C-STRING), but they are lisp strings, so do not
> terminate at embedded NULL characters. It would be nice to be able to send
> a vector of unsigned bytes directly.

Without having read the rest of your post terribly carefully, as I
know nothing about sockets: this bit is wrong.  Lisp strings, when
passed as c-strings to foreign functions, do end up with a
null-terminated representation.  If you can come up with a
counterexample test case, the sbcl developers' mailing list wants to
know about it.

Christophe
From: Icarus Sparry
Subject: Re: SBCL, udp sockets and sendto
Date: 
Message-ID: <pan.2004.12.05.17.50.24.426908@icarus.freeuk.com>
On Sun, 05 Dec 2004 11:42:41 +0000, Christophe Rhodes wrote:

> Icarus Sparry <······@icarus.freeuk.com> writes:
> 
>> As it is, it only allows me to send strings (because I have specified
>> the BUF argument to be a C-STRING), but they are lisp strings, so do not
>> terminate at embedded NULL characters. It would be nice to be able to
>> send a vector of unsigned bytes directly.
> 
> Without having read the rest of your post terribly carefully, as I know
> nothing about sockets: this bit is wrong.  Lisp strings, when passed as
> c-strings to foreign functions, do end up with a null-terminated
> representation.  If you can come up with a counterexample test case, the
> sbcl developers' mailing list wants to know about it.
> 
> Christophe

I stand by my statement. It may be that a NULL has been added, but the
important thing is that the string does not terminate at an embedded NULL.
The sendto function takes a buffer and a length, in the same way that the
'write' call does.

The C interface to 'sendto' takes the following parameters

1) socket descriptor. A small integer, returned from a 'socket' call
2) buffer. A pointer to some memory address
3) length. The number of bytes to send
4) flags. A logior of some values, which my code always sets to 0
5) sockaddr. A pointer to one of a number of weird structures
6) sockaddrlen. The size of the weird structure.

It returns an integer, and does not modify any of the values passed in (C
is not call by reference), nor does it alter any memory that is pointed to
by the two pointers, nor does it keep a copy of the pointers. The 'weird
structures' holds things like the IP address and port number of where to
send the data to. The structures vary in size, e.g. if they have to hold
an IPv4 address or an IPv6 one, and carry tagging information at the start.

The sendto call provides essential functionality needed to make a UDP
server. You create a socket, get a message (using recvfrom), process it,
then send a reply using 'sendto'. The return values of 'recvfrom' gives
you the remote address.

This contrasts with a TCP server. Here you create a socket and 'listen' on
it. When something connects you call 'accept' which creates a new socket,
and future communication to the thing that just connected is via the new
socket.

The existing code in sb-bsd-sockets enables you to write a TCP client, a
UDP client or a TCP server. You can also write a UDP server that services
only one client at a time (by using the 'connect' system call).