[usocket-cvs] r635 - in usocket/trunk: . backend test

Chun Tian (binghe) ctian at common-lisp.net
Fri Apr 1 16:52:19 UTC 2011


Author: ctian
Date: Fri Apr  1 12:52:18 2011
New Revision: 635

Log:
Merge all changes from branch 0.5.x (r616-r634) before tagging the 0.5.1

Modified:
   usocket/trunk/CHANGES
   usocket/trunk/README
   usocket/trunk/backend/abcl.lisp
   usocket/trunk/backend/allegro.lisp
   usocket/trunk/backend/clisp.lisp
   usocket/trunk/backend/cmucl.lisp
   usocket/trunk/backend/lispworks.lisp
   usocket/trunk/backend/sbcl.lisp
   usocket/trunk/backend/scl.lisp
   usocket/trunk/test/test-usocket.lisp
   usocket/trunk/usocket-test.asd
   usocket/trunk/usocket.lisp

Modified: usocket/trunk/CHANGES
==============================================================================
--- usocket/trunk/CHANGES	(original)
+++ usocket/trunk/CHANGES	Fri Apr  1 12:52:18 2011
@@ -8,20 +8,21 @@
 
 0.5.1:
 
-* Bugfix: Fixed wrong macro expansions of {IP|PORT}-{FROM|TO}-OCTET-BUFFER functions!
+* New feature: [CLISP] UDP (Datagram) support based on FFI (Win/Mac/Linux), no RAWSOCK needed.
+* Enhancement: SOCKET-SERVER return a second value (socket) when calling in new-thread mode.
+* Enhancement: [CLISP] Full support of DNS helper functions (GET-HOST-BY-NAME, ...) added.
+* Enhancement: [CLISP] Better network error type detection based on OS error code.
+* Enhancement: [LispWorks] Better network error type detection based on OS error code.
+* Bugfix: Fixed wrong macro expansions of {IP|PORT}-{FROM|TO}-OCTET-BUFFER functions (since 0.4.0)
 * Bugfix: SOCKET-CONNECT didn't set CONNECTED-P for datagram usockets on most backends.
 * Bugfix: [SBCL] Fixes for "SBCL/Win32: finalizer problem, etc", by Anton Kovalenko <anton at sw4me.com>
 * Bugfix: [SBCL] Fixed SOCKET-SERVER (UDP) on SBCL due to a issue in SOCKET-CONNECT when HOST is NIL.
 * Bugfix: [SBCL] SOCKET-CONNECT's TIMEOUT argument now works as a "connection timeout".
 * Bugfix: [CMUCL] Fixed SOCKET-SEND on unconnected usockets under Unicode version of CMUCL.
-* Bugfix: [LispWorks] Better network error type detection on LispWorks.
-* Bugfix: [CLISP] Fixed UDP (Datagram) support (RAWSOCK version), confirmed by CL-NET-SNMP.
-* Enhancement: SOCKET-SERVER return a second value (socket) when calling in new-thread mode.
-* Enhancement: [CLISP] Full support of DNS helper functions (GET-HOST-BY-NAME, ...) added.
+* Bugfix: [CLISP] Fixed and confirmed UDP (Datagram) support (RAWSOCK version).
 
 [TODO]
 
-* New feature: CLISP support UDP without RAWSOCK (using FFI interface)
 * New feature: CLISP support some advanced TCP features which CLISP's SOCKET interface not provide
 * New feature: Macintosh Common Lisp (MCL) support Datagram sockets (UDP)
 

Modified: usocket/trunk/README
==============================================================================
--- usocket/trunk/README	(original)
+++ usocket/trunk/README	Fri Apr  1 12:52:18 2011
@@ -1,6 +1,5 @@
-                                                                -*- text -*-
-
-$Id$
+;;;; -*- Mode: Text -*-
+;;;; $Id$
 
 Content
 =======
@@ -164,14 +163,3 @@
   reuse the FFI code on all platforms (including Windows).  This benefit
   currently outweighs the additional requirement. (hey, it's *Embeddable*
   Common Lisp, so, you probably wanted to embed it all along, right?)
-
-- LispWorks has a bug(?) in wait-for-input-streams which make it
-  unsuited for waiting for input on stream socket servers, making it
-  necessary to resort to different  means.  With the absence of notice-fd
-  on Windows, that currenty leaves Windows unsupported.
-
-- SBCL can't use select() on Windows because it would mean porting
-  the FD_* macros and the select structures which I'm not sure
-  is the right way yet (if I need to write custom Win32 code anyway...)
-  The alternative is to use WSAEventSelect() and friends (which don't
-  have a limited number of sockets).

