球拍,在不知道什么的情况下从端口读取输入

Racket, reading input from a port without knowing what

我目前正在设计一个程序,其中部分程序文件 运行 在 Raspberry Pi 上,另一部分 运行 在我的计算机上。

为了在它们之间进行通信,我通过 TCP/IP 发送消息。 所以要阅读收到的消息,我使用 (read port)。然后我做一些计算并将答案发回。

现在我注意到,当答案是一个数字时,我在另一边没有收到那个答案(我不知道是不是因为它是一个数字,我想是)。虽然已经寄出。之后它会导致不正确的读取(我想是因为它仍在缓冲区中)。

这就是我发送消息的方式:

#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)))))

这就是我读取传入消息(在 Raspberry Pi 上)并发回回复的方式:

#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)

当 运行 运行程序时,我看到发送了一堆消息并正确回答了。 但后来我看到一条消息,希望得到答案,但没有得到答案。 这是 raspberry pi 上显示的内容:

Destination : 2
Message : getStationHoogte
Arguments : '()
Expects answer? #t
Answer : 15

所以消息被执行了,结果是 15(我检查了它,这是它应该产生的结果,所以到目前为止我很高兴)。

请注意 Answer : ... 的显示恰好发生在发送答案之前。

但是在我的电脑上我看到了这个:

Destination : 2
Message : getStationHoogte
Arguments : ()
Expects answer? #t
Answer :

我觉得很奇怪的是答案什么都没有? 这怎么可能?我使用 "read" 来读取收到的答案,这是一个阻塞操作。它怎么会检测到一个答案(我假设在这个例子中是 15 个)(因为它停止阻塞)并产生 "nothing".

这种行为的原因可能是什么?未发送消息(在本例中为数字)的原因可能是什么?

(display answer) 替换为 (write answer) 以查看打印的内容。

虽然我无法从您发布的内容中判断出确切的问题是什么,但我有几点建议:

  1. 您可以将 define-valuestcp-connect 的结果一起使用,如下所示:

    (define-values (in out) (tcp-connect "localhost" 6667))
    
  2. 将每条消息设为单个 writeread 可能更简单、更可靠。为此,只需将所有值放入 list(或者可能是 #:prefab struct)中。您可以使用 match 轻松再次提取元素。例如像这样的东西(我没有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)]))
    

关于换行的更新:

现在您正在 writeing 和 reading s 表达式 (lists),不需要换行符来分隔消息 -- 括号现在起到了这个作用相反。

的作用是缓冲 -- 因此 flush-output。当 expect-answer?#t 时,请务必在运行的任何代码中使用它。

顺便说一句,您可以使用file-stream-buffer-mode更改某些端口(包括TCP端口)的缓冲模式。默认情况下可能是 'block ,这就是为什么你之前需要换行符。如果您将模式更改为 'line,它可能会起作用。但既然您使用的是 s 表达式,我认为这应该无关紧要。您应该在每条消息(或回复)发送后使用 flush-output