Перейти к содержимому

Recv как прочесть всю страницу в си

  • автор:

Как recv() понимает, что все данные получены в случаи TCP?

Насколько я понимаю, данные через TCP передаются как сплошной поток, до тех пор пока соединение не будет разорвано. Если посмотреть на структуру сегмента TCP,там даже нет информации о длинне данных (в отличии от UDP, к примеру). Таким образом, если мы читаем что-то с TCP сокета в буфер, чтение будет происходить до тех пор, пока соединение не закроется, либо буффер не переполниться. Однако если посмотреть на реальный код, это не так — recv() на сервере читает ровно столько байт, сколько отправлено с клиента с помощью send() Каким образом recv() понимает, что все данные получены, и управление нужно вернуть в вызывающий код? Полный и минимальный пример на голых сокетах: Сервер:

#include #include #include #include #include #include const int BUFFER_SIZE = 1024; const int PORT = 12345; int main() < //create server socket int socketFd = ::socket(AF_INET, SOCK_STREAM, 0); if (socketFd < 0) < return -1; >int opt_val = 1; setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof opt_val); //bind to address sockaddr_in socketAddress; socketAddress.sin_family = AF_INET; socketAddress.sin_port = htons(PORT); socketAddress.sin_addr.s_addr = htons(INADDR_ANY); int rc = ::bind(socketFd, reinterpret_cast(&socketAddress), sizeof(socketAddress)); if (rc < 0) < return -2; >//listen rc = ::listen(socketFd, SOMAXCONN); if (rc < 0) < return -3; >//accept new connection sockaddr_in socketAdress; unsigned int sizeOfSocketAdress = sizeof(socketAdress); int clientSocket = ::accept(socketFd, (struct sockaddr *)&socketAdress, &sizeOfSocketAdress); if (clientSocket < 0) < return -4; >//receive char buffer[BUFFER_SIZE]; int receivedBytes = ::recv(clientSocket, buffer, BUFFER_SIZE, MSG_NOSIGNAL); std::cout 
#include #include #include #include #include #include #include #include #include int main() < struct sockaddr_in sa; int res; int socketFd; socketFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (socketFd == -1) < perror("cannot create socket"); exit(EXIT_FAILURE); >memset(&sa, 0, sizeof sa); sa.sin_family = AF_INET; sa.sin_port = htons(12345); res = inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr); if (connect(socketFd, (struct sockaddr *)&sa, sizeof sa) == -1) < perror("connect failed"); close(socketFd); exit(EXIT_FAILURE); >auto buf = "hello"; auto len = 5; int sentBytes = ::send(socketFd, buf, len, 0); std::cout 

Как узнать о наличие данных в сокете?

Как узнать о том что на сокет поступила какая то информация.Тоесть к примеру чтобы определять момент когда внутри бесконечного цикла пора использовать recv.

5 ответов

24 июня 2007 года
809 / / 27.07.2006

recv ждет данные, т.е. при вызове он ожидает до тех пор, пока не поступят данные.
Вызываешь recv и все. когда данные придут, выполнение кода продолжится само.

26 июня 2007 года
589 / / 01.06.2006

recv ждет данные, т.е. при вызове он ожидает до тех пор, пока не поступят данные.
Вызываешь recv и все. когда данные придут, выполнение кода продолжится само.

Если возникает вопрос, как использовать время ожидания, пока recv ничего не делает (данных для чтения нет), используй средства синхронизации. Под виндами — это WSAAsyncSelect и WSAEventSelect.

13 июля 2007 года
209 / / 21.06.2003

recv ждет данные, т.е. при вызове он ожидает до тех пор, пока не поступят данные.
Вызываешь recv и все. когда данные придут, выполнение кода продолжится само.

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

Если возникает вопрос, как использовать время ожидания, пока recv ничего не делает (данных для чтения нет), используй средства синхронизации. Под виндами — это WSAAsyncSelect и WSAEventSelect.

конечно хорошо но идет завязка на сторонние объекты которые хороши только при небольших размерах и нет необходимости в значительной масштабируемости (хотя можно поспорить, но делать не буду).

Лично я часто использую механизм класического select’а от модели Беркли:

// очищаем набор сокетов
FD_ZERO(&Read);

// добавляем сокет в набор
FD_SET(Socket,&Read);

// заполняем структуру времени ожидания
Wait.tv_sec = 0;
Wait.tv_usec = 100;

