Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Сети > Прочитать все данные из сокета


Автор: azesmcar 19.3.2009, 08:40
Нужно прочитать все что есть в буффере сокета, т.е. все что пришло на данный момент. Можно конечно сделать recv на 10000 байт и посмотреть что пришло, но как-то некрасиво...к тому же буффер ему тоже надо будет создавать большой..собственно как это сделать?

Автор: InvalidProperty 19.3.2009, 09:27
Не скажу, что мой пример единственно правильный, но я делал следующим образом:

Код

#define CTERM 1

/* 
... 
*/

string buffer;
char    recv_line[MAXLINE + 1];
ssize_t n;

/* 
... 
*/

buffer.clear();
while( (n = _reads(connfd, recv_line, MAXLINE)) > 0)
{
 try
  { buffer += recv_line; }
 catch(exception)
 {
  printf("can not save data into buffer\n");
  return -1;
 }

 if(strchr(recv_line, CTERM) != NULL)                 // wait for term char
  break;
}
if(n == (ssize_t)-1)
 { printf("can not read from socket - %m"); return -1; }


Код

 ssize_t _reads(int fd, char* buff, size_t count)
 {
  ssize_t        n;

  if( (n = _read(fd, buff, count)) != (ssize_t)-1)
   buff[n] = 0;

  return n;
 }

 ssize_t _read(int fd, void* buff, size_t count)
 {
  ssize_t    n;
  off_t        offset;
  bool        is_pipe = false;

  if( (offset = lseek(fd, 0, SEEK_CUR)) == (off_t)-1)
  {
   if(errno != ESPIPE)
    return (ssize_t)-1;
   else
    is_pipe = true;
  }

 read_again:
  if( (n = read(fd, buff, count)) == (ssize_t)-1)
  {
   /* interrupted system call => return file position to that place, where we have stopped */
   if(errno == EINTR)
   {
    if(!is_pipe)
     if(lseek(fd, offset, SEEK_SET) == (off_t)-1)
      return (ssize_t)-1;
    goto read_again;
   }
  }

  return n;
 }


в этом примере протокол построен таким образом, что клиентская сторона должна отправлять знак CTERM - признак окончания сообщения.

Буду очень рад, если кто-нибудь укажет мне на недочеты моего метода или, вообще, предложит что-то свое

Автор: GrayCardinal 19.3.2009, 16:19
azesmcar, 
Лезем в opsuperlib, или просто смотрим функтяру :
Цитата

__export int DIONREAD (int fd)
{
    int ret = -1;
    if (ioctl (fd, FIONREAD, &ret) != 0)
  return -1;
    return ret;
}

(вертает количество байт, которые ща есть для чтения)

в opsuperlib еще куча всего, рекомендую smile



Автор: azesmcar 19.3.2009, 16:26
Цитата

Лезем в opsuperlib, или просто смотрим функтяру :


я нашел ioctlsocket, но он только под виндоуз..ioctl его аналог под линукс или как? могу и макрос вставить в принципе для кроссплатформенности (ибо мне нужен кроссплатформенный код).
InvalidProperty, посмотрим какие еще будут варианты smile

Автор: InvalidProperty 19.3.2009, 16:46
azesmcar, да я весь уши  smile 

Автор: vinick 20.3.2009, 12:33
ЕМНИП размер приемного буфера у сокета можно получить через getsockopt(...,SO_RECVBUF,...). Ну а потом на неблокируемом сокете читать число байт равное размеру буфера.  В этом случае должен вычитываться весь буфер. Можно еще добавить select или иной механизм мультиплексирования чтобы в холостую не читать.

Только не понятно зачем такое требование ? Ведь как-только в приемном буфере сокета появится место - сразу начнут поступать новые данные... это учебная задача?


Автор: azesmcar 20.3.2009, 12:41
Цитата

Только не понятно зачем такое требование?


как зачем? пришли данные, которые мне нужно прочитать...сколько мне читать если я не знаю сколько их там?

Цитата

Ведь как-только в приемном буфере сокета появится место - сразу начнут поступать новые данные

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

Цитата

это учебная задача?

похоже? smile

Автор: vinick 20.3.2009, 12:41
InvalidProperty,  EINTR возвращается если системный вызов был прерван до того как прочитан хотя бы один байт. Так что lseek у тебя не имеет смысла.

Автор: InvalidProperty 20.3.2009, 13:39
Цитата

EINTR
    Системный вызов был прерван сигналом до того, как был прочитан хотя бы один байт. 

да, ступил я чего-то. Сенкс, vinick

Добавлено через 10 минут и 18 секунд
Цитата(vinick @  20.3.2009,  12:33 Найти цитируемый пост)
Ведь как-только в приемном буфере сокета появится место - сразу начнут поступать новые данные...

Откуда? А если их никто больше не передает и на том конце клиент ждет, когда придет ответ?

Цитата(vinick @  20.3.2009,  12:33 Найти цитируемый пост)
ЕМНИП размер приемного буфера у сокета можно получить через getsockopt(...,SO_RECVBUF,...). Ну а потом на неблокируемом сокете читать число байт равное размеру буфера. В этом случае должен вычитываться весь буфер.

А как узнать, что в данный момент в буфере находится целое сообщение, а не его кусок? Я понимаю, что ТС это не учитывает по роду задачи, но мне интересно, как справиться с получением целого сообщения, если его длина неизвестна

