set a timeout to tcp-connect

34 views
Skip to first unread message

mazert

unread,
Jul 17, 2015, 9:31:59 AM7/17/15
to racket...@googlegroups.com
Hello,

When i try to connect on smtp.google.com on port 587 (submission)
through tcp-connect, it works perfectly ; but if instead of 587 i put
666 (for example) it stucks cause the server dont answer.

So after reading fews hours the doc, I didn't found a native solution to
implement this, so I programmed a thing like this :

#lang racket

(define (foo)
(let* ([a (thread (λ ()
(let-values ([(p-in p-out) (tcp-connect
"google.com" 80)])
(kill-thread b))))]
[b (thread (λ ()
(sleep 2)
(printf "I'll kill you !")
(kill-thread a)))])
(void)))

The only problem is that b have to be defined before killing him.. :p .
Someone can help me to make it working ? Or have a better idea to do
implement what i need ?

Thanks in advance.

Matthias Felleisen

unread,
Jul 17, 2015, 10:16:23 AM7/17/15
to mazert, racket...@googlegroups.com

On Jul 17, 2015, at 9:31 AM, mazert <rom...@elgeekador.net> wrote:

> #lang racket
>
> (define (foo)
> (let* ([a (thread (λ ()
> (let-values ([(p-in p-out) (tcp-connect "google.com" 80)])
> (kill-thread b))))]
> [b (thread (λ ()
> (sleep 2)
> (printf "I'll kill you !")
> (kill-thread a)))])
> (void)))


#lang racket

(define (foo)
(letrec ([a (thread (λ ()
(let-values ([(p-in p-out) (tcp-connect "google.com" 80)])
(printf "I will kill b\n")
(kill-thread b))))]
[b (thread (λ ()
(displayln `(hello world))
(sleep 2)
(printf "I'll kill a\n")
(kill-thread a)))])
(void)))


This actually works. But I am not sure why you'd want to do something like that.








mazert

unread,
Jul 17, 2015, 10:59:08 AM7/17/15
to racket...@googlegroups.com
Le 17/07/2015 16:16, Matthias Felleisen a écrit :
> This actually works. But I am not sure why you'd want to do something like that.

Ah yes, letrec is what I was looking for :) . The goal was to test a
port with a specific timeout (here I set it to two for dev purposes, but
10 is better). I rewrited foo with better comments below.

Just to be sure, I cant have the prompt return without using a
(thread-wait thd) function ?

1) The port is not good so b kills a, then b ends. The program continue.
2) But if I set (thread-wait a), b kills a, then b ends. As a was
killed, it is considered as terminated so it is the same as 1) ?



#lang racket

(define (foo)
(letrec ([a (thread (λ ()
(let-values ([(p-in p-out) (tcp-connect
"smtp.gmail.com" 5887)])
(printf "Server port is good")
(kill-thread b))))]
[b (thread (λ ()
(displayln `(hello world))
(sleep 2)
(printf "Server port is not good")
(kill-thread a)))])
(thread-wait b)))


George Neuner

unread,
Jul 17, 2015, 11:06:18 AM7/17/15
to Matthias Felleisen, racket users
Won't the connection attempt eventually time out and cause an exception when the TCP stack closes the socket?  I know the default connect timeout is rather long though (150 seconds?).

George

Matthew Flatt

unread,
Jul 17, 2015, 11:08:44 AM7/17/15
to mazert, racket...@googlegroups.com
At Fri, 17 Jul 2015 16:58:50 +0200, mazert wrote:
> Le 17/07/2015 16:16, Matthias Felleisen a écrit :
> > This actually works. But I am not sure why you'd want to do something like
> that.
>
> Ah yes, letrec is what I was looking for :) . The goal was to test a
> port with a specific timeout (here I set it to two for dev purposes, but
> 10 is better). I rewrited foo with better comments below.
>
> Just to be sure, I cant have the prompt return without using a
> (thread-wait thd) function ?
>
> 1) The port is not good so b kills a, then b ends. The program continue.
> 2) But if I set (thread-wait a), b kills a, then b ends. As a was
> killed, it is considered as terminated so it is the same as 1) ?