// ожидаем подтверждения
events = select(0,&Read,NULL,NULL,&Wait);

// если нет ошибки
if( (events != SOCKET_ERROR) && events ) return TRUE;

— если читать нечего, возвращает через 100 мс FALSE, если есть то TRUЕ и немедленно по наступлении этого случая. При этом время ожидания «переводится» на другие процессы. Кстати данный метод использую при анализе перед отработкой Accept для другой работы если она нужна, дабы не городить сторонние потоки.

Recv как прочесть всю страницу в си

1) На Раздел распространяются все Правила Форума.
2) Перед тем, как создать новый топик, убедитесь, что Вы читали Правила создания тем в Разделе.
3) Вопросы, не связанные с программированием (настройки MS Visual Studio, книги, библиотеки и т.д.),
обсуждаются в разделе C/C++: Прочее
4) Вопросы разработки .NET (Windows Form, C++/CLI и т.п.) приложений на Visual C++/C# обсуждаются в разделе .NET.
5) Нарушение Правил может повлечь наказание со стороны модераторов.

Модераторы: ElcnU
‘> select и recv

  • Подписаться на тему
  • Сообщить другу
  • Скачать/распечатать тему

Сообщ. #1 , 23.08.07, 17:18
Рейтинг (т): 1

нужно вычитать страницу. Вроде получается, но есть одна странность

std::size_t recvBuffer (void* buffer, std::size_t size)
fd_set fds = <0>;
std::size_t sizeBuffer = size;
timeval timeout = <0, 0>;
char *pBuffer = (char *)buffer;
FD_SET (m_socket, &fds);
while (size > 0)
int result = select (0, &fds, NULL, NULL, &timeout);
if (result == SOCKET_ERROR)
std::exception («Socket closed»);
if (!result) break;
int bytes = ::recv (m_socket, pBuffer, (int)size, 0);
if ((bytes == 0) || (bytes == SOCKET_ERROR))
std::exception («Socket closed»);
size -= bytes;
pBuffer += bytes;
return (sizeBuffer — size);

тоесть перед каждой попыткой чтения вызывается select с timeout = 0, который просто проверяет есть ли сокет, который может работать с таким timeout.

Пока страница не вычиталась select возвращает «1», а вот когда вычиталась «0».
Ну вроде все логично, т.к. сервер после того, как отдал страницу — закрывает сокет.
Но вод если не выходить после проверки значения select, то recv зависает.

Странно, но при разрыве соединения он должен сразу вернуть «0», а он виснет.

Сообщ. #2 , 23.08.07, 20:38
Рейтинг (т): 215
Цитата cthutq @ 23.08.07, 17:18
Но вод если не выходить после проверки значения select, то recv зависает.

Фраза звучит вообще непонятно
Те не менее
Селект — функция одноразовая, и использование ее в цикле требует иного подхода:

После своего завершения функция select() сбросит биты, указывающие на любой сокет, не готовый к определенной операции ввода-вывода

Те все FD надо внести в цикл, чтобы сохранить идеологию использования селекта
Сообщ. #3 , 24.08.07, 03:57
Рейтинг (т): 1
Цитата Oleg2004 @ 23.08.07, 20:38
Фраза звучит вообще непонятно

ну если данные вычитались — сокет разрывает соединение и recv должен вернуть 0 (так написано в MSDN)
А он виснет т.к. данных нет. Поэтому приходится проверять select на 0

Цитата Oleg2004 @ 23.08.07, 20:38

Те не менее
Селект — функция одноразовая, и использование ее в цикле требует иного подхода:

Цитата
После своего завершения функция select() сбросит биты, указывающие на любой сокет, не готовый к определенной операции ввода-вывода

Те все FD надо внести в цикл, чтобы сохранить идеологию использования селекта

Тоесть в цикое эио не правильное использование ? Я просто где-то читал, что recv изменит флаги select, поэтому ее нужно
вызывать каждый раз перед recv.

Млм имеется ввиду, что нужно внести

FD_SET (m_socket, &fds);

в цикл ? Это тогда будет правильно ?

Сообщ. #4 , 24.08.07, 08:06
Рейтинг (т): 215
Цитата cthutq @ 24.08.07, 03:57
ну если данные вычитались — сокет разрывает соединение и recv должен вернуть 0 (так написано в MSDN)

