Метод формирования сообщений фиксированного размера TCP в Golang - PullRequest
0 голосов
/ 17 июня 2020

Я не могу понять, как работает формирование сообщения с использованием заголовка длины префикса фиксированного размера.

Говорят, что массив байтов фиксированного размера будет содержать длину отправляемого сообщения. Но как бы вы определяли массив байтов фиксированного размера, в частности, в Golang.

Скажем, это мое сообщение:

Hello

Его длина равна 5.

Итак если я хочу отправить это через поток TCP, чтобы убедиться, что я получаю все сообщение на другом конце, мне нужно будет сказать ему, сколько байтов он должен прочитать.

Простой заголовок будет length:message:

5:Hello // [53 58 104 101 108 108 111]

Но если длина сообщения увеличивается в 10 раз каждый раз, байтов будет больше. Таким образом, размер заголовка составляет c динамически.

36:Hello, this is just a dumb question. // [51 54 58 72 101 108 108 111 44 32 116 104 105 115 32 105 115 32 106 117 115 116 32 97 32 100 117 109 98 32 113 117 101 115 116 105 111 110 46]

Итак, здесь 36 занимает 2 байта.

Один из подходов, о котором я пришел, - это рассмотреть максимальную длину сообщения для протокол. Скажем, 10 КБ = 10240 байт. Затем добавьте к длине сообщения начальные 0. Таким образом, я уверен, что у меня будет фиксированный 5-байтовый заголовок.

Будет ли это работать для всех случаев?

Если да, что, если у меня есть сообщение размером более 10 КБ , должен ли я разделить его на 2 сообщения?

Если нет, то каковы другие решения?

Я хочу реализовать решения в Golang.

ОБНОВЛЕНИЕ 1:

Я читал о порядке байтов, хотя не мог понять, что они делают, что вызывает байты фиксированной длины. Но я нашел пример в python и попытался записать его в go следующим образом:

Клиент:

    const maxLengthBytes = 8
    conn, err := net.Dial("tcp", "127.0.0.1:9999")
    if err != nil {
        fmt.Println(err)
        return
    }
    message := "Hello, this is just a dumb question"
    bs := make([]byte, maxLengthBytes)
    binary.LittleEndian.PutUint64(bs, uint64(len(text)))
    bytes := append(bs, []byte(text)...)
    conn.Write(bytes)

Сервер:

    listener, err := net.ListenTCP("tcp", &net.TCPAddr{Port: 9999})
    if err != nil {
        fmt.Println(err)
        return
    }
    for {
        tcp, err := listener.AcceptTCP()
        if err != nil {
            fmt.Println(err)
            continue
        }
        go Reader(tcp)
    }

func Reader(conn *net.TCPConn) {
    foundLength := false
    messageLength := 0
    for {
        if !foundLength {
            var b = make([]byte, maxLengthBytes)
            read, err := conn.Read(b)
            if err != nil {
                fmt.Println(err)
                continue
            }
            if read != 8 {
                fmt.Println("invalid header")
                continue
            }
            foundLength = true
            messageLength = int(binary.LittleEndian.Uint64(b))
        } else {
            var message = make([]byte, messageLength)
            read, err := conn.Read(message)
            if err != nil {
                fmt.Println(err)
                continue
            }
            if read != messageLength {
                fmt.Println("invalid data")
                continue
            }
            fmt.Println("Received:", string(message))
            foundLength = false
            messageLength = 0
        }
    }
}

1 Ответ

1 голос
/ 17 июня 2020

Пожалуйста, обратитесь к моему ответу в этом посте TCP-клиент для Android: текст не получен полностью

По сути, вы должны определить, как данные хранятся / форматируются. Например:

  1. Мы сохраняем длину префикса как int32 (4 байта) с прямым порядком байтов. Он отличается от твоего.

  2. В вашем решении длина равна string, зафиксировать длину сложно. Для вашего решения вы должны использовать строку фиксированной длины. Например: 10 символов и добавление нуля в начале.

Для ваших вопросов.

  1. Это не работает для всех случаев с одним префиксом длина. У него есть ограничение, например, если мы используем int32 в качестве длины префикса, длина сообщения должна быть меньше Integer32.max, верно?

  2. Да, мы должны разделить или даже комбинировать (пожалуйста, обратитесь к моему объяснению в приведенной выше ссылке).

  3. У нас есть много способов справиться с ограничением длины, если это наша проблема (на самом деле, почти протоколы приложений имеют максимальный размер запроса) . Вы можете использовать еще один бит, чтобы указать, превышает ли сообщение максимальную длину для его разрешения, верно?

...