Yes.

Beware, though, that there's a race condition here:

> #lang racket
>
> (define (foo)
> (letrec ([a (thread (λ ()
> (let-values ([(p-in p-out) (tcp-connect
> "smtp.gmail.com" 5887)])
> (printf "Server port is good")
> (kill-thread b))))]
> [b (thread (λ ()
> (displayln `(hello world))
> (sleep 2)
> (printf "Server port is not good")
> (kill-thread a)))])
> (thread-wait b)))

It's possible (but unlikely) for the thread bound to `a` to complete
and try to kill `b` before the variable `b` is initialized.

Here's what I'd do:

* Create a custodian to manage the thread, and shut the custodian down
instead of killing the thread. That way, in addition to killing the
thread, any network resources are also released.

* Instead of starting a timer thread, use `sync/timeout`.

(define (foo)
(define c (make-custodian))
(define t (parameterize ([current-custodian c])
(thread
(λ ()
(let-values ([(p-in p-out)
(tcp-connect "smtp.gmail.com" 5887)])
(printf "Server port is good"))))))
(displayln `(hello world))
(unless (sync/timeout 2 t) ; produces #f on timeout
(printf "Server port is not good")
(custodian-shutdown-all c)))


mazert

unread,
Jul 17, 2015, 1:01:43 PM7/17/15
to racket...@googlegroups.com
Le 17/07/2015 17:08, Matthew Flatt a écrit :
> It's possible (but unlikely) for the thread bound to `a` to complete
> and try to kill `b` before the variable `b` is initialized.

Ah ok, yes that is risky in this case, plus we didn't close the tcp
connection.
>
> Here's what I'd do:
>
> * Create a custodian to manage the thread, and shut the custodian down
> instead of killing the thread. That way, in addition to killing the
> thread, any network resources are also released.
>
> * Instead of starting a timer thread, use `sync/timeout`.
>
> (define (foo)
> (define c (make-custodian))
> (define t (parameterize ([current-custodian c])
> (thread
> (λ ()
> (let-values ([(p-in p-out)
> (tcp-connect "smtp.gmail.com" 5887)])
> (printf "Server port is good"))))))
> (displayln `(hello world))
> (unless (sync/timeout 2 t) ; produces #f on timeout
> (printf "Server port is not good")
> (custodian-shutdown-all c)))

That is an elegant solution :) . So I read the doc about custodian with
parameterize to know more, but it is always a little mysterious for me.

when : (parameterize ([current-custodian c])
We say that we run the code in the 'body' part with a c parameter that
contain an empty custodian.
But we never added something to c, how the connections are 'linked' to
c, when we use custodian-shutdown-all ? It is automatic for all
thread/network/streams etc... functions in the 'body' part of
(parameterize) ?

When we bind the (parameterize) to a variable, the type is the type of
last result in the 'body' part in (parameterize) ? , So as a thread is
an evt, we can use it in sync/timeout.

mazert

unread,
Jul 17, 2015, 1:14:16 PM7/17/15
to racket...@googlegroups.com
Le 17/07/2015 17:06, George Neuner a écrit :
> Won't the connection attempt eventually time out and cause an exception
> when the TCP stack closes the socket? I know the default connect
> timeout is rather long though (150 seconds?).

Yes it will close for sure but after a long time (maybe 150 sec as you
said). Also it can server side maybe...

I'm not a TCP/IP specialist so surely there is a better way to follow.
One day I'll read a little the book mentionned in racket doc :

TCP/IP Illustrated, Volume 1 by W. Richard Stevens
http://www.jtbookyard.com/uploads/6/2/9/3/6293106/tcp_ip_illustrated_volume_1_the_protocols_2nd_edit.pdf

Reply all
Reply to author
Forward
0 new messages