Немного другой порядок действий
recv() — запускаем и получаем данные — если не получаем, то действительно будем висеть хоть до конца света — ждать, когда данные прийдут
Как только на той строне все передадут, она же (та сторона) посылает сегмент FIN (типа я все отправил)
и наш recv() в этом случае возвращает 0
Вот получив этот 0, мы и закрываем сокет на нашей стороне (но только если больше мы ничего ни принимать, ни передавать по этому сокету не будем) сами
Значит что делают? ведь recv() не имеет своего таймаута
Вот здесь, правильно — ставят селект — но с таймаутом -(у вас он почему-то 0 — те селект завершается мгновенно)
те recv() запускается, если в буфер сокета пришли данные
Если данных нет например 10 минут — значит надо рвать соединение — что-то на той стороне наверно сдохло или сеть полетела или.

Цитата cthutq @ 24.08.07, 03:57
в цикл ? Это тогда будет правильно ?

Для правильной работы селекта в цикле — да
В вашем случае же надо еще принять во внимание то что я написал выше

Сообщ. #5 , 25.08.07, 04:50
Рейтинг (т): 1

Oleg2004
посмотрел снифером — так все и происходит

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

void recvBuffer (void* buffer, std::size_t size)
char *pBuffer = (char *)buffer;
while (size > 0)
int bytes = ::recv (m_socket, pBuffer, (int)size, 0);
if ((bytes == 0) || (bytes == SOCKET_ERROR))
std::exception («Socket closed»);
size -= bytes;
pBuffer += bytes;

тоесть в этом случае не возвращается количество прочитанных байт. Они ли прочитаются все — либо что-то произощло и бросается исключение.

А вот если это Http-клиент, то размер считываемых данных неизвестен. Тогда я перевожу сокет в неблокирующий режим и
при приходе FIN (recv = 0) просто выхожу, а не бросаю исключение

std::size_t recvBuffer (void* buffer, std::size_t size)
fd_set fds = <0>;
std::size_t sizeBuffer = size;
timeval timeout = <0, 0>;
char *pBuffer = (char *)buffer;
while (size > 0)
FD_SET (m_socket, &fds);
int result = select (0, &fds, NULL, NULL, &timeout);
if (result == SOCKET_ERROR)
std::exception («Socket closed»);
if (!result) break;
int bytes = ::recv (m_socket, pBuffer, (int)size, 0);
if (bytes == SOCKET_ERROR)
std::exception («Socket closed»);
if (bytes == 0) break;
size -= bytes;
pBuffer += bytes;
return (sizeBuffer — size);

Тоесть помимо проверки на bytes я делаю проверку на result == 0. Это нужно делать ?
И правильно ли это вообще организовано ?

Цитата Oleg2004 @ 24.08.07, 08:06
у вас он почему-то 0 — те селект завершается мгновенно

Ну я так и не нашел нужный таймаут для HTTP сервера. Скольно нужно ждать ?
А если он = 0, то тут просто проверка на закрытие сокета.

Правда иногда сервер меня посылает нафиг (закрывает сокет) т.к. наверно он не успевает передать данные. а мой таймаут = 0. Чему он должен быть равен ?

Recv как прочесть всю страницу в си

Здравствуйте. Проверяю на входящие данные путем
int n = recv(sock1,a,1000,0);

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

n = recv(. );
if(n == 0)

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

Форумчанин
Регистрация: 03.01.2010
Сообщений: 229
перед посылкой данных отправить приложению информацию о том, какой размер сейчас следует принять
СтарожилДжуниор
Регистрация: 19.07.2009
Сообщений: 3,336

опять начинаешь мозги выносить?

I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance

Форумчанин
Регистрация: 05.06.2007
Сообщений: 529

Я про то, что так и задумано, что бы recv(); ставил программу на паузу?

открывается сокет, ожидается входящее соединение, сразу же следует recv(); и получаем входящие данные . все ок, но сокет не закрываем, т.к нужно принять следующие данные, которые придут чуть позже, даже хоть через минуту. вот тут и вопрос, как мне собственно узнать, что пришли эти новые данные, не ставя программу на паузу ожидания, что и получается с recv();?

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

Регистрация: 28.01.2009
Сообщений: 20,999

Я про то, что так и задумано, что бы recv(); ставил программу на паузу?

можете использовать Overlapped для ReadFile(второй винсок позволяет)

Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.

Пепел Феникса
Посмотреть профиль
Найти ещё сообщения от Пепел Феникса

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *