Giter Site home page Giter Site logo

usocket / usocket Goto Github PK

View Code? Open in Web Editor NEW
215.0 215.0 50.0 2 MB

Universal socket library for Common Lisp

Home Page: https://common-lisp.net/project/usocket/

License: Other

Common Lisp 99.40% Dockerfile 0.39% Makefile 0.21%
common-lisp networking socket

usocket's Introduction

USOCKET - Universal socket library for Common Lisp

https://common-lisp.net/project/usocket/

This is the usocket Common Lisp sockets library - a library to bring sockets access to the broadest of Common Lisp implementations as possible.

The library currently supports:

  1. Allegro CL
  2. ABCL (ArmedBear)
  3. Clasp
  4. Clozure CL
  5. Corman Lisp
  6. GNU CLISP
  7. CMUCL
  8. ECL
  9. LispWorks (4.3 and up)
  10. Digitool MCL and RMCL (5.0 and up)
  11. Mezzano
  12. MOCL
  13. SBCL
  14. Scieneer CL
  15. Symbolics Lisp Machine (Genera)

If your favorite Common Lisp is missing from the above list, please contact [email protected] and submit a request. Please include references to available sockets functions in your Lisp implementation.

The library is ASDF-enabled, meaning that you can tar up a checkout and use that to asdf-install:install the package in your system package site. (Or use your usual ASDF tricks to use the checkout directly.)

Remarks on licensing

Even though the source code has an MIT-style license attached to it, when compiling this code with some of the supported Lisp implementations you may not end up with an MIT-style binary version due to the licensing of the implementations themselves. ECL is such an example and - when it comes to be supported - so is GCL.

Non-support of :external-format

Because of its definition in the Hyperspec, there's no common external-format between Lisp implementations: every vendor has chosen a different way to solve the problem of newline translation or character set recoding.

Because there's no way to avoid platform-specific code in the application when using external-format, the purpose of a portability layer gets defeated. So, for now, usocket doesn't support external-format.

The workaround to get reasonably portable external-format support is to layer a flexi-stream (from flexi-streams) on top of a usocket stream.

API definition

  • usocket (class)

  • stream-usocket (class; usocket derivative)

  • stream-server-usocket (class; usocket derivative)

  • socket-connect (host port &key element-type) (function)

    Create an active/connected socket. host can be a vectorized IP, or a string representation of a dotted IP address, or a hostname for lookup in the DNS system.

  • socket-listen (host port &key reuseaddress backlog element-type) (function)

    Create a passive/listening socket. For possible values of host, see socket-connect.

  • socket-accept (socket &key element-type) (method)

    Create an active/connected socket. Returns (server side) a connected socket derived from a listening/passive socket.

  • socket-close (socket) (method)

    Where socket is a previously-returned socket.

  • socket (usocket slot accessor),

    Returns the internal/implementation-defined socket representation.

  • socket-stream (socket) (usocket slot accessor),

    Returns a value which satisfies the normal stream interface.

  • socket-shutdown

Errors:

  • address-in-use-error
  • address-not-available-error
  • bad-file-descriptor-error
  • connection-refused-error
  • connection-aborted-error
  • connection-reset-error
  • invalid-argument-error
  • no-buffers-error
  • operation-not-supported-error
  • operation-not-permitted-error
  • protocol-not-supported-error
  • socket-type-not-supported-error
  • network-unreachable-error
  • network-down-error
  • network-reset-error
  • host-down-error
  • host-unreachable-error
  • shutdown-error
  • timeout-error
  • unkown-error

Non-fatal conditions:

  • interrupted-condition
  • unkown-condition

(for a description of the API methods and functions see https://common-lisp.net/project/usocket/api-docs.shtml)

Test suite

The test suite unfortunately isn't mature enough yet to run without some manual configuration. Several elements are required which are hard to programatically detect. Please adjust the test file before running the tests, for these variables:

  • +non-existing-host+: The stringified IP address of a host on the same subnet. No physical host may be present.
  • +unused-local-port+: A port number of a port not in use on the machine the tests run on.
  • +common-lisp-net+: A vector with 4 integer elements which make up an IP address. This must be the IP "common-lisp.net" resolves to.

Known problems

  • CMUCL error reporting wrt sockets raises only simple-errors meaning there's no way to tell different error conditions apart. All errors are mapped to unknown-error on CMUCL.

  • The ArmedBear backend doesn't do any error mapping (yet). Java defines exceptions at the wrong level (IMO), since the exception reported bears a relation to the function failing, not the actual error that occurred: for example 'Address already in use' (when creating a passive socket) is reported as a BindException with an error text of 'Address already in use'. There's no way to sanely map BindException to a meaningfull error in usocket. [This does not mean the backend should not at least map to unknown-error!]

  • When using the library with ECL, you need the C compiler installed to be able to compile and load the Foreign Function Interface. Not all ECL targets support DFFI yet, so on some targets this would be the case anyway. By depending on this technique, usocket can 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?)

usocket's People

Contributors

binghe avatar bpseudopod avatar charje avatar contrapunctus-1 avatar dalkire avatar dkochmanski avatar drmeister avatar ebrasca avatar equwal avatar fjl avatar genworks avatar glv2 avatar hanshuebner avatar kilianmh avatar kpoeck avatar linkfly avatar lokedhs avatar mmontone avatar mr-dispatch avatar phoe avatar rabuf avatar rudolfochrist avatar snunez1 avatar stassats avatar synchromesh avatar tmccombs avatar varjagg avatar vibs29 avatar yitzchak avatar zhscn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

usocket's Issues

Produced documentation with HELP

Hi

I wanted to use USOCKET for a little side project of mine and I got sidetracked into looking at its documentation.
I ended up using USOCKET as a testbed for HELambdaP. Bottom line, I added functionality to HELambdaP and gotten a new version of the documentation (that can be regenerated at will, apart form the front pages with the tables etc).

If anybody is interested here is a zip of the documentation. It is all static pages (it unzips in a "docs/html/..." folder structure). The HTML is oldish (uses FRAMESETS), but it looks, IMHO, pretty nice.

Let me know what you think...

All the best

Marco Antoniotti

usocket-docs.zip

Cannot connect to a listener on 0.0.0.0

Related to #75 but here, characterised as a bug.

This is in the context of IPv6, with both IPv6 and IPv4 interfaces.

Notably, localhost has a A and an AAAA records!

