[plt-scheme] telnet client in scheme

From: Mike T. Machenry (dskippy at ccs.neu.edu)
Date: Thu Jun 5 01:42:07 EDT 2003

These two functions have been pretty useful for me. I use them in all the
client/server programs I write. The code is based on code Shriram wrote for
Teach Scheme! 2 but changed so that each function takes a function which
takes an input and an output port.

;; open-connection: (String Integer (InputPort OutputPort -> void) -> void)
;; open a connection to a machine and pass the ports to a handler
(define (open-connection machine port handler)
  (let-values ([(server->me me->server)
                (tcp-connect machine port)])
    (handler server->me me->server)
    (close-output-port me->server)
    (close-input-port server->me)))

;; install-listener: (Integer (InputPort OutputPort -> void) -> void)
;; creates a listener in a thread of its own and calls handler with the ports
(define (install-listener port handler)
  (let ([listener (tcp-listen port)])
    (with-handlers ([exn:break?
                     (lambda (exn)
                       (tcp-close listener))])
      (let loop ()
        (let-values ([(client->me me->client)
                      (tcp-accept listener)])
          (thread
           (lambda ()
             (handler client->me me->client)
             (close-input-port client->me)
             (close-output-port me->client))))
        (loop)))))

-mike

On Thu, Jun 05, 2003 at 02:03:56AM +0200, Toon wrote:
>   For list-related administrative tasks:
>   http://list.cs.brown.edu/mailman/listinfo/plt-scheme
> 
> Here's a simple client I've written to make chatprogs and stuff...
> The client starts to try to connect to a server from the moment you ask
> your client to connect, on failure he automatically tries to connect
> again...
> Use (client 'disconnect) to disconnect or stop the attempt...
> 
> Hope it will help you with wathever you wanted to do... It works totally
> the same as telnet anyway...
> 
> Greetz.
> toon
> 
> Usage:
> 
> (define client (create-basic-client <inputmanager>)) => inputmanager could
> be display, anyway a proc with 1 var
> (client 'set-ip "<ip-address>")
> (client 'set-port <port>)
> (client 'connect)
> 
> then use
> 
> (client "<msg>") to write to server => When you work graphical the msg will
> be a string anyway, so this
>                                                           is the easiest way
> ...
> 
> 
> the basic-client code:
> 
> (define (create-basic-client inputmanager)
>   (letrec [;;ServerIP
> 
>            (ip #f)
> 
>            ;;Serverport
> 
>            (port #f)
> 
>            ;;Inputport
> 
>            (input #f)
> 
>            ;;Outputport
> 
>            (output #f)
> 
>            ;;Are you currently trying to connect to server?
> 
>            (trying-to-connect? #f)
> 
>            ;;The reading-thread
> 
>            (return-loop-thread #f)
> 
>            ;;Connecting to server
> 
>            (connect (lambda ()
>                       (if (and (not input) (not output) ip port)
>                           (set! return-loop-thread (thread (lambda ()
> 
> (error-display-handler connection-error-manager)
>                                                              (set!
> trying-to-connect? #t)
> 
> (call-with-values
>                                                               (lambda ()
> (tcp-connect ip port))
>                                                               (lambda
> (inputport outputport)
>                                                                 (set! input
> inputport)
>                                                                 (set! output
> outputport)))
>                                                              (set!
> trying-to-connect? #f)
> 
> (return-loop)))))))
> 
>            ;;Disconnecting from server
> 
>            (disconnect (lambda (reconnect?)
>                          (if return-loop-thread
>                              (begin
>                                (if (output-port? output)
>                                    (close-output-port output))
>                                (if (input-port? input)
>                                    (close-input-port input))
>                                (set! input #f)
>                                (set! output #f)
>                                (if reconnect?
>                                    (read-me 'connect)
>                                    (begin
>                                      (kill-thread return-loop-thread)
>                                      (set! return-loop-thread #f)))))))
> 
>            ;;Reading servermsg's and sending them to the inputmanager
> 
>            (return-loop (lambda ()
>                           (let [(data (read-line input))]
>                             (if (eof-object? data)
>                                 (begin
>                                   (inputmanager "DISCONNECTED")
>                                   (read-me 'disconnect 'reconnect))
>                                 (begin
>                                   (inputmanager (car (split data "\r")))
>                                   (return-loop))))))
> 
>            ;;Automatically reconnecting on failure while trying to connect
> 
>            (connection-error-manager (lambda (string struct)
>                                        (if trying-to-connect?
>                                            (begin
>                                              (set! input #f)
>                                              (set! output #f)
>                                              (read-me 'connect)))))
> 
>            ;;Abstractions of the ADT and server-output-manager
> 
>            (read-me (lambda (m . args)
>                       (case m
>                         ((connect) (connect))
>                         ((disconnect) (disconnect (pair? args)))
>                         ((set-ip) (if (not (null? args)) (set! ip (car
> args))))
>                         ((set-port) (if (not (null? args)) (set! port (car
> args))))
>                         ((delete-ip) (set! ip #f))
>                         ((delete-port) (set! port #f))
>                         (else (if (and output (string? m))
>                                   (display (string-append m "\r\n") output)
>                                   #f)))))]
>     read-me))
> ----- Original Message ----- 
> From: "Neil W. Van Dyke" <neil at neilvandyke.org>
> To: "Chris" <chris81 at wanadoo.fr>
> Cc: "ml plt scheme" <plt-scheme at qua.cs.brown.edu>
> Sent: Wednesday, June 04, 2003 1:47 PM
> Subject: Re: [plt-scheme] telnet client in scheme
> 
> 
> >   For list-related administrative tasks:
> >   http://list.cs.brown.edu/mailman/listinfo/plt-scheme
> >
> > Chris <chris81 at wanadoo.fr> writes at 12:13 04-Jun-2003 +0200:
> > > I just want to know if a client telnet made in scheme allready exist and
> >
> > One might exist (I don't know), but writing your own is a really good
> > learning exercise.
> >
> > > and it's send me nothing back but i see the connexion opened in the
> telnetd
> > > log. Am I missing something ?
> >
> > Perhaps the Telnet server is not sending a full line; for example, maybe
> > it's just sending "Password: " without a newline sequence.  Or maybe
> > it's using a nonstandard newline convention, like CR (PLT's "read-line"
> > can be made to handle this, but there's a better way).
> >
> > Instead of using "read-line", you probably want your code to read single
> > characters or blocks of characters, rather than waiting for the server
> > to send a newline.  You will need this for other parts of the protocol
> > anyway.  Here's a simple variation on the code you posted:
> >
> >     (display "*DEBUG* Connecting...\n")
> >
> >     (define-values (p-in p-out) (tcp-connect "host" 23))
> >
> >     (display "*DEBUG* Connected.\n")
> >
> >     (let loop ()
> >       (display "*DEBUG* Waiting for character...\n")
> >       (let ((c (read-char p-in)))
> >         (printf "*DEBUG* Read character: ~S\n" c)
> >         (loop)))
> >
> > If this code doesn't read any characters from your Telnet server, then
> > it could be waiting for the client to send one or more newlines or it
> > could be waiting for the client to initiate a cryptographic
> > authentication.  Don't think too hard about "*DEBUG*" messages that
> > aren't appearing, since they might be stuck in unflushed I/O buffers.
> > Try running a protocol sniffer or packet analyzer, like the free
> > Ethereal, while you connect to your Telnet server with your system's
> > Telnet client, to see how it works.
> >
> > Once you can read and write single characters with your Telnet server,
> > take a look at the PLT procedures "read-string-avail!/enable-break" and
> > "write-string-avail/enable-break", and "object-wait-multiple" for how to
> > do this robustly and more efficiently.
> >
> > Searching for "telnet" from "http://www.rfc-editor.org/rfcsearch.html"
> > yields over 100 documents, most of which are not relevant to you.  RFC
> > 318 ("ftp://ftp.rfc-editor.org/in-notes/rfc318.txt") might be a good
> > starting point.
> >
> > -- 
> >                                              http://www.neilvandyke.org/
> 


Posted on the users mailing list.