Echo server: Difference between revisions
Content added Content deleted
(→{{header|Common Lisp}}: added a proper usocket example. Removed my earlier actors-based example) |
|||
Line 545: | Line 545: | ||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |
||
{{improve|Common Lisp|There should be a http://common-lisp.net/project/usocket/ example.}} |
|||
Here is a basic [http://common-lisp.net/project/usocket/ :usocket] example (it should work with any Common Lisp): |
|||
Sockets is not a standard part of Common Lisp but many implementations have support for this. The following example {{works with|CLISP}} |
|||
<lang lisp>(ql:quickload (list :usocket)) |
|||
(defpackage :echo (:use :cl :usocket)) |
|||
(in-package :echo) |
|||
(defun read-all (stream) |
|||
(loop for char = (read-char-no-hang stream nil :eof) |
|||
until (or (null char) (eq char :eof)) collect char into msg |
|||
finally (return (values msg char)))) |
|||
(defun echo-server (port &optional (log-stream *standard-output*)) |
|||
(let ((connections (list (socket-listen "127.0.0.1" port :reuse-address t)))) |
|||
(unwind-protect |
|||
(loop (loop for ready in (wait-for-input connections :ready-only t) |
|||
do (if (typep ready 'stream-server-usocket) |
|||
(push (socket-accept ready) connections) |
|||
(let* ((stream (socket-stream ready)) |
|||
(msg (concatenate 'string "You said: " (read-all stream)))) |
|||
(format log-stream "Got message...~%") |
|||
(write-string msg stream) |
|||
(socket-close ready) |
|||
(setf connections (remove ready connections)))))) |
|||
(loop for c in connections do (loop while (socket-close c)))))) |
|||
(echo-server 12321) |
|||
</lang> |
|||
It's single threaded, so you can't REPL around with a running server. You'll need to start a second Lisp prompt, load the above and |
|||
<lang lisp>(defun echo-send (message port) |
|||
(with-client-socket (sock str "127.0.0.1" port) |
|||
(write-string message str) |
|||
(force-output str) |
|||
(when (wait-for-input sock :timeout 5) |
|||
(coerce (read-all str) 'string)))) |
|||
(echo-send "Hello echo!" 12321) |
|||
</lang> |
|||
The return value of that call should be "You said: Hello echo!". |
|||
The usocket library notwithstanding, sockets are not a standard part of Common Lisp, but many implementations provide them. Here is a CLISP-specific example: {{works with|CLISP}} |
|||
<lang lisp>(defvar *clients* '() |
<lang lisp>(defvar *clients* '() |
||
"This is a list of (socket :input status) which is used with |
"This is a list of (socket :input status) which is used with |
||
Line 594: | Line 636: | ||
(echo-server 12321)</lang> |
(echo-server 12321)</lang> |
||
Here is an actors-based example that uses the http://common-lisp.net/project/usocket/ library. {{works with|SBCL}} |
|||
<lang lisp>(defpackage :echo |
|||
(:use :cl :usocket :cl-actors) |
|||
(:import-from :cl-actors #:self)) |
|||
(in-package :echo) |
|||
(defun read-line-no-hang (stream) |
|||
"Reads a line from stream. Returns a partial line rather than hanging." |
|||
(apply #'values |
|||
(loop for char = (read-char-no-hang stream nil :eof) |
|||
until (or (null char) (eq :eof char) (char= #\newline char)) |
|||
collect char into str |
|||
finally (return (list (when str (coerce str 'string)) char))))) |
|||
(defactor tcp-server (server handler connections) (message) |
|||
(loop for ready in (wait-for-input (cons server connections) :ready-only t) |
|||
do (if (typep ready 'stream-server-usocket) |
|||
(push (socket-accept server) connections) |
|||
(multiple-value-bind (line last-char) (read-line-no-hang (socket-stream ready)) |
|||
(when (eq last-char :eof) |
|||
(delete ready connections) |
|||
(socket-close ready)) |
|||
(send handler ready line)))) |
|||
(send self nil) |
|||
next) |
|||
(defactor echo-handler (stream) (socket input) |
|||
(format stream "~a~%" input) |
|||
(format (socket-stream socket) input) |
|||
next) |
|||
(defparameter +port+ 12321) |
|||
(defparameter *handler* (echo-handler :stream *standard-input*) |
|||
"Accepts and deals with socket messages.") |
|||
(defparameter *server* |
|||
(tcp-server :server (socket-listen "127.0.0.1" +port+) |
|||
:handler *handler*) |
|||
"Listens on the specified address+port. Passes socket input to specified handler.") |
|||
(send *server* :ping) ;; get the server started</lang> |
|||
In order to run this example, load the :cl-actors and :usocket libraries, start up a socket connection to 127.0.0.1:12321 and send something. In keeping with the theme, here's an actors-based way of doing it: |
|||
<lang lisp> |
|||
(defactor tcp-naive-client (sock) (message) |
|||
(format (socket-stream sock) message) |
|||
(force-output (socket-stream sock)) |
|||
next) |
|||
(defparameter *client* (tcp-naive-client :sock (socket-connect "127.0.0.1" +port+))) |
|||
(send *client* "test~%test") |
|||
;; should print |
|||
;; test |
|||
;; test |
|||
(send *client* "test") |
|||
;; should print |
|||
;; test |
|||
(defparameter *client2* (tcp-naive-client :sock (socket-connect "127.0.0.1" +port+))) |
|||
(send *client2* "woo!") |
|||
;; should print |
|||
;; woo! |
|||
;; despite the fact that *client* isn't reading anything, and didn't terminate its last message with a newline |
|||
</lang> |
|||
=={{header|D}}== |
=={{header|D}}== |