17:48[pjb@despina org.xquartz:0 ~ 17Gi]$ ping -c 3 localhost
PING localhost (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.053 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.046 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.054 ms

--- localhost ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.046/0.051/0.054/0.004 ms

17:48[pjb@despina org.xquartz:0 ~ 17Gi]$ ping6 -c 3 localhost
PING6(56=40+8+8 bytes) ::1 --> ::1
16 bytes from ::1, icmp_seq=0 hlim=64 time=0.088 ms
16 bytes from ::1, icmp_seq=1 hlim=64 time=0.074 ms
16 bytes from ::1, icmp_seq=2 hlim=64 time=0.073 ms

--- localhost ping6 statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 0.073/0.078/0.088/0.007 ms

Listening to localhost and connecting to localhost works:

(usocket:with-socket-listener (listen  "localhost"   9999 :element-type '(unsigned-byte 8) :reuseaddress t :backlog 1)
  (usocket:with-client-socket (client client-stream  "localhost" 9999 :element-type '(unsigned-byte 8))
    (usocket:with-server-socket (server (usocket:socket-accept listen))
      (write-byte 42  client-stream)
      (force-output client-stream)
      (read-byte  (usocket:socket server)))))

42

But listening on all the IPv4 addresses, and connecting to localhost does not work:

(usocket:with-socket-listener (listen  "0.0.0.0"   9999 :element-type '(unsigned-byte 8) :reuseaddress t :backlog 1)
  (usocket:with-client-socket (client client-stream  "localhost" 9999 :element-type '(unsigned-byte 8))
    (usocket:with-server-socket (server (usocket:socket-accept listen))
      (write-byte 42  client-stream)
      (force-output client-stream)
      (read-byte  (usocket:socket server)))))

> Debug: Error #<usocket:connection-refused-error #x3020051CEBCD>
> While executing: (:internal swank::invoke-default-debugger), in process worker(470).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.

$ macosx-netstat-tnpl |grep 9999
tcp4       0      0  *.9999                 *.*                    LISTEN

That's because as mentionned in #75, usocket:socket-connect only tries IPv6 and not both IPv6 and IPv4 addresses. Cf. patch provided in #75.

Four failing tests in usocket-tests

I just loaded usocket, usocket-tests, and usocket-udp via quicklisp on SBCL tonight and ran usocket-tests:do-tests and had a few failures: http://paste.lisp.org/display/305311

% uname -a Linux ??? 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt20-1+deb8u2 (2016-01-02) x86_64 GNU/Linux

% sbcl --version SBCL 1.3.1

Also running the latest version of quicklisps and all of my dists:

CL-USER> (ql:update-all-dists)
1 dist to check.
You already have the latest version of "quicklisp": 2015-12-18.
NIL
CL-USER> (ql:update-client)
The most up-to-date client, version 2015-09-24, is already installed.
T

socket-send returns NIL on ABCL

Expected behavior:
socket-send returns the number of bytes written.

Actual behavior:
Returns NIL, which causes a TYPE-ERROR to be signaled from the UDP socket-server before crashing.

Steps to replicate:

(usocket:socket-server "127.0.0.1" 4440 #'print nil :protocol :datagram :in-new-thread t)
(setf sock (usocket:socket-connect "127.0.0.1" 4440 :protocol :datagram))
(usocket:socket-send sock (make-array 65 :initial-element 0) 65)

Version found in:
b0f5f0f

Patch:

--- backend/abcl.lisp
+++ backend/abcl.lisp
@@ -351,7 +351,8 @@
     (loop for i from offset below (+ size offset)
        do (setf (jarray-ref byte-array i) (*->byte (aref buffer i))))
     (with-mapped-conditions (usocket host)
-      (jcall $@send/1 socket packet))))
+      (jcall $@send/1 socket packet)
+      size)))
 
 ;;; TODO: return-host and return-port cannot be get ...
 (defmethod socket-receive ((usocket datagram-usocket) buffer length

Possible bug: write causes EOF without reading data

I've come across what appears to be a strange bug while diagnosing a server of mine. I'm not sure that this is an issue with usocket itself, but both SBCL and CCL exhibit the same behavior.

Symptoms: a client connects to a server, writes some data, and immediately closes the socket. If the server writes data after the socket has been closed, a subsequent read will fail to read data written by the client.

Test code (I've been testing on Windows so far, not sure if this is also present on Linux):

(defparameter +host+ "127.0.0.1")
(defparameter +port+ 9001)

;; client.lisp
(defun do-write ()
  (let* ((sock (usocket:socket-connect +host+ +port+))
         (stream (usocket:socket-stream sock)))
    (format stream "Rhubarb")
    (finish-output stream)
    (usocket:socket-close sock)))

;; server.lisp
(defun do-read ()
  (let* ((srv-sock (usocket:socket-listen +host+ +port+ :element-type '(unsigned-byte 8)))
         (sock (usocket:socket-accept srv-sock))
         (stream (usocket:socket-stream sock)))
    ;; Make sure the client's closed the socket.
    (sleep 1)
    ;; Comment this write out, and everything works normally.
    (write-sequence (make-array 40 :initial-element 3 :element-type '(unsigned-byte 8))
                    stream)
    (finish-output stream)
    (let ((seq (make-array 1 :element-type '(unsigned-byte 8))))
      ;; With the write above in place, this will return 0 (i.e. end-of-file).
      ;; Without the write, it returns 1 and reads the first byte of client data.
      (prog1 (read-sequence seq stream)
             (usocket:socket-close sock)
             (usocket:socket-close srv-sock)))))

LispWorks specific: socket-shutdown on stream-usocket need to pass the stream to comm:socket-stream-shutdown (rather than the socket)

in backend/lispworks.lisp, the method socket-shutdown on stream-usocket currently calls
comm:socket-stream-shutdown on the socket, which is wrrong, because comm:socket-stream-shutdown expects a socket-stream. It needs to pass the stream, that is replace

(socket usocket)
by
(socket-stream usocket)

Causes two failures when running the regressions tests on LispWorks.

error with usocket datagram socket sending userial buffers on SBCL 1.2.1 Windows 8

I apologize if this is actually an issue with userial or Windows.

Code can be found here: https://gist.github.com/ezrarush/01741135c7141621210c

Using userial with usocket's datagram socket causes an intermittent error.

Socket error in "sendto": 10014 (The system detected an invalid pointer address in attempting to use a pointer argument in a call.)
[Condition of type SB-BSD-SOCKETS:SOCKET-ERROR]

Using an array instead of userial's buffer does not seem to error.

Missing USOCKET::FDSET-ALLOC on CL REPL (EQL5, Andoid)

I'm trying to use hunchentoot.

Before loading hunchentoot, *FEATURES* includes :ECL, :ECL-BYTECMP, :EQL, :EQL5 and :ANDROID -to narrow the field for you, though among these I'm aware that usocket system configuration seems to involve only :ECL and :ECL-BYTECOMP, from what I've been able to glean from usocket files.

hunchentoot pulls in usocket, but it raises a missing USOCKET::FDSET-ALLOC when I try to (start ...) hunchentoot per the documentation.

(ql:quickload "hunchentoot")

(in-package 'usocket)

(fboundp 'fdset-alloc)

NIL

When I reported the issue to hunchentoot, hunchentoot told me it was an issue for usocket. Another CL user on another forum also recommended that I report the issue to usocket.

Meanwhile, I'm trying to trace the bug to get this working, but I don't have enough recent CL experience or knowledge about all of the internals to be able to help much yet.

edit: forgot to proofread

When #+ipv6 also try to resolve an IPv4 address if the connection cannot be established with the IPv6 address.

Related to #66

IMO, there's little reason to split the implementation in two for #-ipv6 and #+ipv6 (openmcl.lisp vs clozure.lisp).
In any case, when #+ipv6, if there is no server behind the IPv6 address, try the IPv4 address!
eg. in clozure.lisp (perhaps other places would apply too):

#+ipv6
(defun socket-connect (host port &key (protocol :stream) element-type
                                   timeout deadline nodelay
                                   local-host local-port)
  (when (eq nodelay :if-supported)
    (setf nodelay t))
  (with-mapped-conditions (nil host)
    (let (remote local mcl-sock)
      (loop
        :for address-family :in '(:internet6 :internet)
        :do (tagbody
               (setf remote  (when (and host port)
		                       (openmcl-socket:resolve-address :host (host-to-hostname host)
						                                       :port port
						                                       :socket-type protocol
                                                               :address-family address-family))
                     local   (when (and local-host local-port)
		                       (openmcl-socket:resolve-address :host (host-to-hostname local-host)
						                                       :port local-port
						                                       :socket-type protocol
                                                               :address-family address-family))
                     mcl-sock (handler-bind
                                  ((ccl:socket-creation-error
                                     (lambda (err)
                                       (if (eq address-family :internet6) ; the first try, let's ignore the error
                                           (go :continue))
                                       (signal err))))  
                                (apply #'openmcl-socket:make-socket
			                           `(:type ,protocol
			                                   ,@(when (or remote local)
				                                   `(:address-family ,(openmcl-socket:socket-address-family (or remote local))))
			                                   ,@(when remote
				                                   `(:remote-address ,remote))
			                                   ,@(when local
				                                   `(:local-address ,local))
			                                   :format ,(to-format element-type protocol)
			                                   :external-format ,ccl:*default-external-format*
			                                   :deadline ,deadline
			                                   :nodelay ,nodelay
			                                   :connect-timeout ,timeout
			                                   :input-timeout ,timeout))))
               (loop-finish)
             :continue))
      (ecase protocol
        (:stream
         (make-stream-socket :stream mcl-sock :socket mcl-sock))
        (:datagram
         (make-datagram-socket mcl-sock :connected-p (and remote t)))))))

I/O timeouts

I am wondering if it would be possible for usocket to support I/O timeouts on implementations that support it? This would really add a lot of value to me. I would volunteer to work on supporting Clozure CL.

Umlaut domains not found ("bücher.at")

While my browser can resolve bücher.at, USOCKET can't.

Furthermore, some implementations (or perhaps just the FFI interface) only allow simple strings, so data coming from outsite (JSON, XML, ...) may break:

(usocket:get-host-by-name 
  (make-array 9
              :fill-pointer 9
              :element-type 'character
              :initial-contents "bücher.at"))
; The condition The value "bücher.at" is not of type SIMPLE-STRING
;               when binding STRING occurred with errno: 0.
;    [Condition of type USOCKET:UNKNOWN-ERROR]
; 
; Frames:
;   0.  (SB-ALIEN::STRING-TO-C-STRING "bücher.at" :UTF-8) [external]
;   1.  (SB-BSD-SOCKETS:GET-HOST-BY-NAME "bücher.at")
;   2.  (USOCKET:GET-HOSTS-BY-NAME "bücher.at")
;   3.  (USOCKET:GET-HOST-BY-NAME "bücher.at")

If would be great if such internal restrictions would be ensured at the lower USOCKET border, so that the users don't need to know about them.

Thanks a lot!

+lispworks-error-map+ contains conditions that need fixing

  1. NS-TRY-AGAIN-CONDITION is currently not a sub-class of socket-condition. It actually causes
    an error when RAISE-USOCK-ERR tries to signal it, because it passes :socket which is not
    an acceptable initarg for NS-TRY-AGAIN-CONDITION. That happens to me while running
    the tests of cl+ssl.

    That seem to me like a "typo". Changing the definition of NS-TRY-AGAIN-CONDITION to
    inherit from ns-try-again-condition like this:

    (define-usocket-condition-classes
       (ns-try-again-condition)
        (socket-condition))
    

    eliminate the failures in cl+ssl, and didn't change the number of failures in the usocket regression tests.

  2. OUT-OF-MEMORY-ERROR and ALREADY-SHUTDOWN-ERROR are not defined anywhere. I didn't
    see any problem because of these, just noticed these while looking at the error in cl+ssl.

Don't default to IPv4 if the host has no IPv4 interface

If usocket is used on an IPv6-only host, and if the endpoint it is trying to reach resolves to both an IPv4 and IPv6 DNS address, usocket will always try the IPv4 address. A smarter decision needs to be made. Perhaps look at the routing table?

Is there a way to tell usocket to use IPv6 only?

WAIT-FOR-INPUT with READY-ONLY: return only one socket

Currently WAIT-FOR-INPUT with READY-ONLY argument conses up a new list every time.

I propose adding a new key that will tell WAIT-FOR-INPUT to return only the first socket that was detected as ready. This way, no consing will occur.

Error: The condition Bad address (error #14) during socket creation operation in bind occurred with errno: 0

I am trying to run hunchentoot on ccl , and I have tried this demo in hunchentoot document :
(T482:lisp/ccl/201907271334) # cat 1.lisp
(hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 80))
(hunchentoot:define-easy-handler (say-yo :uri "/yo") (name)
(setf (hunchentoot:content-type*) "text/plain")
(format nil "Hey~@[ A]!" name))
then I loaded it in ccl :
(T482:lisp/ccl/201907271334) # ./ccl/ppccl64 -I 0.img -l 1.lisp

Error: The condition Bad address (error #14) during socket creation operation in bind occurred with errno: 0.
While executing: USOCKET::RAISE-ERROR-FROM-ID, in process listener(1).
Type :GO to continue, :POP to abort, :R for a list of available restarts.
If continued: Skip loading "1.lisp"
Type :? for other options.
1 >

multicast support

Any possibility of adding multicast support? I'd prefer not having to use another socket library for upnp and service discovery.
I know it's at least possible on sbcl since I've previously used that for multicast.

Symbol "INET6-SOCKET" not found in the SB-BSD-SOCKETS package.

CL-USER> (ql:quickload :usocket)
To load "usocket":
Load 1 ASDF system:
usocket
; Loading "usocket"
[package usocket].......
;
; caught ERROR:
; READ error during COMPILE-FILE:
;
; Symbol "INET6-SOCKET" not found in the SB-BSD-SOCKETS package.
;
; Line: 287, Column: 71, File-Position: 10470
;
; Stream: #<SB-SYS:FD-STREAM
; for "file /home/ll/quicklisp/dists/quicklisp/software/usocket-0.6.4/backend/sbcl.lisp"
; {10049D1853}>

REDUCE #'MAX called with no arguments

While testing my multithreaded application, I got the following error:

invalid number of arguments: 0
   [Condition of type SB-INT:SIMPLE-PROGRAM-ERROR]

Restarts:
 0: [REPLACE-FUNCTION] Call a different function with the same arguments
 1: [CALL-FORM] Call a different form
 2: [RETRY] Abort the current iteration and send the acceptor back to its loop.
 3: [ABORT] abort thread (#<THREAD "Gateway - Acceptor for 127.0.0.1:41605" RUNNING {1005231EC3}>)

Backtrace:
  0: (MAX) [external]
      Locals:
        NIL = #<FUNCTION MAX>
        NIL = #<STANDARD-GENERIC-FUNCTION SB-BSD-SOCKETS:SOCKET-FILE-DESCRIPTOR (1)>
  1: (REDUCE #<FUNCTION MAX> NIL :KEY #<STANDARD-GENERIC-FUNCTION SB-BSD-SOCKETS:SOCKET-FILE-DESCRIPTOR (1)>)
      Locals:
        #:.DEFAULTING-TEMP. = #<STANDARD-GENERIC-FUNCTION SB-BSD-SOCKETS:SOCKET-FILE-DESCRIPTOR (1)>
        #:.DEFAULTING-TEMP.#1 = NIL
        #:.DEFAULTING-TEMP.#2 = 0
        #:.DEFAULTING-TEMP.#3 = NIL
        #:.DEFAULTING-TEMP.#4 = NIL
        FUNCTION = #<FUNCTION MAX>
        #:N-SUPPLIED-2 = NIL
        SEQUENCE = NIL
        SB-DEBUG::MORE = (:KEY #<STANDARD-GENERIC-FUNCTION SB-BSD-SOCKETS:SOCKET-FILE-DESCRIPTOR (1)>)
  2: (USOCKET::WAIT-FOR-INPUT-INTERNAL #S(USOCKET::WAIT-LIST :%WAIT NIL :WAITERS NIL :MAP #<HASH-TABLE :TEST EQL :COUNT 0 {1005364443}>) :TIMEOUT 0.001)
      Locals:
        #:.DEFAULTING-TEMP. = 0.001
        SOCKETS = #S(USOCKET::WAIT-LIST :%WAIT NIL :WAITERS NIL :MAP #<HASH-TABLE :TEST EQL :COUNT 0 {1005DF2DF3}>)
  3: (USOCKET:WAIT-FOR-INPUT #S(USOCKET::WAIT-LIST :%WAIT NIL :WAITERS NIL :MAP #<HASH-TABLE :TEST EQL :COUNT 0 {1005364443}>) :TIMEOUT 0.001 :READY-ONLY T)
      Locals:
        #:.DEFAULTING-TEMP. = 0.001
        #:.DEFAULTING-TEMP.#1 = T
        SOCKET-OR-SOCKETS = #S(USOCKET::WAIT-LIST :%WAIT NIL :WAITERS NIL :MAP #<HASH-TABLE :TEST EQL :COUNT 0 {1005DF2DF3}>)
  4: (USOCKET:WAIT-FOR-INPUT #<USOCKET:STREAM-SERVER-USOCKET {10052311D3}> :TIMEOUT 0.001 :READY-ONLY T)
      Locals:
        #:.DEFAULTING-TEMP. = 0.001
        #:.DEFAULTING-TEMP.#1 = T
        SOCKET-OR-SOCKETS = #<USOCKET:STREAM-SERVER-USOCKET {10052311D3}>
  ...

REDUCE #'MAX was called with an empty argument list, which is an error. There should be a check which asserts that the list is not empty.

I'm on SBCL 1.4.9 on Linux.

[SBCL Windows] wait-for-input nil-timeout bug

Hey,

I think I found a bug in usocket Windows SBCL.
step to replicate:

CL-USER> (ql:quickload :usocket) (use-package :usocket)
CL-USER> (wait-for-input (socket-listen "localhost" 65001) :ready-only t)

Argument X is not a NUMBER: NIL
   [Condition of type SIMPLE-TYPE-ERROR]

Restarts:
 0: [RETRY] Retry SLIME REPL evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1003CC8033}>)

Backtrace:
  0: (SB-KERNEL:TWO-ARG-* NIL 1000)
  1: (USOCKET::WAIT-FOR-INPUT-INTERNAL #S(USOCKET::WAIT-LIST :%WAIT
#<SB-ALIEN-INTERNALS:ALIEN-VALUE :SAP #X00660570 :TYPE (* (SIGNED 64))>
:WAITERS (#<STREAM-SERVER-USOCKET {1005309413}>) :MAP #<HASH-TABL..
  2: (WAIT-FOR-INPUT #S(USOCKET::WAIT-LIST :%WAIT
#<SB-ALIEN-INTERNALS:ALIEN-VALUE :SAP #X00660570 :TYPE (* (SIGNED 64))>
:WAITERS (#<STREAM-SERVER-USOCKET {1005309413}>) :MAP #<HASH-TABLE :TEST
EQL :COUNT..
  3: (WAIT-FOR-INPUT #<STREAM-SERVER-USOCKET {1005309413}> :TIMEOUT NIL
:READY-ONLY T)
  4: (SB-INT:SIMPLE-EVAL-IN-LEXENV (WAIT-FOR-INPUT (SOCKET-LISTEN
"localhost" 65001) :READY-ONLY T) #<NULL-LEXENV>)
  5: (EVAL (WAIT-FOR-INPUT (SOCKET-LISTEN "localhost" 65001) :READY-ONLY T))

bug location:
https://github.com/Wukix/usocket/blob/master/backend/sbcl.lisp#L539

workaround(?):
the alien function behind all of it, for an infinite wait, expects a -1 casted to (unsigned 32), and it might be possible that (/ (- (expt 2 32) 1) 1000) is the thing to put in the wait-for-input call.

Please let me know what you think about it.

Cheers
~phoe

Unable to detect a closed socket in a non-blocking way

The API tells me what to do in two cases:

  • ...check whether reading from a socket stream will block, and
  • ...check whether the other end has closed my socket stream.

I find it impossible to:

  • ...check whether the other end has closed my socket stream without blocking.

Let's assume a following situation:

> (defparameter *socket* (socket-listen "localhost" 65035)) ;; => *SOCKET*
> (defparameter *stream* (socket-stream (socket-accept (wait-for-input *socket* :timeout 3000)))) ;; => *STREAM* ;after ncat localhost 65035
;; Ctrl-C on the ncat, socket gets closed, OR NOT, socket still open
> (listen *stream*) ;; => NIL

In a situation portrayed above, I should NOT do a (read-line *stream*) because (listen *stream*) gives me a NIL.

The issue I see is, (listen *stream*) gives a NIL in two cases:

  • when there is no data on the stream (usual case),
  • when there is an EOF on the stream (problematic case).
    And I am unable to separate these two cases.

In other words: is there any way to separate these two cases mentioned above?
In yet other words: in the situation portrayed within the code block, what command will reliably tell me whether the NIL given to me by the (listen *socket*) is because of lack of data or an EOF?

No tag for 0.6.4.1

The commit for 0.6.4.1 should be tagged as such so it’s easy to compare changes against etc.

:element-type arg of socket-accept doesn't work on CCL

The :element-type argument of socket-accept doesn't seem to be working properly in CCL -- it seems to ignore the argument and use whatever element-type that was used for socket-listen. It works fine in SBCL.

Example:

(defvar server-sock
  (usocket:socket-listen "127.0.0.1" 9998
                         :reuse-address t))

(defvar sock
  (usocket:socket-accept server-sock
                         :element-type '(unsigned-byte 8)))

(stream-element-type (usocket:socket-stream sock))

In SBCL this reports (unsigned-byte 8) as expected, but in CCL it reports character even though we specifically told socket-accept to use (unsigned-byte 8).

If I add the :element-type to the socket-listen call it works fine, but if I'm reading the documentation right it seems like I shouldn't have to.

LispWorks specific: socket-accept should check is actually got a socket

socket-accept for stream-server-usocket in backend/lispworks.lisp needs to check that the socket it got from comm::accept-connection-to-socket or comm::get-fd-from-socket is not NIL, because both of these can return NIL for failures.

What it should do if it is NIL is not obvious. LispWorks has a mechanism that re-tries until some number of consecutive failures, and then closes the accepting socket and tries to re-open it.

Maybe it should take another keyword like :error-handling to tell it what to do.

At the moment it gets an error of type usocket:invalid-socket-error inside the call make-stream-socket. A minimal change would be to add an accept-error condition and to signal an erro with it.

LispWorks specific: need to ensure callers of RAISE-USOCK-ERR return the right values(s)

backend/lispworks.lisp

The LispWorks version of RAISE-USOCK-ERR in some cases calls SIGNAL
rather than ERROR, which means it may return, returning the result of
SIGNAL, which is nil. That may cause a problem if the caller expects
specific return values.

I actually got an error because PLUSP was called with nil from UDP-EVENT-LOOP
(in server.lisp), presumably because the second return value of SOCKET-RECEIVE
was nil. That can happen if SOCKET-RECEIVE ended calling RAISE-USOCK-ERR in the
end and then RAISE-USOCK-ERR returns. SOCKET-RECEIVE needs to make sure it return
the expected values when RAISE-USOCK-ERR returns.

SOCKET-SEND probably also needs fixing.

The others calls look ok to return nil.

The error occurred in an idle image, in the process that was created by SOCKET-SERVER,
some time (probably few hours) after I ran the regression tests of usocket, so it is
not easily reproducible.

Should NS-TRY-AGAIN-CONDITION be a SERIOUS-CONDITION?

I just got thrown into the debugger with a USOCKET:NS-TRY-AGAIN-CONDITION signalled.

This surprised me, because the code was wrapped in a HANDLER-BIND dealing with SERIOUS-CONDITION. The CLHS entry for SERIOUS-CONDITION states: All conditions serious enough to require interactive intervention if not handled should inherit from the type serious-condition.

Because Usocket sent me to the debugger, I suppose the condition is such that this really should be a SERIOUS-CONDITION, yes?

USOCKET:NS-HOST-NOT-FOUND-ERROR condition has unbound host-or-ip slot on SBCL

How to reproduce:

  1. Turn off networking on your computer.
  2. Eval this code in the repl:
(handler-case (usocket:get-hosts-by-name "github.com")
  (USOCKET:NS-HOST-NOT-FOUND-ERROR (c)
    (format nil "Slot bound: ~A"
            (slot-boundp c 'usocket::host-or-ip))))

It will output:

"Slot bound: NIL"

If you eval (usocket:get-hosts-by-name "github.com") then you'll see such traceback:

Condition USOCKET:NS-HOST-NOT-FOUND-ERROR was signalled.
   [Condition of type USOCKET:NS-HOST-NOT-FOUND-ERROR]

Restarts:
 0: [RETRY-REQUEST] Retry the same request.
 1: [RETRY] Retry SLY mREPL evaluation request.
 2: [*ABORT] Return to SLY's top level.
 3: [ABORT] abort thread (#<THREAD "sly-channel-1-mrepl-remote-1" RUNNING {1003C8BFF3}>)

Backtrace:
 0: (USOCKET::HANDLE-CONDITION #<SB-BSD-SOCKETS:HOST-NOT-FOUND-ERROR {1006D91CE3}> NIL)
 1: (SB-KERNEL::%SIGNAL #<SB-BSD-SOCKETS:HOST-NOT-FOUND-ERROR {1006D91CE3}>)
 2: (ERROR SB-BSD-SOCKETS:HOST-NOT-FOUND-ERROR :ERROR-CODE 8 :SYSCALL "getaddrinfo")
 3: (SB-BSD-SOCKETS::ADDRINFO-ERROR "getaddrinfo" 8)
 4: (SB-BSD-SOCKETS:GET-HOST-BY-NAME #<unavailable argument>)
 5: (USOCKET:GET-HOSTS-BY-NAME "api.github.com")
 6: (USOCKET:SOCKET-CONNECT "api.github.com" 443 :PROTOCOL :STREAM :ELEMENT-TYPE (UNSIGNED-BYTE 8) :TIMEOUT 10 :DEADLINE NIL :NODELAY T :LOCAL-HOST NIL :LOCAL-PORT NIL)

Inspecting condition object shows:

#<USOCKET:NS-HOST-NOT-FOUND-ERROR {1006D91DA3}>
--------------------
The object is a CONDITION of type USOCKET:NS-HOST-NOT-FOUND-ERROR.
HOST-OR-IP: #<unbound>

usocket::handle-condition on cygwin

I'm seeing the following when connection errors occur in clisp on cygwin/windows

NO-APPLICABLE-METHOD: When calling #1=# with arguments #2=(#3=#<OS-ERROR
#x8071DC96>), no method is applicable.
[Condition of type METHOD-CALL-TYPE-ERROR]

Restarts:
0: [RETRY] try calling SIMPLE-CONDITION-FORMAT-ARGUMENTS again
1: [RETURN] specify return values
2: [SKIP] Skip file /cygdrive/e/Music/20 - Calvin Harris - I'm Not Alone [Tiësto Remix].flac
3: [RETRY] Retry SLIME REPL evaluation request.
4: [*PROCESS-INPUT] Continue reading input.
5: [ABORT] Return to SLIME's top level.
--more--

Backtrace:
0: <1/523> #
[522] frame binding variables (~ = dynamically):
| ~ SYSTEM::CONDITION-RESTARTS <--> NIL
1: <1/508> #
- 0
2: <1/502> #<COMPILED-FUNCTION #:|87 107 (DEFGENERIC NO-APPLICABLE-METHOD (GF &REST ARGS) ...)-9-1|>
<2/502> #
- NO-APPLICABLE-METHOD
3: <1/498> #
<2/498> #
- NIL
4: <1/494> #
- #<OS-ERROR #x8071DC96>
5: [490] unwind-protect frame
6: [487] catch frame for tag SYSTEM::DONE-SIGNALING
7: <1/490> #
<2/485> # 1
[481] frame binding variables (~ = dynamically):
| ~ PRINT-ESCAPE <--> T
8: <1/478> # 6
[472] handler frame for conditions CONDITION
9: <1/464> #
- NIL

Broken link on the readme

Your link to the API on the readme is broken. I have submitted a pull request.

You need a link with SSL, ie https instead of http

Package lock violation on SBCL 1.4.12

SBCL 1.4.12, usocket fresh from git 203ee42, ASDF v3.3.2

When I load the package with ASDF, it fails to compile backend/sbcl.lisp due to a package lock violation. The output I'm getting is this:

in: DEFUN WAIT-FOR-INPUT-INTERNAL
;     (LET* ((USOCKET::WAIT-LIST (USOCKET::WAIT-LIST-%WAIT USOCKET::SOCKETS)))
;       COUNT
;       USOCKET::ERR)
;
; caught STYLE-WARNING:
;   The variable WAIT-LIST is defined but never used.

;     (SETQ COUNT 0)
; ==>
;   (SET 'COUNT 0)
;
; caught WARNING:
;   violating package lock on COMMON-LISP:COUNT

;     (MULTIPLE-VALUE-SETQ (COUNT USOCKET::ERR)
;       (SB-UNIX:UNIX-FAST-SELECT
;        (1+
;         (REDUCE #'MAX USOCKET::WAIT-LIST :KEY
;                 #'SB-BSD-SOCKETS:SOCKET-FILE-DESCRIPTOR))
;        (ADDR USOCKET::RFDS) NIL NIL (WHEN USOCKET::TIMEOUT USOCKET::SECS)
;        (WHEN USOCKET::TIMEOUT USOCKET::MUSECS)))
; --> VALUES PROG1 LET SETF MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL FUNCTION
; --> VALUES SETQ
; ==>
;   (SET 'COUNT #:NEW1)
;
; caught WARNING:
;   violating package lock on COMMON-LISP:COUNT

usocket leaks file descriptors on sb-int:broken-pipe conditions

In the following code:

        (bt:make-thread
         (lambda ()
           (log-errors (bt:with-timeout (15)
                         (with-client-socket (sk s remote 19023 :element-type '(unsigned-byte 8))
                           (write-sequence resp s)))))

When write-sequence is a big long write that has its connection reset midway, the unwind-protect in with-client-socket does not seem to close the FD and I eventually run out of fds. There is a sb-int:broken-pipe condition sent that seems to be throw from within the stream close.

Trace output (never returns normally so there is no trace prints for return values.

  0: (USOCKET:SOCKET-CLOSE #<USOCKET:STREAM-USOCKET {1002D486A3}>)
    1: (CLOSE #<SB-SYS:FD-STREAM for "socket 192.168.2.1:54255, peer: 192.168.2.2:19023" {1002D497A3}>)

usocket:wait-for-input waits only in the first call

When socket-or-sockets argument is only one socket,
wait-list is created only once and event object isn't reset.

;; OPTIMIZATION: in case socket-or-sockets is an atom, create the wait-list

(let ((rv (wsa-wait-for-multiple-events 1 (wait-list-%wait wait-list)

So, usocket:wait-for-input waits only in the first call.

I made a workaround for lem editor as follows.
https://github.com/cxxxr/lem/pull/385/files#diff-c197fa296dcb24f88b3bbed4b49452bdR242

But, it might be better to specify the second argument of WSAEnumNetworkEvents.

https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-wsaenumnetworkevents

(let ((rv (wsa-enum-network-events (os-socket-handle socket) 0

MAX called with invalid number of arguments

I'm using (usocket:wait-for-input client-socket :timeout 0.1 :ready-only t) in a loop inside a thread, and getting the following error:

MAX called with invalid number of arguments: 0

I don't know why this happens, or under what circumstances; I just know that it does. Maybe there's a race condition? Maybe the socket is being closed? Either way, some kind of "invalid socket" error should be raised, rather than crashing.

wait-for-input fails to honor :ready-only t

wait-for-input is broken whenever :ready-only t is specified. This is because single-socket-p is defined to be (atom socket-or-sockets) whenever wait-for-input is invoked. This includes when it is invoked recursively to replace the list-of-usockets with a wait-list. But a wait-list is always an atom. Thus, everything after the (unless (wait-list-p socket-or-sockets) ...) form will always have single-socket-p true, regardless of the number of wait-list-waiters.

Example: suppose socket-or-sockets is initially (a b c d), all sockets, and ready-only is t. As it's not a wait-list, and not null, wait-for-input will be invoked recursively with a wait-list with four wait-list-waiters. This time single-socket-p will be t, because a wait-list is an atom. It will invoke listen and wait-for-input-internal and set the socket states accordingly. Let's suppose that only d has been found to have input available. When it comes time to return the list of ready sockets, single-socket-p will still be t, and since the first socket in the list, a, isn't ready, nil will be returned.

One fix for this would be to replace

&aux (single-socket-p (atom socket-or-sockets))

with

&aux 
(single-socket-p (or (and (wait-list-p socket-or-sockets) 
                          (= (length (wait-list-waiters socket-or-sockets)) 1))
                 (usocket-p socket-or-sockets)))

This would fail to consider a list with a single usocket as a single socket, but so does the current code.

Not receiving data after first tcp transmission SBCL Windows

I hit an issue on usocket 0.7.1 on SBCL 1.4.12 and 1.4.14 on Windows (both 32 and 64-bit) where I had a relatively simple 'consume all data' loop like so:

(loop
   :while (usocket:wait-for-input socket)
   :do (process-data (usocket:socket-stream socket)))

The first TCP packet coming in from the remote would come in fine, I would write a response, and the remote would receive it (Wireshark).
However, any data sent from the remote afterwards failed to wake up from usocket:wait-for-input.

I even tried switching to using cl:listen on the socket stream like so:

(loop
   :with stream := (usocket:socket-stream socket)
   :if (listen stream)
     :do (process-data stream)
   :else
     :do (sleep +a-sufficient-amount-of-time+))

and experienced the same exact behaviour.

Fortunately, it seems to have been fixed in one of the commits between 1af412b and the current (0b05f01).

I have not sat down to figure out exactly what caused this behaviour, but I wanted to post it here for posterity

Thanks!

INVALID-ARGUMENT-ERROR on socket-receive

Hi!
Just learning some Common Lisp with usocket. I've been trying different TCP and UDP tutorials and while making different connections on TCP I have had significant issues in recreating a simple UDP example:

(defun UDP-one-shot-V1 (&optional (port 1232))
  (let ((socket (usocket:socket-connect
		 nil
		 nil
		 :protocol     :datagram
		 :element-type '(unsigned-byte 8)
		 :local-host   "127.0.0.1"
		 :local-port   port))
	(buffer (make-array 8 :element-type '(unsigned-byte 8))))
    (unwind-protect
	 (multiple-value-bind (received size remote-host remote-port)
	     ;; NOTE: An explicit buffer can be given. If the length
	     ;; is nil buffer's length will be used.
	     (usocket:socket-receive socket buffer 8)
	   (format t "~A~%" received)
	   (usocket:socket-send socket
				(reverse received)
				size
				:host remote-host
				:port remote-port))
      (usocket:socket-close socket))))

I've tried giving nil and explicit size as buffer&size parameters, as well as buffer with nil as size too, but both of them and the example result in the same error:

Backtrace:
  0: (USOCKET::HANDLE-CONDITION #<SB-BSD-SOCKETS:INVALID-ARGUMENT-ERROR {100375B833}> #<USOCKET:DATAGRAM-USOCKET {100375B773}>)
      Locals:
        CONDITION = #<SB-BSD-SOCKETS:INVALID-ARGUMENT-ERROR {100375B833}>
        SOCKET = #<USOCKET:DATAGRAM-USOCKET {100375B773}>
  1: (SB-KERNEL::%SIGNAL #<SB-BSD-SOCKETS:INVALID-ARGUMENT-ERROR {100375B833}>)
      Locals:
        CONDITION = #<SB-BSD-SOCKETS:INVALID-ARGUMENT-ERROR {100375B833}>
        HANDLER-CLUSTERS = (((#<SB-KERNEL::CLASSOID-CELL SB-IMPL::EVAL-ERROR> . #<CLOSURE # {7F0C5FD9DE0B}>)) ((#<SB-KERNEL::CLASSOID-CELL SB-C:COMPILER-ERROR> . #<FUNCTION # {5222748B}>)) ..)
  2: (ERROR SB-BSD-SOCKETS:INVALID-ARGUMENT-ERROR :ERRNO 22 :SYSCALL "recvfrom")
      Locals:
        CONDITION = #<SB-BSD-SOCKETS:INVALID-ARGUMENT-ERROR {100375B833}>
        #:G8039 = SB-BSD-SOCKETS:INVALID-ARGUMENT-ERROR
        SB-DEBUG::MORE = (:ERRNO 22 :SYSCALL "recvfrom")
  3: (SB-BSD-SOCKETS:SOCKET-ERROR "recvfrom" 22)
      Locals:
        ERRNO = 22
        WHERE = "recvfrom"
  4: ((FLET SB-BSD-SOCKETS::WITH-SOCKET-ADDR-THUNK :IN SB-BSD-SOCKETS:SOCKET-RECEIVE) #<SB-ALIEN-INTERNALS:ALIEN-VALUE :SAP #X7F0C58001230 :TYPE (* (SB-ALIEN:STRUCT SB-BSD-SOCKETS-INTERNAL::SOCKADDR-IN (SB..
      Locals:
        SB-BSD-SOCKETS::COPY-BUFFER = #<SB-ALIEN-INTERNALS:ALIEN-VALUE :SAP #X7F0C58001250 :TYPE (* (ARRAY (SB-ALIEN:UNSIGNED 8) 1))>
        SB-BSD-SOCKETS::SIZE = 16
        SB-BSD-SOCKETS::SOCKADDR = #<SB-ALIEN-INTERNALS:ALIEN-VALUE :SAP #X7F0C58001230 :TYPE (* ..)>
  5: (SB-BSD-SOCKETS::CALL-WITH-SOCKET-ADDR #<SB-BSD-SOCKETS:INET-SOCKET 127.0.0.1:1232, fd: 3 {100375B203}> NIL #<CLOSURE (FLET SB-BSD-SOCKETS::WITH-SOCKET-ADDR-THUNK :IN SB-BSD-SOCKETS:SOCKET-RECEIVE) {7..
      Locals:
        SOCKADDR = #<SB-ALIEN-INTERNALS:ALIEN-VALUE :SAP #X7F0C58001230 :TYPE (* ..)>
        SOCKADDR-ARGS = NIL
        SOCKET = #<SB-BSD-SOCKETS:INET-SOCKET 127.0.0.1:1232, fd: 3 {100375B203}>
        THUNK = #<CLOSURE (FLET SB-BSD-SOCKETS::WITH-SOCKET-ADDR-THUNK :IN SB-BSD-SOCKETS:SOCKET-RECEIVE) {7F0C5FD9DB8B}>
  6: ((:METHOD SB-BSD-SOCKETS:SOCKET-RECEIVE (SB-BSD-SOCKETS:SOCKET T T)) #<SB-BSD-SOCKETS:INET-SOCKET 127.0.0.1:1232, fd: 3 {100375B203}> #(0 0 0 0 0 0 ...) 8 :OOB NIL :PEEK NIL :WAITALL NIL :DONTWAIT NIL..
      Locals:
        #:.DEFAULTING-TEMP. = (UNSIGNED-BYTE 8)
        SB-BSD-SOCKETS::BUFFER = #(0 0 0 0 0 0 ...)
        SB-BSD-SOCKETS::BUFFER#1 = #(0 0 0 0 0 0 ...)
        SB-BSD-SOCKETS::DONTWAIT = NIL
        SB-BSD-SOCKETS::ELEMENT-TYPE = (UNSIGNED-BYTE 8)
        LENGTH = 8
        LENGTH#1 = 8
        SB-BSD-SOCKETS::OOB = NIL
        SB-BSD-SOCKETS::PEEK = NIL
        SB-BSD-SOCKETS:SOCKET = #<SB-BSD-SOCKETS:INET-SOCKET 127.0.0.1:1232, fd: 3 {100375B203}>
        SB-BSD-SOCKETS::WAITALL = NIL
  7: ((:METHOD USOCKET:SOCKET-RECEIVE (USOCKET:DATAGRAM-USOCKET T T)) #<USOCKET:DATAGRAM-USOCKET {100375B773}> #(0 0 0 0 0 0 ...) 8 :ELEMENT-TYPE (UNSIGNED-BYTE 8)) [fast-method]
      Locals:
        USOCKET::BUFFER = #(0 0 0 0 0 0 ...)
        USOCKET::ELEMENT-TYPE = (UNSIGNED-BYTE 8)
        LENGTH = 8
        USOCKET:SOCKET = #<USOCKET:DATAGRAM-USOCKET {100375B773}>
  8: (MASTER-CLASS/SRC/SERVER-03:UDP-ONE-SHOT-V1 1232)
      Locals:
        PORT = 1232
        SOCKET = #<USOCKET:DATAGRAM-USOCKET {100375B773}>

Environment:
SBCL 1.4.16,
WSL on Windows 10

socketpair-like function

It would be nice to have a usocket API function that behaves like socketpair(2). In the BSD sockets API, this function creates a pair of connected sockets.

There would be two uses for this:

  • When creating a network server with usocket, it is not obvious how to shut down the server cleanly. I ran into this problem with hunchentoot quite a while ago, and tried to fix it in this pull request, but the solution is not perfect and still has issues in certain cases. If usocket had socketpair, shutting down a TCP accept loop could be done by using WAIT-FOR-INPUT to wait for a client to accept or a signal on a 'shutdown socket'.
  • To test TCP networking code, a lot of setup is required. Having the new function would help by removing the need to set up a listener to accept a single connection to run the test.

Thoughts about implementation

I have the following signature in mind:

socket-pair &key protocol element-type => (values socket1 socket2)

socketpair is defined by POSIX, so all Unix-like operating systems provide it with the same interface. This means that usocket backends using FFI can just wrap the C function on Unix.

However, it is not available on Windows. But a portable replacement can be implemented in Lisp using the usocket API. For TCP, the replacement would basically create a listener on localhost, connect to it, and then close the listener again, leaving only the established socket connection. You can see what this kind of replacement looks like in libcurl.

One thing to note is that POSIX socketpair does not work with all address families. It's mostly intended to be used with AF_LOCAL. The question is, what should GET-PEER-NAME and GET-LOCAL-NAME return for these sockets? I guess they could just return 127.0.0.1, 0.

usocket 0.8.4 is broken on windows version 0.8.3 does compile

usocket 0.8.4 is broken on windows version 0.8.3 does compile

; caught ERROR:
; READ error during COMPILE-FILE:
;
; Symbol "BROKEN-PIPE" not found in the SB-INT package.
;
; Line: 266, Column: 30, File-Position: 8743
;
; Stream: #<SB-INT:FORM-TRACKING-STREAM for "file C:\Users\validation\quicklisp\dists\quicklisp\software\usocket-0.8.4\backend\sbcl.lis
p" {25AFEBB1}>

debugger invoked on a UIOP/LISP-BUILD:COMPILE-FILE-ERROR in thread
#<THREAD "main thread" RUNNING {23170329}>:
COMPILE-FILE-ERROR while
compiling #<CL-SOURCE-FILE "usocket" "backend" "sbcl">

unresolved external symbol 'gettimeofday' (msvc ECL, Windows 10)

Trying to load usocket on Windows using ECL built with MSVC throws the following error:

sbcl.obj : error LNK2019: unresolved external symbol gettimeofday referenced in function L10read_select
C:\Users\USER\Documents\ecl\msvc\package\quicklisp\dists\quicklisp\software\usocket-0.8.3\backend\sbcl-tmpQ0855BGK.fas : fatal error LNK1120: 1 unresolved externals

`wait-for-input` not returning on client disconnection

Hello! I noticed that given these functions:

(defun conn-server (&key
                     (interface nil)
                     (port 8080))
  (usocket:with-socket-listener (server interface port)
    (format t "Started server at ~A:~A~%" (usocket:get-local-address server) (usocket:get-local-port server))
    (usocket:with-connected-socket (client (usocket:socket-accept server))
      (format t "Client connected from ~A:~A~%" (usocket:get-peer-address client) (usocket:get-peer-port client))
      (format t "Waiting for input~%")
      (usocket:wait-for-input client))
    (format t "Client disconnected~%")))

(defun conn-client (&key
                      (host "localhost")
                      (port 8080))
  (usocket:with-client-socket (c nil host port)))

If I have a server running, and then call (conn-client), whether from the same process or a remote one, the server enters wait-for-input and never returns.

This may or may not be related to #43 .

This is SBCL 1.4.12 on 64-bit Windows 10.

Additionally, switching from usocket:wait-for-input to an open-stream-p on the socket stream seems to never return nil either.
Maybe I'm doing something logically wrong, but I'd love guidance if that's the case.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.