From: ······@gmail.com
Subject: sockets and streams and sbcl
Date: 
Message-ID: <1156194484.881223.181040@m79g2000cwm.googlegroups.com>
Hello,
I have a rather SBCL specific question on reading and writing from
sockets.

I've been trying to figure out how to receive a connection from telnet
and both read and write to it.

At the moment, reading from it is fairly trivial using
socket-make-stream, but I can't seem to write to the stream using
format the way I would have expected, instead I get an error saying
that it is not a character output stream.

What is the appropriate way to send and receive text over a connection
like this?

Thanks
From: Lars Rune Nøstdal
Subject: Re: sockets and streams and sbcl
Date: 
Message-ID: <pan.2006.08.22.09.26.45.310627@gmail.com>
On Mon, 21 Aug 2006 14:08:05 -0700, ······@gmail.com wrote:

> Hello,
> I have a rather SBCL specific question on reading and writing from
> sockets.
> 
> I've been trying to figure out how to receive a connection from telnet and
> both read and write to it.


Here is one way of doing this:



(defpackage :telnet-server
  (:use :cl :sb-bsd-sockets)
  (:import-from :sb-thread :destroy-thread :thread-alive-p))
(in-package :telnet-server)


(defmacro with-thread (name &body body)
  "Defines a thread that executes `body'. Returns the thread-instance."
  `(sb-thread:make-thread
    (lambda ()
      ,@body)
    :name ,name))


(defun default-handler (stream)
  (write-line "incoming connection ...")
  (format t "User said: ~A~%" (read-line stream)) (finish-output)
  (format stream "Hello, this is the response from the server!~%"))


(defparameter *server-thread* nil)


(defun telnet-server (&key (host #(0 0 0 0)) (port 7221) (handler #'default-handler))
  (when (and *server-thread* (thread-alive-p *server-thread*))
    (ignore-errors (destroy-thread *server-thread*)))
  (setf *server-thread*
        (with-thread "server-thread"
          (let ((server-socket (make-instance 'inet-socket :type :stream :protocol :tcp)))
            (unwind-protect
                 (progn
                   (setf (sockopt-reuse-address server-socket) t)
                   (socket-bind server-socket host port)
                   (socket-listen server-socket 10)
                   (loop
                      (let ((incoming-stream
                             (socket-make-stream (socket-accept server-socket)
                                                 :output t :input t
                                                 :element-type :default
                                                 :external-format :utf-8
                                                 :buffering :none)))
                        (with-thread "incoming-stream"
                          (unwind-protect
                               (funcall handler incoming-stream)
                            (ignore-errors (close incoming-stream)))))))
              (ignore-errors (socket-close server-socket)))))))
(export 'telnet-server)



For some reason the output generated in `default-handler'- which I
expected would go to the REPL-buffer - seems to go to the
`*inferiour-lisp*' buffer. I had this problem once before, but fixed it by
adding `(setf swank:*globally-redirect-io* t)' in `~/.swank.lisp' but this
doesn't seem to work anymore.


-- 
Lars Rune Nøstdal
http://lars.nostdal.org/