Racket, чтение ввода из порта, не зная, что

В настоящее время я разрабатываю программу, в которой часть программных файлов запускается на 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 :

Что мне кажется действительно очень странным, так это то, что ответ - ничего? Как это возможно? Я использую «чтение» для чтения входящих ответов, это блокирующая операция. Как может быть, что он обнаруживает ответ (я бы предположил, что 15 в этом примере) (потому что он перестает блокировать) и все же не выдает «ничего».

Что может быть причиной такого поведения? По какой причине сообщение (в данном случае номер) не отправляется?


person Kevin    schedule 06.04.2015    source источник
comment
добавить тег языка программирования   -  person Marcus Müller    schedule 06.04.2015
comment
Забыл, сейчас добавил ;)   -  person Kevin    schedule 06.04.2015


Ответы (2)


Хотя я не могу сказать из того, что вы опубликовали, в чем именно заключается проблема, у меня есть пара предложений:

  1. Вы можете использовать define-values с результатом tcp-connect, например:

    (define-values (in out) (tcp-connect "localhost" 6667))
    
  2. Было бы проще и надежнее, если бы каждое сообщение состояло из одного write и read. Для этого просто поместите все значения в list (или, может быть, #:prefab struct). Вы можете использовать match, чтобы снова легко извлечь элементы. Например, что-то вроде этого (которое я не запускал/не тестировал):

    (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)]))
    

Обновление о новых строках:

Теперь, когда вы writeсоставляете и readсоставляете s-выражения (lists), новые строки не нужны для разделения сообщений — вместо этого эту роль выполняют круглые скобки.

Что имеет значение, так это буферизация -- ergo flush-output. И обязательно используйте его в любом коде, работающем, когда expect-answer? равно #t.

Кстати, вы можете изменить режим буферизации для некоторых типов портов (включая порты TCP) с помощью file-stream-buffer-mode. Вероятно, по умолчанию это было 'block, и поэтому раньше вам были нужны символы новой строки. Это могло бы сработать, если бы вместо этого вы изменили режим на 'line. Но теперь, когда вы используете s-выражения, я не думаю, что это имеет значение. Вы должны просто использовать flush-output после отправки каждого сообщения (или ответа).

person Greg Hendershott    schedule 06.04.2015
comment
Действительно, это спасает меня от многих проблем! Может ли кто-нибудь сказать мне, когда я должен отправить новую строку, а когда нет? Потому что я заметил, что если я не отправляю новые строки после каждой записи, у меня иногда возникают проблемы (неправильные чтения). - person Kevin; 07.04.2015

Замените (display answer) на (write answer), чтобы увидеть, что напечатано.

person soegaard    schedule 06.04.2015
comment
Спасибо, дома проверю! - person Kevin; 06.04.2015