;;;; is deferred to inet.lisp, unix.lisp, etc
(eval-when (:load-toplevel :compile-toplevel :execute)
+
(defclass socket ()
((file-descriptor :initarg :descriptor
:reader socket-file-descriptor)
:reader socket-type
:documentation "Type of the socket: :STREAM or :DATAGRAM.")
(stream))
- (:documentation "Common base class of all sockets, not ment to be
+ (:documentation "Common base class of all sockets, not meant to be
directly instantiated.")))
(defmethod print-object ((object socket) stream)
sockaddr
(size-of-sockaddr socket))))
(cond
- ((and (= fd -1) (= sockint::EAGAIN (sb-unix::get-errno)))
+ ((and (= fd -1)
+ (member (sb-unix::get-errno)
+ (list sockint::EAGAIN sockint::EINTR)))
nil)
((= fd -1) (socket-error "accept"))
(t (apply #'values
(defgeneric socket-receive (socket buffer length
&key
- oob peek waitall element-type)
- (:documentation "Read LENGTH octets from SOCKET into BUFFER (or a freshly-consed buffer if
-NIL), using recvfrom(2). If LENGTH is NIL, the length of BUFFER is
-used, so at least one of these two arguments must be non-NIL. If
-BUFFER is supplied, it had better be of an element type one octet wide.
-Returns the buffer, its length, and the address of the peer
-that sent it, as multiple values. On datagram sockets, sets MSG_TRUNC
-so that the actual packet length is returned even if the buffer was too
-small"))
+ oob peek waitall dontwait element-type)
+ (:documentation
+ "Read LENGTH octets from SOCKET into BUFFER (or a freshly-consed
+buffer if NIL), using recvfrom(2). If LENGTH is NIL, the length of
+BUFFER is used, so at least one of these two arguments must be
+non-NIL. If BUFFER is supplied, it had better be of an element type
+one octet wide. Returns the buffer, its length, and the address of the
+peer that sent it, as multiple values. On datagram sockets, sets
+MSG_TRUNC so that the actual packet length is returned even if the
+buffer was too small."))
(defmethod socket-receive ((socket socket) buffer length
&key
- oob peek waitall
+ oob peek waitall dontwait
(element-type 'character))
(with-sockaddr-for (socket sockaddr)
(let ((flags
(logior (if oob sockint::MSG-OOB 0)
(if peek sockint::MSG-PEEK 0)
(if waitall sockint::MSG-WAITALL 0)
+ (if dontwait sockint::MSG-DONTWAIT 0)
#+linux sockint::MSG-NOSIGNAL ;don't send us SIGPIPE
(if (eql (socket-type socket) :datagram)
sockint::msg-TRUNC 0))))
sockaddr
(sb-alien:addr sa-len))))
(cond
- ((and (= len -1) (= sockint::EAGAIN (sb-unix::get-errno))) nil)
+ ((and (= len -1)
+ (member (sb-unix::get-errno)
+ (list sockint::EAGAIN sockint::EINTR)))
+ nil)
((= len -1) (socket-error "recvfrom"))
(t (loop for i from 0 below len
do (setf (elt buffer i)
(bits-of-sockaddr socket sockaddr)))))))
(sb-alien:free-alien copy-buffer))))))
+(defmacro with-vector-sap ((name vector) &body body)
+ `(sb-sys:with-pinned-objects (,vector)
+ (let ((,name (sb-sys:vector-sap ,vector)))
+ ,@body)))
+
+(defgeneric socket-send (socket buffer length
+ &key
+ address
+ external-format
+ oob eor dontroute dontwait nosignal
+ #+linux confirm #+linux more)
+ (:documentation
+ "Send LENGTH octets from BUFFER into SOCKET, using sendto(2). If BUFFER
+is a string, it will converted to octets according to EXTERNAL-FORMAT. If
+LENGTH is NIL, the length of the octet buffer is used. The format of ADDRESS
+depends on the socket type (for example for INET domain sockets it would
+be a list of an IP address and a port). If no socket address is provided,
+send(2) will be called instead. Returns the number of octets written."))
+
+(defmethod socket-send ((socket socket) buffer length
+ &key
+ address
+ (external-format :default)
+ oob eor dontroute dontwait nosignal
+ #+linux confirm #+linux more)
+ (let* ((flags
+ (logior (if oob sockint::MSG-OOB 0)
+ (if eor sockint::MSG-EOR 0)
+ (if dontroute sockint::MSG-DONTROUTE 0)
+ (if dontwait sockint::MSG-DONTWAIT 0)
+ (if nosignal sockint::MSG-NOSIGNAL 0)
+ #+linux (if confirm sockint::MSG-CONFIRM 0)
+ #+linux (if more sockint::MSG-MORE 0)))
+ (buffer (etypecase buffer
+ (string
+ (sb-ext:string-to-octets buffer
+ :external-format external-format
+ :null-terminate nil))
+ ((simple-array (unsigned-byte 8))
+ buffer)
+ ((array (unsigned-byte 8))
+ (make-array (length buffer)
+ :element-type '(unsigned-byte 8)
+ :initial-contents buffer))))
+ (len (with-vector-sap (buffer-sap buffer)
+ (unless length
+ (setf length (length buffer)))
+ (if address
+ (with-sockaddr-for (socket sockaddr address)
+ (sb-alien:with-alien ((sa-len sockint::socklen-t
+ (size-of-sockaddr socket)))
+ (sockint::sendto (socket-file-descriptor socket)
+ buffer-sap
+ length
+ flags
+ sockaddr
+ sa-len)))
+ (sockint::send (socket-file-descriptor socket)
+ buffer-sap
+ length
+ flags)))))
+ (cond
+ ((and (= len -1)
+ (member (sb-unix::get-errno)
+ (list sockint::EAGAIN sockint::EINTR)))
+ nil)
+ ((= len -1)
+ (socket-error "sendto"))
+ (t len))))
+
(defgeneric socket-listen (socket backlog)
(:documentation "Mark SOCKET as willing to accept incoming connections. BACKLOG
defines the maximum length that the queue of pending connections may
nil))))))
-(defgeneric socket-make-stream (socket &rest args)
+(defgeneric socket-make-stream (socket &key input output
+ element-type external-format
+ buffering
+ timeout)
(:documentation "Find or create a STREAM that can be used for IO on
-SOCKET (which must be connected). ARGS are passed onto
-SB-SYS:MAKE-FD-STREAM."))
-
-(defmethod socket-make-stream ((socket socket) &rest args)
+SOCKET \(which must be connected\). Specify whether the stream is for
+INPUT, OUTPUT, or both \(it is an error to specify neither\). ELEMENT-TYPE
+and EXTERNAL-FORMAT are as per OPEN. TIMEOUT specifies a read timeout
+for the stream."))
+
+(defmethod socket-make-stream ((socket socket)
+ &key input output
+ (element-type 'character)
+ (buffering :full)
+ (external-format :default)
+ timeout)
+ "Default method for SOCKET objects. An ELEMENT-TYPE of :DEFAULT
+will construct a bivalent stream. Acceptable values for BUFFERING
+are :FULL, :LINE and :NONE. Streams will have no TIMEOUT
+by default.
+ The stream for SOCKET will be cached, and a second invocation of this
+method will return the same stream. This may lead to oddities if this
+function is invoked with inconsistent arguments \(e.g., one might request
+an input stream and get an output stream in response\)."
(let ((stream
(and (slot-boundp socket 'stream) (slot-value socket 'stream))))
(unless stream
- (setf stream (apply #'sb-sys:make-fd-stream
- (socket-file-descriptor socket)
- :name "a constant string"
- :dual-channel-p t
- args))
+ (setf stream (sb-sys:make-fd-stream
+ (socket-file-descriptor socket)
+ :name "a socket"
+ :dual-channel-p t
+ :input input
+ :output output
+ :element-type element-type
+ :buffering buffering
+ :external-format external-format
+ :timeout timeout)))
(setf (slot-value socket 'stream) stream)
- (sb-ext:cancel-finalization socket))
+ (sb-ext:cancel-finalization socket)
stream))
\f