2016-09-08 2 views
0

Я реализую приложение TCP/IP-сервера, которое использует epoll в режиме с краем и выполняет неблокирующие операции сокета. Клиенты используют простые операции блокировки без epoll.C socket atom non-blocking read

Я не вижу, как «атомные чтения» могут быть реализованы на стороне сервера. Чтобы объяснить, что я имею в виду «атомное считывание», см. Этот пример с простыми операциями блокировки:

  • И клиент, и сервер используют буферы 64 КБ. (На уровне приложений. Они не меняют буферы сокета уровня ядра.)
  • Клиент записывает данные 12K с одной записью.
  • Сервер читает его. В этом случае он всегда читает все 12K, когда буферы одинаковы. Поэтому он не может читать только половину. Это то, что я называю «атомарным».

Но в случае Epoll + неблокирующие операции это может произойти:

  • И клиент и сервер используют 64K буферов. (На уровне приложений. Они не меняют буферы сокета уровня ядра.)
  • Клиент записывает данные 12K с одной записью.
  • 6K поступает на сервер
  • Epoll указывает приложению, что данные прибывшего в сокет
  • приложение считывает 6К в буфер, используя нелипкая операцию.
  • При повторном чтении он возвращает EAGAIN/EWOULDBLOCK.

В этом случае чтение не является «атомарным». Не гарантируется, что, когда данные были записаны с помощью одной операции записи, чтение будет возвращать целое в одном фрагменте.

Можно ли узнать, когда данные являются частичными? Я знаю, что одно решение заключается в том, чтобы всегда добавлять размер данных в начало, иначе можно было бы всегда закрывать и повторно открывать соединение, но я не хочу этого делать: потому что я думаю, что ядро ​​должно знать, что не полный «пакет» (как называется эта единица, называемая BTW?), поскольку она обеспечивает атомарность для операций блокировки.

Большое спасибо!

+3

Обычно нужно писать все операции ввода/вывода в * петли * и поддерживать свои собственные буферы. –

+0

Если ваш вызов 'read' в сокете не возвратил' 0', вы можете ожидать, что там будет больше данных, которые вы можете захватить в цикле событий. – jacob

+0

В двоичной передаче обычно используется разделитель, такой как начало передачи и конец передачи. В текстовых передачах проверка синтаксиса является вашим другом. Если вы передаете строки json, вы можете проанализировать строку json, чтобы определить, завершено ли это или нет. – alvits

ответ

1

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

Таким образом, дело в том, чтобы приложение «атомарное» читало его пожелания. Например:

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

[2 байта длина сбщ] [байт данных интересов]

На основе этой информации приложение делает чтение должно принимать меры.Он должен опросить сокет, пока он не получит все байты, как указано байтами длины msg. Только тогда обработайте данные.

Если вам нужно «атомарное» чтение, а не частичное чтение, вы можете использовать флаг MSG_PEEK в recv. Это не должно удалять данные из буфера сокета. Приложение заглядывает в сокет, посмотрите, требуется ли требуемое количество данных в буфере сокета на основе возвращаемого значения.

ret = recv(sd, buf, MAX_CALL_DATA_SIZE, MSG_PEEK);

+0

Я бы принял его, но у меня нет кармы для него;) Хотя вы тоже можете добавить это: «Ваша презумпция в отношении блокирующего случая была неправильной: например, сервер может читать только 4K с тем, что блокирует чтение, а затем возвращает». –

+0

NP! Удачи для большей репутации. И не забудьте принять ;-) – Prabhu

+0

Если ответ был полезен, его можно было выдержать/принять? – Prabhu