I'm currently designing a program in which part of the program files run on a Raspberry Pi and the other part runs on my computer.
To communicate between them I send messages over TCP/IP.
So to read incoming messages, I use (read port)
. Then I do some calculations and send the answer back.
Now I noticed that when the answer is a number I don't receive that answer on the other side (I don't know if it is because it's a number or not, I assume it is). Although it has been sent. And afterwards it causes incorrect reads (I suppose because it's still in the buffer).
So this is how I send messages :
#lang racket
; Not important
(require (rename-in racket/tcp
(tcp-connect racket-tcp-connect)
(tcp-listen racket-tcp-listen)))
(define (tcp-connect adress port)
(let-values ([(port-in port-out) (racket-tcp-connect adress port)])
(cons port-in port-out)))
;;;;
(define ports (tcp-connect "localhost" 6667))
(define in (car ports))
(define out (cdr ports))
(define (send destination message expectAnswer? . arguments)
(write-byte destination out) ; Send the destination (is a number < 256) (so that I know on the other side which object I have to send the message to).
(newline out) ; I noticed that if I don't do this, sometimes the message won't be sent.
(write message out)
(newline out)
(write arguments out)
(newline out)
(write expectAnswer? out)
(newline out)
(flush-output out)
(display "destination : ") (display destination) (newline)
(display "Message : ") (display message) (newline)
(display "Arguments : ") (display arguments) (newline)
(display "Expects an answer? ") (display expectAnswer?) (newline)
(when expectAnswer?
(let ((answer (read in)))
(if (eof-object? answer)
'CC ; CC = Connection Closed
(begin (display "Answer : ")(display answer)(newline)(newline)
answer)))))
And this is how I read incoming messages (on the Raspberry Pi) and send an answer back :
#lang racket
; Not important
(require (rename-in racket/tcp
(tcp-listen racket-tcp-listen)
(tcp-accept racket-tcp-accept)))
(define (tcp-accept port)
(let-values ([(port-in port-out) (racket-tcp-accept (racket-tcp-listen port))])
(cons port-in port-out)))
;;;;
(define ports (tcp-accept 6667))
(define in (car ports))
(define out (cdr ports))
(define (executeMessage destination message argumentList expectAnswer?)
(let ((destinationObject (decode destination)) ; This is the object that corresponds to the number we received
(answer '()))
(if (null? argumentList)
(set! answer (destinationObject message))
(set! answer (apply (destinationobject message) argumentList)))
(display "Destination : ")(display destination)(newline)
(display "Message : ")(display message)(newline)
(display "Arguments : ")(display argumentList)(newline)
(display "Expects answer? ")(display expectAnswer?) (newline)
(display "Answer : ")(display answer)(newline)(newline)
; We send the answer back if it is needed.
(when expectAnswer?
(write answer out)
(newline out) ; Because I noticed that if I don't to this, it won't be sent.
(flush-output out))))
; We call this function to skip the newlines that are send "(newline out)"
(define (skipNewline)
(read-byte in))
(define (listenForMessages)
(when (char-ready? in) ; Could be omitted.
; A message was sent
(let ((destination (read-byte in))
(message (begin (skipNewline) (read in)))
(argumentList (begin (skipNewline) (read in)))
(expectAnswer? (begin (skipNewline) (read in))))
(skipNewline)
(executeMessage destination message argumentList expectAnswer?)))
(listenForMessages))
(listenForMessages)
When running the program I see a bunch of messages being sent and answered correctly. But then I see a message which expects an answer and doesn't get one. This is what is displayed on the raspberry pi :
Destination : 2
Message : getStationHoogte
Arguments : '()
Expects answer? #t
Answer : 15
So the message was executed and the result was 15 (I checked it and that's the result it was supposed to produce, so I'm happy so far).
Notice that the display of Answer : ...
happens just before sending the answer.
But on my computer I read this :
Destination : 2
Message : getStationHoogte
Arguments : ()
Expects answer? #t
Answer :
What I find really really strange is that the answer is nothing? How is that possible? I use "read" for reading incoming answers, that's a blocking operation. How can it be that it detects an answer (I would suppose 15 in this example) (because it stops blocking) and yet produce "nothing".
What could be the reason of this behaviour? What could be the reason a message (in this case a number) isn't send?
Although I can't tell from what you posted what the exact problem is, I have a couple suggestions:
You can use define-values
with the result of tcp-connect
, like so:
(define-values (in out) (tcp-connect "localhost" 6667))
It might be simpler and more reliable for each message to be a single write
and read
. To do so, simply put all the values inside a list
(or maybe a #:prefab
struct
). You can use match
to easily extract the elements again. For example something like this (which I haven't run/tested):
(define (send destination message expect-answer? . arguments)
(write (list destination message expect-answer? arguments)
out)
(newline out) ;do you actually need this?
(flush-output out) ;you definitely do want this!
(when expect-answer?
(match (read in)
[(? eof-object?) 'CC] ; CC = Connection Closed
[answer (printf "Answer : ~a\n" answer)])))
(define (listen-for-messages)
(match (read in)
[(? eof-object?) 'CC]
[(list destination message expect-answer? arguments)
(execute-message destination message arguments expect-answer?)
(listen-for-messages)]))
Update about newlines:
Now that you're write
ing and read
ing s-expressions (list
s), newlines aren't needed to separate messages -- parentheses now serve that role instead.
What does matter is buffering -- ergo flush-output
. And be sure to use it in whatever code runs when expect-answer?
is #t
, too.
By the way, you can change the buffering mode for some kinds of ports (including TCP ports) with file-stream-buffer-mode. Probably it was 'block
by default and that's why you needed newlines, before. It might have worked if you'd changed the mode to 'line
, instead. But now that you're using s-expressions I don't think it should matter. You should just use flush-output
after each message (or answer) is sent.