Как получить данные файла через сокет в Go? - PullRequest
1 голос
/ 16 октября 2011

У меня есть две небольшие программы, прекрасно взаимодействующие через сокет, где принимающая сторона находится в Go. Все работает замечательно, когда мои сообщения достаточно малы, чтобы поместиться в 1024-байтовый буфер, и их можно получить за одно чтение из соединения, но теперь я хочу перенести данные с изображения размером более 100 КБ или более. Я предполагаю, что правильное решение не состоит в том, чтобы увеличивать буфер, пока любое изображение не может поместиться в.

Псевдо-гоу:

var buf = make([]byte,1024)
conn, err := net.Dial("tcp", ":1234")

for {
    r, err := conn.Read(buf[0:])
    go readHandler(string(buf[0:r]),conn)
}

Как улучшить процедуру чтения сокетов, чтобы принимать как простые сообщения размером в несколько байтов, так и большие данные? Бонусные баллы, если вы можете превратить все данные изображения в io.Reader для использования в image.Decode.

Ответы [ 2 ]

3 голосов
/ 17 октября 2011

У меня нет прямого опыта работы с TCP в Go, но мне кажется, что вы стали жертвой довольно типичного недопонимания того, что гарантирует TCP.

Дело в том, что, в отличие, скажем, от UDP и SCTP , TCP не имеет концепции границ сообщений, потому что он ориентирован на поток. Это означает, что TCP транспортирует непрозрачные потоки байтов, и у вас очень мало контроля над «разбиением» этого потока по отношению к принимающей стороне.

Я подозреваю, что «отправка сообщения 100k +» - это библиотека времени выполнения / сети на стороне отправителя, которая обычно «обманывает» вас, загружая ваше «сообщение» во внутренние буферы и затем направляя его в любой стек TCP стека ОС. позволяет это сделать (на вездесущем оборудовании / программном обеспечении это обычно около 8 КБ). Размер кусков, которые получатель получает, этот поток полностью не определен; единственное, что определено, это порядок байтов в потоке, который сохраняется.

Следовательно, может оказаться, что вам придется пересмотреть свой подход к получению данных. Точный подход варьируется в зависимости от характера передаваемых данных:

  • Самый простой способ (если у вас есть контроль над протоколом уровня приложения) - передать длину следующей «полезной нагрузки сообщения» в специальном поле длины фиксированного формата. Затем уничтожение всего сообщения является двухэтапным процессом: 1) получить столько байтов, чтобы получить поле длины, прочитать его, проверить значение для здравомыслия, затем 2) прочитать много следующих байтов и покончить с этим.
  • Если у вас нет контроля над протоколом уровня приложения, анализ сообщений становится более сложным и обычно требует какого-то сложного конечного автомата.

Для получения дополнительной информации посмотрите это и это .

2 голосов
/ 18 октября 2011

Вы можете использовать io.ReadFull, чтобы прочитать []byte определенной длины.Это предполагает, что вы заранее знаете, сколько байтов нужно прочитать.

Что касается image.Decode, то должна быть возможность передать conn непосредственно в функцию image.Decode.Это предполагает, что вы не выполняете никаких чтений из соединения, пока изображение не будет декодировано.

Ваш код

for {
    r, err := conn.Read(buf[0:])
    go readHandler(string(buf[0:r]),conn)
}

, по-видимому, предполагает, что запускаемая вами программа читает с conn Это не кажется хорошей идеей, потому что в конечном итоге вы получите несколько одновременных чтений из соединения (без контроля над порядком, в котором будут выполняться чтения): один в цикле forеще один в readHandler.

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