2016-05-30 3 views
2

Я пытаюсь реализовать свой собственный клиент beanstalkd как способ обучения. https://github.com/kr/beanstalkd/blob/master/doc/protocol.txtgolang - bufio read multiline до (CRLF) r n delimiter

На данный момент я использую bufio для чтения в строке данных, ограниченной \n.

res, err := this.reader.ReadLine('\n')

Это нормально, когда я отправить одну команду, и читать аа одного ответа строки, как: INSERTED %d\r\n, но я нахожу трудности, когда я пытаюсь заказать работу, потому что тело работа может быть несколько строк, и как такой, я не могу использовать ограничитель \n.

Есть ли способ прочитать в буфер до CRLF?

например. когда я отправляю команду reserve. Мой ожидаемый отклик выглядит следующим образом:

RESERVED <id> <bytes>\r\n 
<data>\r\n 

Но данные могут содержать \n, так что мне нужно, чтобы читать до \r\n.

В качестве альтернативы - есть способ чтения определенного количества байтов, как указано в <bytes>, в примере ответа выше?

На данный момент у меня есть (эээ Погрузочно-разгрузочные работы удалены):

func (this *Bean) receiveLine() (string, error) { 
    res, err := this.reader.ReadString('\n') 
    return res, err 
} 

func (this *Bean) receiveBody(numBytesToRead int) ([]byte, error) { 
    res, err := this.reader.ReadString('\r\n') // What to do here to read to CRLF/up to number of expected bytes? 

    return res, err 
} 

func (this *Bean) Reserve() (*Job, error) { 

    this.send("reserve\r\n") 
    res, err := this.receiveLine() 

    var jobId uint64 
    var bodylen int 
    _, err = fmt.Sscanf(res, "RESERVED %d %d\r\n", &jobId, &bodylen) 

    body, err := this.receiveBody(bodylen) 

    job := new(Job) 
    job.Id = jobId 
    job.Body = body 

    return job, nil 
} 

ответ

6

Рез, эээ: = this.reader.Read ('\ п')

Не делает любой смысл для меня. Вы имели в виду ReadBytes/ReadSlice/ReadString?

Вам необходимо bufio.Scanner.

Определите свой bufio.SplitFunc (пример представляет собой копию bufio.ScanLines с изменениями для поиска '\ r \ n'). Измените его, чтобы он соответствовал вашему делу.

// dropCR drops a terminal \r from the data. 
func dropCR(data []byte) []byte { 
    if len(data) > 0 && data[len(data)-1] == '\r' { 
     return data[0 : len(data)-1] 
    } 
    return data 
} 


func ScanCRLF(data []byte, atEOF bool) (advance int, token []byte, err error) { 
     if atEOF && len(data) == 0 { 
      return 0, nil, nil 
     } 
     if i := bytes.Index(data, []byte{'\r','\n'}); i >= 0 { 
      // We have a full newline-terminated line. 
      return i + 2, dropCR(data[0:i]), nil 
     } 
     // If we're at EOF, we have a final, non-terminated line. Return it. 
     if atEOF { 
      return len(data), dropCR(data), nil 
     } 
     // Request more data. 
     return 0, nil, nil 
    } 

Теперь оберните свой io.Reader своим пользовательским сканером.

scanner := bufio.NewScanner(this.reader) 
scanner.Split(ScanCRLF) 
// Set the split function for the scanning operation. 
scanner.Split(split) 
// Validate the input 
for scanner.Scan() { 
     fmt.Printf("%s\n", scanner.Text()) 
} 

if err := scanner.Err(); err != nil { 
     fmt.Printf("Invalid input: %s", err) 
} 

Прочитано bufio package's Исходный код о сканере.

В качестве альтернативы - есть способ чтения определенного количества байтов, как указано в примере ответа выше?

Прежде всего, вам нужно прочитать строку «RESERVED \ r \ n» как-то.

И тогда вы можете использовать

nr_of_bytes : = read_number_of_butes_somehow(this.reader) 
buf : = make([]byte, nr_of_bytes) 
this.reader.Read(buf) 

или LimitedReader.

Но мне не нравится этот подход.

Спасибо за это - читатель.Чтение ('\ n') было опечаткой - я исправил вопрос. Я также добавил пример кода, где я до сих пор. Как вы можете видеть, я могу получить количество ожидаемых байтов тела. Не могли бы вы объяснить, почему вам не нравится идея чтения определенного количества байтов? Это кажется наиболее логичным?

Я хотел бы видеть определение Бин, особенно читателя. Представьте, этот счетчик как-то не прав.

  1. Его краткость: вам нужно найти следующее «\ r \ n» и отбросить все до этой точки? или нет? зачем вам нужен счет в первую очередь?

  2. Его больше, чем должно быть (или даже хуже его огромного!).

    2.1 Нет следующего сообщения в читателе: хорошо, чтение короче, чем ожидалось, но его штраф.

    2.2 Ожидается следующее сообщение: bah, вы читаете его часть и нет простого способа для восстановления.

    2.3 Его огромный: вы не можете выделить память, даже если сообщение составляет только 1 байт.

Эти байтовые счетчики в целом предназначены для проверки сообщения. И выглядит, как в случае с протоколом beanstalkd.

Использование сканера, синтаксический анализ сообщение, проверьте длину с ожидаемым количеством ... прибыль

UPD

Имейте в виде, по умолчанию bufio.Scanner не может прочитать более 6ки, установите максимальную длину с scanner.Buffer первый. И это плохо, потому что вы не можете изменить эту опцию «на лету», и некоторые данные, возможно, были «pre» -read от сканера.

UPD2

Думая о моем последнем обновлении. Взгляните на net.textproto, как он реализует dotReader, как простой конечный автомат. Вы можете сделать что-то подобное с первой командой чтения и проверкой «ожидаемых байтов» на полезную нагрузку.

+0

Спасибо за это - 'reader.Read ('\ n')' был опечаткой - я исправил вопрос. Я также добавил пример кода, где я до сих пор. Как вы можете видеть, я могу получить количество ожидаемых байтов тела. Не могли бы вы объяснить, почему вам не нравится идея чтения определенного количества байтов? Это кажется наиболее логичным? – Gravy

+0

Ответ добавлен в сообщение. – Darigaaz

Смежные вопросы