Modified: usocket/trunk/backend/abcl.lisp
==============================================================================
--- usocket/trunk/backend/abcl.lisp	(original)
+++ usocket/trunk/backend/abcl.lisp	Fri Apr  1 12:52:18 2011
@@ -343,6 +343,10 @@
 ;;; TODO: return-host and return-port cannot be get ...
 (defmethod socket-receive ((usocket datagram-usocket) buffer length
 			   &key (element-type '(unsigned-byte 8)))
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+		   (integer 0)                          ; size
+		   (unsigned-byte 32)                   ; host
+		   (unsigned-byte 16)))                 ; port
   (let* ((socket (socket usocket))
 	 (real-length (or length +max-datagram-packet-size+))
 	 (byte-array (jnew-array $*byte real-length))

Modified: usocket/trunk/backend/allegro.lisp
==============================================================================
--- usocket/trunk/backend/allegro.lisp	(original)
+++ usocket/trunk/backend/allegro.lisp	Fri Apr  1 12:52:18 2011
@@ -155,6 +155,10 @@
       (socket:send-to s buffer length :remote-host host :remote-port port))))
 
 (defmethod socket-receive ((socket datagram-usocket) buffer length &key)
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+		   (integer 0)                          ; size
+		   (unsigned-byte 32)                   ; host
+		   (unsigned-byte 16)))                 ; port
   (with-mapped-conditions (socket)
     (let ((s (socket socket)))
       (socket:receive-from s length :buffer buffer :extract t))))

Modified: usocket/trunk/backend/clisp.lisp
==============================================================================
--- usocket/trunk/backend/clisp.lisp	(original)
+++ usocket/trunk/backend/clisp.lisp	Fri Apr  1 12:52:18 2011
@@ -44,40 +44,80 @@
       (mapcar #'host-to-vector-quad
               (posix:hostent-addr-list hostent)))))
 
