This commit is contained in:
Ingy döt Net 2013-10-27 22:24:23 +00:00
parent 6f050a029e
commit 776bba907c
3887 changed files with 59894 additions and 7280 deletions

View file

@ -1,47 +1,24 @@
(defvar *clients* '()
"This is a list of (socket :input status) which is used with
`socket:socket-status' to test for data ready on a socket.")
(ql:quickload (list :usocket))
(defpackage :echo (:use :cl :usocket))
(in-package :echo)
(defun echo-server (port)
"Listen on `port' for new client connections and for data arriving on
any existing client connections"
(let ((server (socket:socket-server port)))
(format t "Echo service listening on port ~a:~d~%"
(socket:socket-server-host server)
(socket:socket-server-port server))
(unwind-protect
(loop
(when (socket:socket-status server 0 1)
(echo-accept-client (socket:socket-accept server
:external-format :dos
:buffered t)))
(when *clients*
(socket:socket-status *clients* 0 1)
(mapcar #'(lambda (client)
(when (eq :input (cddr client))
(echo-service-client (car client)))
(when (eq :eof (cddr client))
(echo-close-client (car client)))) *clients*)))
(socket-server-close server))))
(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-accept-client (socket)
"Accept a new client connection and add it to the watch list."
(multiple-value-bind
(host port) (socket:socket-stream-peer socket)
(format t "Connect from ~a:~d~%" host port))
(push (list socket :input nil) *clients*))
(defun echo-service-client (socket)
(let ((line (read-line socket nil nil)))
(princ line socket)
(finish-output socket)))
(defun echo-close-client (socket)
"Close a client connection and remove it from the watch list."
(multiple-value-bind
(host port) (socket:socket-stream-peer socket)
(format t "Closing connection from ~a:~d~%" host port))
(close socket)
(setq *clients* (remove socket *clients* :key #'car)))
(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)

View file

@ -1,38 +1,8 @@
(defpackage :echo
(:use :cl :usocket :cl-actors)
(:import-from :cl-actors #:self))
(in-package :echo)
(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))))
(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
(echo-send "Hello echo!" 12321)

View file

@ -1,22 +1,47 @@
(defactor tcp-naive-client (sock) (message)
(format (socket-stream sock) message)
(force-output (socket-stream sock))
next)
(defvar *clients* '()
"This is a list of (socket :input status) which is used with
`socket:socket-status' to test for data ready on a socket.")
(defparameter *client* (tcp-naive-client :sock (socket-connect "127.0.0.1" +port+)))
(defun echo-server (port)
"Listen on `port' for new client connections and for data arriving on
any existing client connections"
(let ((server (socket:socket-server port)))
(format t "Echo service listening on port ~a:~d~%"
(socket:socket-server-host server)
(socket:socket-server-port server))
(unwind-protect
(loop
(when (socket:socket-status server 0 1)
(echo-accept-client (socket:socket-accept server
:external-format :dos
:buffered t)))
(when *clients*
(socket:socket-status *clients* 0 1)
(mapcar #'(lambda (client)
(when (eq :input (cddr client))
(echo-service-client (car client)))
(when (eq :eof (cddr client))
(echo-close-client (car client)))) *clients*)))
(socket-server-close server))))
(send *client* "test~%test")
;; should print
;; test
;; test
(defun echo-accept-client (socket)
"Accept a new client connection and add it to the watch list."
(multiple-value-bind
(host port) (socket:socket-stream-peer socket)
(format t "Connect from ~a:~d~%" host port))
(push (list socket :input nil) *clients*))
(send *client* "test")
;; should print
;; test
(defun echo-service-client (socket)
(let ((line (read-line socket nil nil)))
(princ line socket)
(finish-output socket)))
(defparameter *client2* (tcp-naive-client :sock (socket-connect "127.0.0.1" +port+)))
(defun echo-close-client (socket)
"Close a client connection and remove it from the watch list."
(multiple-value-bind
(host port) (socket:socket-stream-peer socket)
(format t "Closing connection from ~a:~d~%" host port))
(close socket)
(setq *clients* (remove socket *clients* :key #'car)))
(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
(echo-server 12321)

View file

@ -0,0 +1,33 @@
Class EchoSocket
Inherits TCPSocket
Sub DataAvailable()
If Instr(Me.LookAhead, EndofLine.Windows) > 0 Then
Dim data As String = Me.ReadAll
Dim lines() As String = Split(data, EndofLine.Windows)
For i As Integer = 0 To Ubound(lines)
Me.Write(lines(i) + EndOfLine.Windows)
Print(lines(i))
Next
End If
End Sub
End Class
Class EchoServer
Inherits ServerSocket
Function AddSocket() As TCPSocket
Return New EchoSocket
End Function
End Class
Class App
Inherits ConsoleApplication
Function Run(args() As String) As Integer
Listener = New EchoServer
Listener.Port = 12321
Listener.Listen()
While True
DoEvents() 'pump the event loop
Wend
End Function
Private Listener As EchoListener
End Class