Автор: Олег2005 20.3.2009, 15:42
Цитата(InvalidProperty @  20.3.2009,  12:39 Найти цитируемый пост)
А как узнать, что в данный момент в буфере находится целое сообщение, а не его кусок? Я понимаю, что ТС это не учитывает по роду задачи, но мне интересно, как справиться с получением целого сообщения, если его длина неизвестна

TCP-потоковый протокол - и он совершенно не знает, что он передает - это труба, в которую что-то выливают на другом конце - от 1-го байта до более 4 гигов
Разбираться должен с содержимым протокол верхнего уровня (как это делает например HTTP- парсит полученные данные и выуживает инфу.) - т.е. ваше приложение.
А потому есть только конец TCP-сессии - recv() возвращает 0.

Автор: InvalidProperty 20.3.2009, 15:50
Олег2005, итого, имеем, что кроме того, что я уже описал http://forum.vingrad.ru/index.php?showtopic=251879&view=findpost&p=1816608, ничего, в принципе, кардинально нового придумать нельзя. 

Автор: azesmcar 20.3.2009, 16:05
Олег2005, в принципе уже решил..добавляю в буффер пока не наберется нужный размер сообщения, как наберется отдаю обработчику..просто хотелось немного оптимизировать..если не дошло все сообщение, подождать что ли smile

Автор: Олег2005 20.3.2009, 16:10
InvalidProperty, 
Примерно так smile 
azesmcar, 
К сожалению, оптимизировать работу модулей стека - это не наша задача!
Они написаны и работают так, как написано в RFC.
Честно говоря, они справляются и пока мы не жалуемся - чему свидетельство и эти диалоги.

Автор: vinick 20.3.2009, 16:13
Цитата(azesmcar @  20.3.2009,  12:41 Найти цитируемый пост)
и что? у меня протокол с нефиксированным размером сообщений, 

Это не оправдание smile


Цитата(azesmcar @  20.3.2009,  12:41 Найти цитируемый пост)
мне нужно прочитать все что пришло..

Тебе нужно прочитать сообщение или содержимое внутреннего буфера сокета ? Это две совершенно разные и малосвязанные между собой вещи. Читай небольшими кусками, как сазал Олег2005, тебе все равно придется как-то буферизовать поток и дробить его на сообщения на прикладном уровне.


Цитата(InvalidProperty @  20.3.2009,  15:50 Найти цитируемый пост)
имеем, что кроме того, что я уже описал выше, ничего, в принципе, кардинально нового придумать нельзя. 

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

Автор: InvalidProperty 20.3.2009, 16:33
Цитата(vinick @  20.3.2009,  16:13 Найти цитируемый пост)
... самих способов разграничения можно придумать воз и маленькую тележку, и они могут кардинально различаться.

комрад, дык это ессесно. smile

Автор: azesmcar 20.3.2009, 18:28
Цитата

К сожалению, оптимизировать работу модулей стека - это не наша задача!
Они написаны и работают так, как написано в RFC.
Честно говоря, они справляются и пока мы не жалуемся - чему свидетельство и эти диалоги.


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

Цитата

Это не оправдание smile

Я никогда и ни перед кем не оправдываюсь..
Цитата

Тебе нужно прочитать сообщение или содержимое внутреннего буфера сокета ?

а в содержимом внутреннего буфера сокет что если не сообщение?
Цитата

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

я не утверждал обратного, но "как-то" может быть быстро и медленно. Мне желательно сделать быстро..во всяком случае насколько это возможно.

Автор: vinick 20.3.2009, 18:46
Цитата(azesmcar @  20.3.2009,  18:28 Найти цитируемый пост)
а в содержимом внутреннего буфера сокет что если не сообщение?

Там может быть 1 сообщение, 10 сообщений,  половина сообщения, конец первого сообщения и начало второго. в общем там может быть что угодно.

Цитата(azesmcar @  20.3.2009,  18:28 Найти цитируемый пост)
Мне желательно сделать быстро..во всяком случае насколько это возможно.

Если у тебя не InfiniBand, то скорость передачи по сети на порядки будет отставать от самого медленного метода извлечения сообщения из сокета. Так что особый фанатизм в такой оптимизации  ИМХО излишен.

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

Автор: azesmcar 20.3.2009, 19:08
Цитата

Там может быть 1 сообщение, 10 сообщений,  половина сообщения, конец первого сообщения и начало второго. в общем там может быть что угодно.


мне все равно, они парсятся в очередь конкретных сообщений..

Цитата

Если у тебя не InfiniBand, то скорость передачи по сети на порядки будет отставать от самого медленного метода извлечения сообщения из сокета. Так что особый фанатизм в такой оптимизации  ИМХО излишен.

задержка на извлечении одного сообщения - задержит обработку, а следовательно и ответ на сообщение другого пользователя. Так что не совсем излишен. Правда речь не о фанатизме, если не найду другого способа, сделаю как знаю.
Цитата

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


У меня так и сделано. Но если в заголовке написано что сообщение длиной в 30Кб, это не значит что я прочитаю их все. Я просто не хочу читать маленькими кусками. Уже написал класс для буфферизации сообщений..читаю сколько нужно, если в буффере недостаточно (recv вернул меньше чем нужно) жду следующего события. Как наберется - конвертирую в сообщение, передаю обработчику.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)