-#+win32
-(defun remap-maybe-for-win32 (z)
-  (mapcar #'(lambda (x)
-              (cons (mapcar #'(lambda (y)
-                                (+ 10000 y))
-                            (car x))
-                    (cdr x)))
-          z))
-
+;; Format: ((UNIX Windows) . CONDITION)
 (defparameter +clisp-error-map+
-  #+win32
-  (append (remap-maybe-for-win32 +unix-errno-condition-map+)
-          (remap-maybe-for-win32 +unix-errno-error-map+))
   #-win32
-  (append +unix-errno-condition-map+
-          +unix-errno-error-map+))
+  `((:EADDRINUSE . address-in-use-error)
+    (:EADDRNOTAVAIL . address-not-available-error)
+    (:EBADF . bad-file-descriptor-error)
+    (:ECONNREFUSED  . connection-refused-error)
+    (:ECONNRESET . connection-reset-error)
+    (:ECONNABORTED . connection-aborted-error)
+    (:EINVAL . invalid-argument-error)
+    (:ENOBUFS . no-buffers-error)
+    (:ENOMEM . out-of-memory-error)
+    (:ENOTSUP . operation-not-supported-error)
+    (:EPERM . operation-not-permitted-error)
+    (:EPROTONOSUPPORT . protocol-not-supported-error)
+    (:ESOCKTNOSUPPORT . socket-type-not-supported-error)
+    (:ENETUNREACH . network-unreachable-error)
+    (:ENETDOWN . network-down-error)
+    (:ENETRESET . network-reset-error)
+    (:ESHUTDOWN . already-shutdown-error)
+    (:ETIMEDOUT . timeout-error)
+    (:EHOSTDOWN . host-down-error)
+    (:EHOSTUNREACH . host-unreachable-error))
+  #+win32
+  `((:WSAEADDRINUSE . address-in-use-error)
+    (:WSAEADDRNOTAVAIL . address-not-available-error)
+    (:WSAEBADF . bad-file-descriptor-error)
+    (:WSAECONNREFUSED  . connection-refused-error)
+    (:WSAECONNRESET . connection-reset-error)
+    (:WSAECONNABORTED . connection-aborted-error)
+    (:WSAEINVAL . invalid-argument-error)
+    (:WSAENOBUFS . no-buffers-error)
+    (:WSAENOMEM . out-of-memory-error)
+    (:WSAENOTSUP . operation-not-supported-error)
+    (:WSAEPERM . operation-not-permitted-error)
+    (:WSAEPROTONOSUPPORT . protocol-not-supported-error)
+    (:WSAESOCKTNOSUPPORT . socket-type-not-supported-error)
+    (:WSAENETUNREACH . network-unreachable-error)
+    (:WSAENETDOWN . network-down-error)
+    (:WSAENETRESET . network-reset-error)
+    (:WSAESHUTDOWN . already-shutdown-error)
+    (:WSAETIMEDOUT . timeout-error)
+    (:WSAEHOSTDOWN . host-down-error)
+    (:WSAEHOSTUNREACH . host-unreachable-error)))
 
 (defun handle-condition (condition &optional (socket nil))
   "Dispatch correct usocket condition."
-  (typecase condition
-    (system::simple-os-error
-       (let ((usock-err
-              (cdr (assoc (car (simple-condition-format-arguments condition))
-                          +clisp-error-map+ :test #'member))))
-         (when usock-err ;; don't claim the error if we don't know
-	   ;; it's actually a socket error ...
-             (if (subtypep usock-err 'error)
-                 (error usock-err :socket socket)
-               (signal usock-err :socket socket)))))))
+  (let (error-keyword error-string)
+    (typecase condition
+      (system::simple-os-error
+       (let ((errno (car (simple-condition-format-arguments condition))))
+	 (setq error-keyword (os:errno errno)
+	       error-string (os:strerror errno))))
+      (simple-error
+       (let ((keyword
+	      (car (simple-condition-format-arguments condition))))
+	 (setq error-keyword keyword
+	       error-string (os:strerror keyword))))
+      (error (error 'unknown-error :real-error condition))
+      (condition (signal 'unknown-condition :real-condition condition)))
+    (when error-keyword
+      (let ((usocket-error
+	     (cdr (assoc error-keyword +clisp-error-map+ :test #'eq))))
+	(if usocket-error
+	    (if (subtypep usocket-error 'error)
+		(error usocket-error :socket socket)
+		(signal usocket-error :socket socket))
+	    (error "Unknown OS error: ~A (~A)" error-string error-keyword))))))
 
 (defun socket-connect (host port &key (protocol :stream) (element-type 'character)
                        timeout deadline (nodelay t nodelay-specified)
                        local-host local-port)
-  (declare (ignore nodelay))
+  (declare (ignore nodelay)
+	   (ignorable timeout local-host local-port))
   (when deadline (unsupported 'deadline 'socket-connect))
   (when nodelay-specified (unsupported 'nodelay 'socket-connect))
   (case protocol
@@ -97,13 +137,11 @@
        (make-stream-socket :socket socket
 			   :stream socket))) ;; the socket is a stream too
     (:datagram
-     #+rawsock
+     #+(or rawsock ffi)
      (socket-create-datagram (or local-port *auto-port*)
 			     :local-host (or local-host *wildcard-host*)
-			     :remote-host host
+			     :remote-host (and host (host-to-vector-quad host))
 			     :remote-port port)
-     #+(and ffi (not rawsock))
-     ()
      #-(or rawsock ffi)
      (unsupported '(protocol :datagram) 'socket-connect))))
 
@@ -147,7 +185,7 @@
      (remove-waiter (wait-list usocket) usocket))
   (socket:socket-server-close (socket usocket)))
 
-(defmethod get-local-name ((usocket usocket))
+(defmethod get-local-name ((usocket stream-usocket))
   (multiple-value-bind
       (address port)
       (socket:socket-stream-local (socket usocket) t)
@@ -162,13 +200,13 @@
 (defmethod get-local-address ((usocket usocket))
   (nth-value 0 (get-local-name usocket)))
 
-(defmethod get-peer-address ((usocket stream-usocket))
+(defmethod get-peer-address ((usocket usocket))
   (nth-value 0 (get-peer-name usocket)))
 
 (defmethod get-local-port ((usocket usocket))
   (nth-value 1 (get-local-name usocket)))
 
-(defmethod get-peer-port ((usocket stream-usocket))
+(defmethod get-peer-port ((usocket usocket))
   (nth-value 1 (get-peer-name usocket)))
 
 (defun %setup-wait-list (wait-list)
@@ -235,17 +273,22 @@
     "Returns the buffer, the number of octets copied into the buffer (received)
 and the address of the sender as values."
     (let* ((sock (socket socket))
-           (sockaddr (unless (connected-p socket)
-                       (rawsock:make-sockaddr :inet)))
-           (rv (if sockaddr
-                   (rawsock:recvfrom sock buffer sockaddr :start 0 :end length)
-                   (rawsock:recv sock buffer :start 0 :end length)))
-           (host 0) (port 0))
-      (unless (connected-p socket)
-        (let ((data (rawsock:sockaddr-data sockaddr)))
-          (setq host (ip-from-octet-buffer data :start 4)
-                port (port-from-octet-buffer data :start 2))))
-      (values buffer rv host port)))
+           (sockaddr (rawsock:make-sockaddr :inet))
+           (real-length (or length +max-datagram-packet-size+))
+           (real-buffer (or buffer
+                            (make-array real-length
+                                        :element-type '(unsigned-byte 8)))))
+      (let ((rv (rawsock:recvfrom sock real-buffer sockaddr
+                                 :start 0 :end real-length))
+            (host 0) (port 0))
+        (unless (connected-p socket)
+          (let ((data (rawsock:sockaddr-data sockaddr)))
+            (setq host (ip-from-octet-buffer data :start 4)
+                  port (port-from-octet-buffer data :start 2))))
+        (values (if buffer real-buffer (subseq real-buffer 0 rv))
+                rv
+                host
+                port))))
 
   (defmethod socket-send ((socket datagram-usocket) buffer length &key host port)
     "Returns the number of octets sent."
@@ -256,19 +299,40 @@
                                                (make-sockaddr_in)
                                                (host-byte-order host)
                                                port))))
+           (real-length (or length (length buffer)))
+           (real-buffer (if (typep buffer '(simple-array (unsigned-byte 8) (*)))
+                            buffer
+                          (make-array real-length
+                                      :element-type '(unsigned-byte 8)
+                                      :initial-contents (subseq buffer 0 real-length))))
            (rv (if (and host port)
-                   (rawsock:sendto sock buffer sockaddr
+                   (rawsock:sendto sock real-buffer sockaddr
                                    :start 0
-                                   :end length)
-                   (rawsock:send sock buffer
+                                   :end real-length)
+                   (rawsock:send sock real-buffer
                                  :start 0
-                                 :end length))))
+                                 :end real-length))))
       rv))
 
   (defmethod socket-close ((usocket datagram-usocket))
     (when (wait-list usocket)
        (remove-waiter (wait-list usocket) usocket))
     (rawsock:sock-close (socket usocket)))
+
+  (declaim (inline get-socket-name))
+  (defun get-socket-name (socket function)
+    (let ((sockaddr (rawsock:make-sockaddr :inet (make-sockaddr_in))))
+      (funcall function socket sockaddr)
+      (let ((data (rawsock:sockaddr-data sockaddr)))
+        (values (hbo-to-vector-quad (ip-from-octet-buffer data :start 2))
+                (port-from-octet-buffer data :start 0)))))
+
+  (defmethod get-local-name ((usocket datagram-usocket))
+    (get-socket-name (socket usocket) 'rawsock:getsockname))
+
+  (defmethod get-peer-name ((usocket datagram-usocket))
+    (get-socket-name (socket usocket) 'rawsock:getpeername))
+
 ) ; progn
 
 ;;;
@@ -278,27 +342,22 @@
 #+(and ffi (not rawsock))
 (progn
   ;; C primitive types
-  (ffi:def-c-type size_t)
-  (ffi:def-c-type in_addr_t   ffi:uint32)
-  (ffi:def-c-type in_port_t   ffi:uint16)
-  (ffi:def-c-type sa_family_t ffi:uint8)
-  (ffi:def-c-type socklen_t   ffi:uint32)
+  (ffi:def-c-type socklen_t ffi:uint32)
 
   ;; C structures
   (ffi:def-c-struct sockaddr
-    (sa_len     ffi:uint8)
-    (sa_family  sa_family_t)
+    #+macos (sa_len ffi:uint8)
+    (sa_family  #-macos ffi:ushort
+		#+macos ffi:uint8)
     (sa_data    (ffi:c-array ffi:char 14)))
 
-  #+ignore
-  (ffi:def-c-struct in_addr
-    (s_addr     in_addr_t))
-
   (ffi:def-c-struct sockaddr_in
-    (sin_len    ffi:uint8)
-    (sin_family sa_family_t)
-    (sin_port   in_port_t)
-    (sin_addr   in_addr_t) ; should be struct in_addr
+    #+macos (sin_len ffi:uint8)
+    (sin_family #-macos ffi:short
+		#+macos ffi:uint8)
+    (sin_port   #-macos ffi:ushort
+		#+macos ffi:uint16)
+    (sin_addr   ffi:uint32)
     (sin_zero   (ffi:c-array ffi:char 8)))
 
   (ffi:def-c-struct timeval
@@ -308,7 +367,7 @@
   ;; foreign functions
   (ffi:def-call-out %sendto (:name "sendto")
     (:arguments (socket ffi:int)
-		(buffer (ffi:c-ptr ffi:uint8))
+		(buffer ffi:c-pointer)
 		(length ffi:int)
 		(flags ffi:int)
 		(address (ffi:c-ptr sockaddr))
@@ -319,13 +378,24 @@
 	       #+win32 :stdc-stdcall)
     (:return-type ffi:int))
 
+  (ffi:def-call-out %send (:name "send")
+    (:arguments (socket ffi:int)
+		(buffer ffi:c-pointer)
+		(length ffi:int)
+		(flags ffi:int))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+	       #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
   (ffi:def-call-out %recvfrom (:name "recvfrom")
     (:arguments (socket ffi:int)
-		(buffer (ffi:c-ptr ffi:uint8) :out)
+		(buffer ffi:c-pointer)
 		(length ffi:int)
 		(flags ffi:int)
-		(address (ffi:c-ptr sockaddr) :out)
-		(address-len (ffi:c-ptr ffi:int) :out))
+		(address (ffi:c-ptr sockaddr) :in-out)
+		(address-len (ffi:c-ptr ffi:int) :in-out))
     #+win32 (:library "WS2_32")
     #-win32 (:library :default)
     (:language #-win32 :stdc
@@ -342,6 +412,34 @@
 	       #+win32 :stdc-stdcall)
     (:return-type ffi:int))
 
+  (ffi:def-call-out %connect (:name "connect")
+    (:arguments (socket ffi:int)
+		(address (ffi:c-ptr sockaddr) :in)
+		(address_len socklen_t))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+	       #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %bind (:name "bind")
+    (:arguments (socket ffi:int)
+		(address (ffi:c-ptr sockaddr) :in)
+		(address_len socklen_t))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+	       #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %close (:name #-win32 "close" #+win32 "closesocket")
+    (:arguments (socket ffi:int))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+	       #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
   (ffi:def-call-out %getsockopt (:name "getsockopt")
     (:arguments (sockfd ffi:int)
 		(level ffi:int)
@@ -366,18 +464,221 @@
 	       #+win32 :stdc-stdcall)
     (:return-type ffi:int))
 
+  (ffi:def-call-out %htonl (:name "htonl")
+    (:arguments (hostlong ffi:uint32))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+	       #+win32 :stdc-stdcall)
+    (:return-type ffi:uint32))
+
+  (ffi:def-call-out %htons (:name "htons")
+    (:arguments (hostshort ffi:uint16))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+	       #+win32 :stdc-stdcall)
+    (:return-type ffi:uint16))
+
+  (ffi:def-call-out %ntohl (:name "ntohl")
+    (:arguments (netlong ffi:uint32))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+	       #+win32 :stdc-stdcall)
+    (:return-type ffi:uint32))
+
+  (ffi:def-call-out %ntohs (:name "ntohs")
+    (:arguments (netshort ffi:uint16))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+	       #+win32 :stdc-stdcall)
+    (:return-type ffi:uint16))
+
+  (ffi:def-call-out %getsockname (:name "getsockname")
+    (:arguments (sockfd ffi:int)
+		(localaddr (ffi:c-ptr sockaddr) :in-out)
+		(addrlen (ffi:c-ptr socklen_t) :in-out))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+	       #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %getpeername (:name "getpeername")
+    (:arguments (sockfd ffi:int)
+		(peeraddr (ffi:c-ptr sockaddr) :in-out)
+		(addrlen (ffi:c-ptr socklen_t) :in-out))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+	       #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
   ;; socket constants
   (defconstant +socket-af-inet+ 2)
-  (defconstant +socket-pf-unspec+ 0)
   (defconstant +socket-sock-dgram+ 2)
+  (defconstant +socket-ip-proto-udp+ 17)
+
   (defconstant +sockopt-so-rcvtimeo+ #-linux #x1006 #+linux 20 "Socket receive timeout")
 
-  (defun open-udp-socket (&key local-address local-port read-timeout)
-    "Open a unconnected UDP socket. For binding on address ANY(*), just not set LOCAL-ADDRESS (NIL),
-for binding on random free unused port, set LOCAL-PORT to 0."
-    (let ((socket-fd (%socket +socket-af-inet+ +socket-sock-dgram+ +socket-pf-unspec+)))
-      (if socket-fd
-	  (progn
-	    )
-	  (error "cannot create socket"))))
+  (defparameter *length-of-sockaddr_in* (ffi:sizeof 'sockaddr_in))
+
+  (declaim (inline fill-sockaddr_in))
+  (defun fill-sockaddr_in (sockaddr host port)
+    (let ((hbo (host-to-hbo host)))
+      (ffi:with-c-place (place sockaddr)
+	#+macos
+	(setf (ffi:slot place 'sin_len) *length-of-sockaddr_in*)
+	(setf (ffi:slot place 'sin_family) +socket-af-inet+
+	      (ffi:slot place 'sin_port) (%htons port)
+	      (ffi:slot place 'sin_addr) (%htonl hbo)))
+      sockaddr))
+
+  (defun socket-create-datagram (local-port
+				 &key (local-host *wildcard-host*)
+				      remote-host
+				      remote-port)
+    (let ((sock (%socket +socket-af-inet+ +socket-sock-dgram+ +socket-ip-proto-udp+))
+	  (lsock_addr (fill-sockaddr_in (ffi:allocate-shallow 'sockaddr_in)
+					local-host local-port))
+	  (rsock_addr (when remote-host
+			(fill-sockaddr_in (ffi:allocate-shallow 'sockaddr_in)
+					  remote-host (or remote-port local-port)))))
+      (unless (plusp sock)
+	(error "SOCKET-CREATE-DATAGRAM ERROR (socket): ~A" (os:errno)))
+      (unwind-protect
+	   (let ((rv (%bind sock (ffi:cast (ffi:foreign-value lsock_addr) 'sockaddr)
+			    *length-of-sockaddr_in*)))
+	     (unless (zerop rv)
+	       (error "SOCKET-CREATE-DATAGRAM ERROR (bind): ~A" (os:errno)))
+	     (when rsock_addr
+	       (let ((rv (%connect sock
+				   (ffi:cast (ffi:foreign-value rsock_addr) 'sockaddr)
+				   *length-of-sockaddr_in*)))
+		 (unless (zerop rv)
+		   (error "SOCKET-CREATE-DATAGRAM ERROR (connect): ~A" (os:errno))))))
+	(ffi:foreign-free lsock_addr)
+	(when remote-host
+	  (ffi:foreign-free rsock_addr)))
+      (make-datagram-socket sock :connected-p (if rsock_addr t nil))))
+
+  (defun finalize-datagram-usocket (object)
+    (when (datagram-usocket-p object)
+      (socket-close object)))
+
+  (defmethod initialize-instance :after ((usocket datagram-usocket) &key)
+    (setf (slot-value usocket 'recv-buffer)
+	  (ffi:allocate-shallow 'ffi:uint8 :count +max-datagram-packet-size+))
+    ;; finalize the object
+    (ext:finalize usocket 'finalize-datagram-usocket))
+
+  (defmethod socket-close ((usocket datagram-usocket))
+    (when (wait-list usocket)
+      (remove-waiter (wait-list usocket) usocket))
+    (with-slots (recv-buffer socket) usocket
+      (ffi:foreign-free recv-buffer)
+      (zerop (%close socket))))
+
+  (defmethod socket-receive ((usocket datagram-usocket) buffer length &key)
+    (let ((remote-address (ffi:allocate-shallow 'sockaddr_in))
+	  (remote-address-length (ffi:allocate-shallow 'ffi:int))
+	  nbytes (host 0) (port 0))
+      (setf (ffi:foreign-value remote-address-length)
+	    *length-of-sockaddr_in*)
+      (unwind-protect
+	   (multiple-value-bind (n address address-length)
+	       (%recvfrom (socket usocket)
+			  (ffi:foreign-address (slot-value usocket 'recv-buffer))
+			  +max-datagram-packet-size+
+			  0 ; flags
+			  (ffi:cast (ffi:foreign-value remote-address) 'sockaddr)
+			  (ffi:foreign-value remote-address-length))
+	     (when (minusp n)
+	       (error "SOCKET-RECEIVE ERROR: ~A" (os:errno)))
+	     (setq nbytes n)
+	     (when (= address-length *length-of-sockaddr_in*)
+	       (let ((data (sockaddr-sa_data address)))
+		 (setq host (ip-from-octet-buffer data :start 2)
+		       port (port-from-octet-buffer data))))
+	     (cond ((plusp n)
+		    (let ((return-buffer (ffi:foreign-value (slot-value usocket 'recv-buffer))))
+		      (if buffer ; replace exist buffer of create new return buffer
+			  (let ((end-1 (min (or length (length buffer)) +max-datagram-packet-size+))
+				(end-2 (min n +max-datagram-packet-size+)))
+			    (replace buffer return-buffer :end1 end-1 :end2 end-2))
+			  (setq buffer (subseq return-buffer 0 (min n +max-datagram-packet-size+))))))
+		   ((zerop n))))
+	(ffi:foreign-free remote-address)
+	(ffi:foreign-free remote-address-length))
+      (values buffer nbytes host port)))
+
+  ;; implementation note: different from socket-receive, we know how many bytes we want to send everytime,
+  ;; so, a send buffer will not needed, and if there is a buffer, it's hard to fill its content like those
+  ;; in LispWorks. So, we allocate new foreign buffer for holding data (unknown sequence subtype) every time.
+  ;; 
+  ;; I don't know if anyone is watching my coding work, but I think this design is reasonable for CLISP.
+  (defmethod socket-send ((usocket datagram-usocket) buffer length &key host port)
+    (declare (type sequence buffer)
+	     (type integer length))
+    (let ((remote-address (when (and host port)
+			    (fill-sockaddr_in (ffi:allocate-shallow 'sockaddr_in) host port)))
+	  (send-buffer (let ((buffer-length (length buffer)))
+			 (if (> buffer-length (* length 2))
+			     ;; if buffer is too big, then we copy out a subseq and only allocate as need
+			     (ffi:allocate-deep 'ffi:uint8 (subseq buffer 0 length) :count length :read-only t)
+			     ;; then we allocate the whole buffer directly, that should be faster.
+			     (ffi:allocate-deep 'ffi:uint8 buffer :count (length buffer) :read-only t))))
+	  (real-length (min length +max-datagram-packet-size+))
+	  (nbytes 0))
+      (unwind-protect
+	   (let ((n (if remote-address
+			(%sendto (socket usocket)
+				 (ffi:foreign-address send-buffer)
+				 real-length
+				 0 ; flags
+				 (ffi:cast (ffi:foreign-value remote-address) 'sockaddr)
+				 *length-of-sockaddr_in*)
+			(%send (socket usocket)
+			       (ffi:foreign-address send-buffer)
+			       real-length
+			       0))))
+	     (cond ((plusp n)
+		    (setq nbytes n))
+		   ((zerop n)
+		    (setq nbytes n))
+		   (t (error "SOCKET-SEND ERROR: ~A" (os:errno)))))
+	(ffi:foreign-free send-buffer)
+	(when remote-address
+	  (ffi:foreign-free remote-address))
+	nbytes)))
+
+  (declaim (inline get-socket-name))
+  (defun get-socket-name (socket function)
+    (let ((address (ffi:allocate-shallow 'sockaddr_in))
+	  (address-length (ffi:allocate-shallow 'ffi:int))
+	  (host 0) (port 0))
+      (setf (ffi:foreign-value address-length) *length-of-sockaddr_in*)
+      (unwind-protect
+	   (multiple-value-bind (rv return-address return-address-length)
+	       (funcall function socket
+			(ffi:cast (ffi:foreign-value address) 'sockaddr)
+			(ffi:foreign-value address-length))
+	     (declare (ignore return-address-length))
+	     (if (zerop rv)
+		 (let ((data (sockaddr-sa_data return-address)))
+		   (setq host (ip-from-octet-buffer data :start 2)
+			 port (port-from-octet-buffer data)))
+		 (error "GET-SOCKET-NAME ERROR: ~A" (os:errno))))
+	(ffi:foreign-free address)
+	(ffi:foreign-free address-length))
+      (values (hbo-to-vector-quad host) port)))
+
+  (defmethod get-local-name ((usocket datagram-usocket))
+    (get-socket-name (socket usocket) '%getsockname))
+
+  (defmethod get-peer-name ((usocket datagram-usocket))
+    (get-socket-name (socket usocket) '%getpeername))
+
 ) ; progn

Modified: usocket/trunk/backend/cmucl.lisp
==============================================================================
--- usocket/trunk/backend/cmucl.lisp	(original)
+++ usocket/trunk/backend/cmucl.lisp	Fri Apr  1 12:52:18 2011
@@ -183,6 +183,10 @@
 	(%unix-send (socket usocket) buffer length 0))))
 
 (defmethod socket-receive ((usocket datagram-usocket) buffer length &key)
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+		   (integer 0)                          ; size
+		   (unsigned-byte 32)                   ; host
+		   (unsigned-byte 16)))                 ; port
   (let ((real-buffer (or buffer
                          (make-array length :element-type '(unsigned-byte 8))))
         (real-length (or length
@@ -190,8 +194,7 @@
     (multiple-value-bind (nbytes remote-host remote-port)
         (with-mapped-conditions (usocket)
           (ext:inet-recvfrom (socket usocket) real-buffer real-length))
-      (when (plusp nbytes)
-        (values real-buffer nbytes remote-host remote-port)))))
+      (values real-buffer nbytes remote-host remote-port))))
 
 (defmethod get-local-name ((usocket usocket))
   (multiple-value-bind
@@ -269,17 +272,17 @@
        (multiple-value-bind
            (secs musecs)
            (split-timeout (or timeout 1))
-         (multiple-value-bind
-             (count err)
-             (unix:unix-fast-select (1+ (reduce #'max
-                                                (wait-list-%wait wait-list)))
-                                    (alien:addr rfds) nil nil
-                                    (when timeout secs) musecs)
+         (multiple-value-bind (count err)
+	     (unix:unix-fast-select (1+ (reduce #'max
+						(wait-list-%wait wait-list)))
+				    (alien:addr rfds) nil nil
+				    (when timeout secs) musecs)
+	   (declare (ignore err))
            (if (<= 0 count)
                ;; process the result...
                (dolist (x (wait-list-waiters wait-list))
                  (when (unix:fd-isset (socket x) rfds)
                    (setf (state x) :READ)))
-             (progn
-	       ;;###FIXME generate an error, except for EINTR
-               )))))))
+	       (progn
+		 ;;###FIXME generate an error, except for EINTR
+		 )))))))

Modified: usocket/trunk/backend/lispworks.lisp
==============================================================================
--- usocket/trunk/backend/lispworks.lisp	(original)
+++ usocket/trunk/backend/lispworks.lisp	Fri Apr  1 12:52:18 2011
@@ -81,6 +81,8 @@
 (defconstant *socket_sock_dgram* 2
   "Connectionless, unreliable datagrams of fixed maximum length.")
 
+(defconstant *socket_ip_proto_udp* 17)
+
 (defconstant *sockopt_so_rcvtimeo*
   #-linux #x1006
   #+linux 20
@@ -186,7 +188,7 @@
   "Open a unconnected UDP socket.
    For binding on address ANY(*), just not set LOCAL-ADDRESS (NIL),
    for binding on random free unused port, set LOCAL-PORT to 0."
-  (let ((socket-fd (comm::socket comm::*socket_af_inet* *socket_sock_dgram* comm::*socket_pf_unspec*)))
+  (let ((socket-fd (comm::socket comm::*socket_af_inet* *socket_sock_dgram* *socket_ip_proto_udp*)))
     (if socket-fd
       (progn
         (when read-timeout (set-socket-receive-timeout socket-fd read-timeout))
@@ -358,15 +360,14 @@
                     :element-type '(unsigned-byte 8)
                     :allocation :static)))
 
+(defvar *length-of-sockaddr_in*
+  (fli:size-of '(:struct comm::sockaddr_in)))
+
 (defun send-message (socket-fd message buffer &optional (length (length buffer)) host service)
   "Send message to a socket, using sendto()/send()"
   (declare (type integer socket-fd)
            (type sequence buffer))
-  (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in))
-                                     (len :int
-                                          #-(or lispworks4 lispworks5.0) ; <= 5.0
-                                          :initial-element
-                                          (fli:size-of '(:struct comm::sockaddr_in))))
+  (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in)))
     (fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte))
       (replace message buffer :end2 length)
       (if (and host service)
@@ -374,7 +375,7 @@
             (comm::initialize-sockaddr_in client-addr comm::*socket_af_inet* host service "udp")
             (%sendto socket-fd ptr (min length +max-datagram-packet-size+) 0
                      (fli:copy-pointer client-addr :type '(:struct comm::sockaddr))
-                     (fli:dereference len)))
+                     *length-of-sockaddr_in*))
           (comm::%send socket-fd ptr (min length +max-datagram-packet-size+) 0)))))
 
 (defmethod socket-send ((socket datagram-usocket) buffer length &key host port)
@@ -397,8 +398,9 @@
     (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in))
                                        (len :int
 					    #-(or lispworks4 lispworks5.0) ; <= 5.0
-                                            :initial-element
-                                            (fli:size-of '(:struct comm::sockaddr_in))))
+                                            :initial-element *length-of-sockaddr_in*))
+      #+(or lispworks4 lispworks5.0) ; <= 5.0
+      (setf (fli:dereference len) *length-of-sockaddr_in*)
       (fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte))
         ;; setup new read timeout
         (when read-timeout
@@ -433,6 +435,10 @@
               (values nil n 0 0)))))))
 
 (defmethod socket-receive ((socket datagram-usocket) buffer length &key timeout)
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+		   (integer 0)                          ; size
+		   (unsigned-byte 32)                   ; host
+		   (unsigned-byte 16)))                 ; port
   (multiple-value-bind (buffer size host port)
       (receive-message (socket socket)
                        (slot-value socket 'recv-buffer)

Modified: usocket/trunk/backend/sbcl.lisp
==============================================================================
--- usocket/trunk/backend/sbcl.lisp	(original)
+++ usocket/trunk/backend/sbcl.lisp	Fri Apr  1 12:52:18 2011
@@ -387,6 +387,10 @@
 
 (defmethod socket-receive ((socket datagram-usocket) buffer length
 			   &key (element-type '(unsigned-byte 8)))
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+		   (integer 0)                          ; size
+		   (unsigned-byte 32)                   ; host
+		   (unsigned-byte 16)))                 ; port
   (with-mapped-conditions (socket)
     (let ((s (socket socket)))
       (sb-bsd-sockets:socket-receive s buffer length :element-type element-type))))

Modified: usocket/trunk/backend/scl.lisp
==============================================================================
--- usocket/trunk/backend/scl.lisp	(original)
+++ usocket/trunk/backend/scl.lisp	Fri Apr  1 12:52:18 2011
@@ -145,6 +145,10 @@
 	  (scl-map-socket-error errno :socket socket)))))
 
 (defmethod socket-receive ((socket datagram-usocket) buffer length &key)
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+		   (integer 0)                          ; size
+		   (unsigned-byte 32)                   ; host
+		   (unsigned-byte 16)))                 ; port
   (let ((s (socket socket)))
     (let ((real-buffer (or buffer
 			   (make-array length :element-type '(unsigned-byte 8))))

Modified: usocket/trunk/test/test-usocket.lisp
==============================================================================
--- usocket/trunk/test/test-usocket.lisp	(original)
+++ usocket/trunk/test/test-usocket.lisp	Fri Apr  1 12:52:18 2011
@@ -118,13 +118,11 @@
       (unwind-protect
           (progn
             (format (usocket:socket-stream sock)
-                    "GET / HTTP/1.0~c~c~c~c"
-                    #\Return #\linefeed #\Return #\linefeed)
+                    "GET / HTTP/1.0~2%")
             (force-output (usocket:socket-stream sock))
-            (read-line (usocket:socket-stream sock)))
+            (subseq (read-line (usocket:socket-stream sock)) 0 15))
         (usocket:socket-close sock))))
-  #+(or mcl clisp) "HTTP/1.1 200 OK"
-  #-(or mcl clisp) #.(format nil "HTTP/1.1 200 OK~A" #\Return) nil)
+  "HTTP/1.1 200 OK")
 
 (deftest socket-name.1
   (with-caught-conditions (nil nil)
@@ -188,14 +186,12 @@
       (unwind-protect
           (progn
             (format (usocket:socket-stream sock)
-                    "GET / HTTP/1.0~c~c~c~c"
-                    #\Return #\linefeed #\Return #\linefeed)
+                    "GET / HTTP/1.0~2%")
             (force-output (usocket:socket-stream sock))
             (usocket:wait-for-input sock :timeout *wait-for-input-timeout*)
-            (read-line (usocket:socket-stream sock)))
+            (subseq (read-line (usocket:socket-stream sock)) 0 15))
         (usocket:socket-close sock))))
-  #+(or mcl clisp) "HTTP/1.1 200 OK"
-  #-(or mcl clisp) #.(format nil "HTTP/1.1 200 OK~A" #\Return) nil)
+  "HTTP/1.1 200 OK")
 
 (defun run-usocket-tests ()
   (do-tests))

Modified: usocket/trunk/usocket-test.asd
==============================================================================
--- usocket/trunk/usocket-test.asd	(original)
+++ usocket/trunk/usocket-test.asd	Fri Apr  1 12:52:18 2011
@@ -16,7 +16,7 @@
     :name "usocket test"
     :author "Erik Enge"
     :maintainer "Chun Tian (binghe)"
-    :version "0.1.0"
+    :version "0.2.0"
     :licence "MIT"
     :description "Tests for usocket"
     :depends-on (:usocket

Modified: usocket/trunk/usocket.lisp
==============================================================================
--- usocket/trunk/usocket.lisp	(original)
+++ usocket/trunk/usocket.lisp	Fri Apr  1 12:52:18 2011
@@ -99,18 +99,20 @@
   ((connected-p :type boolean
                 :accessor connected-p
                 :initarg :connected-p)
-   #+(or cmu scl lispworks)
+   #+(or cmu
+         scl
+         lispworks
+         (and clisp ffi (not rawsock)))
    (%open-p     :type boolean
                 :accessor %open-p
                 :initform t
 		:documentation "Flag to indicate if usocket is open,
 for GC on implementions operate on raw socket fd.")
+   #+(or lispworks
+         (and clisp ffi (not rawsock)))
+   (recv-buffer :documentation "Private RECV buffer.")
    #+lispworks
-   (recv-buffer
-    :documentation "Private RECV buffer.")
-   #+lispworks
-   (send-buffer
-    :documentation "Private SEND buffer."))
+   (send-buffer :documentation "Private SEND buffer."))
   (:documentation "UDP (inet-datagram) socket"))
 
 (defun usocket-p (socket)




More information about the usocket-cvs mailing list