Skip to content

Instantly share code, notes, and snippets.

@svspire
Last active February 18, 2021 07:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save svspire/533deeb2cae78f9e41db254e001e4e59 to your computer and use it in GitHub Desktop.
Save svspire/533deeb2cae78f9e41db254e001e4e59 to your computer and use it in GitHub Desktop.
; Inspired by emails from barthes@utc.fr to info-mcl in February 2021
; Uncomment one of the following depending on whether this machine is a broadcaster or receiver
;(pushnew :broadcaster *features*)
;(pushnew :receiver *features*)
(defparameter *count* 0)
(defparameter *listening-port* 40001)
(defparameter *remote-host* "10.10.10.255" "or 192.168.1.255 or whatever matches your local network")
#+BROADCASTER
(defparameter *send-socket* (make-socket
:type :datagram
:format :binary ; CCL can't do :text format for UDP (yet)
:connect :active ; default on CCL
:remote-port *listening-port*
:remote-host *remote-host*
; :local-port *port* ; do NOT use this argument!
:broadcast t
:reuse-address t
))
#+BROADCASTER
(defun bcast (msg)
(let* ((buf (encode-string-to-octets msg :external-format :ISO-8859-1)))
(send-to *send-socket* buf (length buf))
*count*))
;(bcast (format nil "UDP Test Broadcast Message (~A) #~S" *remote-host* (incf *count*)))
#+BROADCASTER
(defun bcast-n (n)
(dotimes (i n) ;
(bcast (format nil "UDP Test Broadcast Message (~A) #~S" *remote-host* (incf *count*)))))
; This produces better results than one-at-a-time in the listener
; (bcast-n 10)
#+RECEIVER
(defun s-receive ()
(let ((receive-socket nil))
(unwind-protect
(progn
(setf receive-socket (make-socket
:type :datagram
:connect :passive
:format :binary
:local-port *listening-port*))
(loop
(print "Ready to receive incoming message...")
(multiple-value-bind (buf nbytes sender-host sender-port)
(receive-from receive-socket 1000 :want-socket-address-p nil)
(declare (ignore nbytes))
(format t "~%RECV from ~A:~D msg: ~S"
(ipaddr-to-dotted sender-host)
sender-port
(decode-string-from-octets buf :external-format :ISO-8859-1)))))
(close receive-socket))))
; (s-receive)
@svspire
Copy link
Author

svspire commented Feb 17, 2021

A few points:
-- You need to set up two sockets; one active for the sender and one passive for the receiver, rather than trying to make one socket do both because they have different management requirements.
-- Above doesn't necessarily mean you cannot send data as a reply from the passive socket to the active one but this code doesn't demonstrate that. The simplest approach is to create yet another socket pair for passive machines to reply back on (if replies are desired).
-- It's generally easiest not to specify a local-port on an active socket.
-- It's a good idea to wrap the passive socket in an unwind-protect and close it if an error happens. This also implies the passive socket should not be a global variable but should be reestablished every time #'s-receive is called.
-- UDP packets can look like they're getting lost when actually they're just getting seen at unpredictable times, out of order. If you run (s-receive) on the receiving machine and (bcast-n 10) on the sending machine, you'll see clearly that packets are being received out of order. That's just the nature of UDP.

As you've discovered, UDP is a bit finicky to get working. There are several layers that could be added to the example to make it more reliable (such as calling ccl::process-input-wait to gain more control over the receiver); see https://github.com/svspire/ping for an example of that (although that example is ICMP rather than UDP, many of the principles are similar.)

SS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment