Количество извлечённых байт через recv()

Тема в разделе "WASM.NETWORKS", создана пользователем s3dworld, 4 сен 2011.

  1. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Всем доброй ночи!

    Сервер передаёт клиенту сообщения разной длины (от 8 до 15 байт). Так как клиент не знает размер сообщения, буфер я подготавливаю заранее большой:

    Код (Text):
    1. char* buffer=0;
    2.  
    3. buffer=new (std::nothrow) char[128];
    4.  
    5. memset(buffer,0,128);
    И вот я извлекаю данные:

    Код (Text):
    1. recvBytes=recv(entitySocket,buffer,128,0);
    В ответ я получаю разные значения (чаще всего 128 байт извлечено), но ни разу не было столько, сколько само сообщение занимало. Откуда берутся такие значения?

    Вообще я думал функция работает так. В качестве третьего параметра я ей указываю такое количество байт, которое я готов извлечь за один раз. А она в свою очередь извлекает либо эти 128 байт (если в буфер пришло столько данных), либо столько, сколько пришло. А тут оказывается как-то по другому. Может я что-то не так понял?

    А то пришлёт мне сервер:

    Код (Text):
    1. "Саша", "Маша", "Даша", "Катя", "Оля", ...
    А на клиенте будет:

    Код (Text):
    1. "Саша", 0x00, 0x00, 0x00, "Маша" , 0x00, ... , 0x00 , 0x00 , ...
     
  2. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
    Такое может быть у TCP-сокета в асинхронном режиме - будет отдавать столько, сколько есть на данный момент. В синхронном режиме TCP-сокет должен ждать до тех пор, пока в сокет не придет указанное количество байт, если только раньше не происходит разрыв TCP-соединения. В любом TCP-случае нет вообще понятия сообщения, есть просто поток байтов без всяких границ, нарезаться на приемнике может как угодно.

    Если речь о UDP - то датаграмма должна приходить один-к-одному (если помещается в буфер).
     
  3. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Dmitry_Milk
    В том то и дело что у меня не блокирующий сокет (использую модель WSAEventSelect()). То есть как не старайся, всегда функция recv() будет возвращать не именно столько, сколько пришло?
     
  4. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
    Да, но не больше, чем вы запросили. Остальное получите последующими вызовами recv. "Нарезку" сообщений надо осуществлять самому, либо через еще один буфер, дописывая в него очередную порцию recv, пытаясь отыскать там границу сообщения (в вашем примере нулевой байт). Либо организовать сообщения в формате с предшествующей длиной сообщения. Второй вариант безопаснее на предмет переполнения буфера.
     
  5. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
    Невнимательно прочитал ваш текст, не заметил "не"

    recv будет возвращать столько, сколько есть в буфере. Просто отправляющая сторона вовсе не обязана формировать порции именно так, как на ней подавались send
    Подавались
    send("Саша");
    send("Маша");
    send("Даша");
    а пакетики могут отправиться такие, например: [СашаМа][шаДаша]
     
  6. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Dmitry_Milk
    Это я понимаю, в TCP всё идёт потоком, а не пакетами. Но я боюсь что передавать буду так:

    Код (Text):
    1. send("Саша");
    2. send("Маша");
    А получу так:

    Код (Text):
    1. "Саша", 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, "Маша", 0x00, 0x00, 0x00, 0x00, 0x00
    А так мне не хотелось бы. Я пишу сетевой движок, и хочется сделать как можно более универсальным, а не задавать размер переданных данных.
     
  7. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    535
    Тогда придется завести FIFO-буфер, данные из recv копировать (или прямо получать) в него, и уже в этом буфере искать разграничители сообщений (разграничители, конечно, должны явно отправляться, скажем, завершающие нули СИ-строк или "\n"), отдавая приложению-пользователю из FIFO-буфера уже все сообщение целиком только тогда, когда в потоке встретился разграничитель.
     
  8. djmans

    djmans New Member

    Публикаций:
    0
    Регистрация:
    27 дек 2006
    Сообщения:
    312
    В чем проблема перед очередным сообщением указать его длину?

    send("\x04Саша");
    send("\x05Костя");

    И гляньте на всякий случай про опцию TCP_NODELAY.
     
  9. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    Мдя.. давненько не заходил на форум, давненько не кодил... Эх.. Но, что помню точно - если у вас ТСР соединение, то как отправили, так и получите, сколько отправили - столько и получите.
    djmans, шедеврально. ТСР над ТСР может еще придумаем?
    s3dworld, вам нужно получать пока recv не вернет 0 или -1 при errno != EWOULDBLOCK или как там в виндах...
     
  10. djmans

    djmans New Member

    Публикаций:
    0
    Регистрация:
    27 дек 2006
    Сообщения:
    312
    2Aspire зачем? Вы не когда про MTU и "Nagle's algorithm" не слышали? Что там типо фрагментация пакетов и т.д...

    >то как отправили, так и получите, сколько отправили - столько и получите.
    Он отправил сообщение на своем протоколе ПОВЕРХ TCP, ему нужно извлекать их по одному, recv в TCP возвращает не сообщениями, а столько сколько доступно во входящем буфере. Там может быть 2 сообщения, 4 сообщения, 4.5 сообщения...

    "как отправили, так и получите" - это про UDP.
     
  11. izl3sa

    izl3sa New Member

    Публикаций:
    0
    Регистрация:
    22 апр 2010
    Сообщения:
    164
    Адрес:
    Spb
    2Aspire
    про TCP над TCP вы не правы кстати, в stream protocols нормально задавать длину вариабельных данных, тк непонятно будет как разграничивать конец одного сообщения с началом другого.
     
  12. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    djmans, izl3sa, я, наверное, не просто так процитировал ТС. Повторюсь:

    "я боюсь что передавать буду так: send("Саша"); send("Маша");
    А получу так: "Саша", 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, "Маша", 0x00, 0x00, 0x00, 0x00, 0x00"


    Скажите, каким образом по ТСР я могу получить такие данные, если я отправил другие?
    Каким образом я могу получить 21 байт, когда я отправлял 10???
     
  13. onSide

    onSide New Member

    Публикаций:
    0
    Регистрация:
    18 июн 2008
    Сообщения:
    476
    Никаким, вы получите 10 байт как и отправили.
    У вас должен быть какой-то определитель конца сообщения, и про "TCP над TCP" вы зря.
    Если вы "получаете" больше чем отправили, значит ошибка в вашем коде наверняка.
     
  14. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    еще один %) onSide, Вы сколько сообщений прочитали? Два последних?
    Спасибо всем за помощь, без вас я бы не справился (плакаю). Теперь топикстартеру помогите дописать сетевой движок.
     
  15. onSide

    onSide New Member

    Публикаций:
    0
    Регистрация:
    18 июн 2008
    Сообщения:
    476
    Aspire перепутал вас с топикстартером)))
     
  16. Phyber

    Phyber New Member

    Публикаций:
    0
    Регистрация:
    27 мар 2010
    Сообщения:
    96
    Всё верно поняли, чтото не то в коде делаете значит.
     
  17. djmans

    djmans New Member

    Публикаций:
    0
    Регистрация:
    27 дек 2006
    Сообщения:
    312
    2Aspire
    >наверное, не просто так процитировал ТС. Повторюсь:

    хрень пишите какую-то, как минимум в мой адрес.
     
  18. Smile

    Smile New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2004
    Сообщения:
    129
    s3dworld

    Код (Text):
    1. char* buffer=0;
    2. buffer=new (std::nothrow) char[128];
    хм, размер небольшой зачем через new выделять, лучше на стеке )
    Код (Text):
    1. u_char buffer[128];
    2. memset(buffer,0,_countof(buffer));
    Если правльно настроен сокет, лишних данных в буфере быть недолжно.

    В коде присутствует entitySocket, что кабэ намекает на движок игры, тогда логичный вопрос почему не использовать готовые библиотеки enet, raknet, hawknl ?
    Там решены проблемы с передачей сообщений через потоковый протокол, и доставка с гарантией по udp. В некоторых есть уже упаковка данных в сетевой формат с компресией.
     
  19. s3dworld

    s3dworld Сергей

    Публикаций:
    0
    Регистрация:
    16 мар 2010
    Сообщения:
    387
    Адрес:
    Ртищево
    Smile
    Хм. Это чем же это намекает? Сетевой движок дописал. Правда есть ошибки по поводу синхронизации потоков. Пока лень переделывать.

    Так собственно если кто-то не понял о чём я говорил, то расскажу подробно. Когда передавались данные (не помню кто передавал клиент или сервер) в маленьком размере, например 8 байт "Hello!!!", то когда я извлекал данные recv(entitySocket,bufferRecv,128,0), то извлекалось у меня почему-то не 8 байт, а по 128, 116..разное количество. Хотя отправлял я именно один раз эти 8 байт. Вот и получалось что у меня при извлечении в начале было "Hello!!!", а потом шли 0x00. Проблема решилась тогда, когда после создания сокета я изменял вручную его буферы отправки и приёма. После этого recv() стал возвращать столько, сколько на данный момент сокет получил.
     
  20. Phyber

    Phyber New Member

    Публикаций:
    0
    Регистрация:
    27 мар 2010
    Сообщения:
    96
    s3dworld
    Как принимаете данные, вы код показали, а как отправляли?