2013-07-15 5 views
1

Я реализую один сервер с новым разработанным протоколом. В соответствии с протоколом клиент отправляет заголовок, за которым следуют данные. Заголовок содержит метаинформацию, включая размер данных.recv() для большого количества данных не работает

Мы предоставляем образец клиента, но клиентская программа также может быть написана третьим лицом. Таким образом, мы не можем полностью полагаться на размер данных поле, указанное в заголовке.

Теперь у меня возникла одна проблема с системным вызовом recv().

 #define SOCKET_CHUNK_SIZE 4096 
     void * value; 

1 value = (void *) malloc(hdr.size); 
2 total_bytes_read = 0; 
3 while(total_bytes_read < hdr.size) { 
4  n = recv(newsockfd, value + total_bytes_read, SOCKET_CHUNK_SIZE, 0); 
5 
6  //fprintf(stderr, " %ld + %d = %ld\n", total_bytes_read, n, total_bytes_read + n); 
7 
8  total_bytes_read += n; 
9 
10  if(n == 0 || n < SOCKET_CHUNK_SIZE) 
11   break; 
12  if(n < 0) 
13   send_error_response(newsockfd); 
14 } 
15 
16 fprintf(stderr, "%ld", total_bytes_read); 

Это прекрасно работает для небольшого объема данных (например, 9420 байт), но не подходит для большей суммы.

Наблюдение:

Пусть клиент посылает некоторое большое количество данных, как 604697 байт (hdr.size):

  1. recv() может читать только 65280 байт. т.е. fprintf на линии № 16 печатает 65280. (я проверил SSIZE_MAX на моей машине, и это 2147483647, так что это гораздо больше, чем SOCKET_CHUNK_SIZE)

  2. Я попытался с помощью MSG_DONTWAIT флаг recv() вызова, но результат тот же.

  3. Я попытался использовать read() системный вызов вместо recv(), результат такой же.

  4. Когда я раскомментирую строку # 6, она отлично работает !! (Но эта строка (и строка № 16) предназначена только для цели отладки. Я не могу сохранить ее в окончательной версии).

  5. Если я использую флаг MSG_WAITALL в recv(), он работает, но блокирует при чтении последнего фрагмента как последний кусок размер меньше, чем SOCKET_CHUNK_SIZE (604697 = 147 * 4096 + 2585). Таким образом, я не могу использовать этот флаг, если только не зависит от размера, предоставленного в заголовке от клиента и изменяющегося в recv().

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

Любой, у кого есть идеи/решения, приветствуется. Как я уже упоминал, у нас есть решение - полагаться на заголовок клиента, но я предпочту его, только когда не найду других способов.

Ravi

+0

'n Rohan

+0

Вы не должны увеличивать 'total_bytes_read' до тех пор, пока не узнаете, что' n> 0'. – EJP

+0

@EJP. Если 'n <0', он отправит ошибку и не примет данные – Ravi

ответ

2

Почти все наблюдения совершенно объяснима:

  1. Я хотел бы, чтобы пропустить один, как я не уверен, если я его правильно, и как я рассматриваю его аса вторичная ошибка.
  2. recv не гарантирует заполнение всего буфера (если вы не установите MSG_WAITALL). Он возвращается после получения некоторых байтов. Следовательно, вторая часть вашего условия в строке 10 мешает вам получать дополнительные данные, если вы не устанавливаете MSG_WAITALL. Установка MSG_WAITALL делает recv возвращаться только после заполнения всего буфера (в вашем случае SOCKET_CHUNK_SIZE).Поскольку размер вашей полезной нагрузки не всегда кратен SOCKET_CHUNK_SIZE, ваш последний вызов recv будет зависать, пока соединение не умрет.
  3. Это связано с состоянием в строке 10 i, упомянутой выше.
  4. Палка с RECV, это Corret способ сделать это
  5. Я думаю раскомментировав Line 6 изменяет выбор времени выполнения таким образом, что случайно больше данных, чем SOCKET_CHUNK_SIZE поступивший на сокете.

Так что лучше всего с моей точки зрения было бы использовать recv без флага MSG_WAITALL и принимать принимающие куски меньше, чем SOCKET_CHUNK_SIZE.

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