Recv как прочесть всю страницу в си
Перейти к содержимому

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

Не кто не сталкивался с проблемой. recv читает первый раз хорошо все данные приходят как надо, на второй раз он читает в буфер 0, 0 читает столько сколько указано в 3 параметре и это длится бесконечно, то есть сокет не блокируется, при этом «Сервер» не чего не отправляет стоит на бреакпоинте.

94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Интернет работает, но Chrome и IE не открывают сайты. Firefox работает, но плохо
Добрый день! У меня два компьютера получают интернет от одного роутера. На одном компьютере всё.

Всего одна из страниц сайта плохо работает в IE и Мозилле (абра-кадабра).В Опере все работает.
Привет всем. Сделал небольшой сайтик на HTML и немножко СSS там присутствует. Это мой первый сайт.

Не работает или совсем плохо работает интернет
Здравствуйте! Что-то третий день уже на винде не работает интернет. Так иногда, если долго.

Перехват API winsock32.recv — под отладчиком работает 100%, без отладчика — 50%
Добрый день, уважаемые Киберчане. Прошу Вашей поддрежки. Описание проблемы. Вызов функции.

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() и многострочные ответы

Пишу софтину для почтовой рассылки. Софтина посредством WinSock должна подконнектиться к серверу, авторизоваться и рассылать письма.
Происходит все так:

connect(sock, SAddr, Sizeof(SAddr)) . msg:='EHLO'; ret:=send(sock,msg,Length(msg),0); . ret:=recv(sock,msg,1024,0);

Вот тут то я, собственно и застопорился. Подключаясь к серверу с помощью telnet и отправляя ему «EHLO» я получаю в ответ 5 строк. Функция recv отдает мне в буфер msg (msg: array[0..1023] of AnsiChar) лишь первую строку, оканчивающуюся на CrLf. Если я пытаюсь вызвать recv еще раз, программа наглухо виснет — оно и понятно, сервер ведь больше ничего не отвечает.
Внимание вопрос! Куда делись оставшиеся 4 строки ответа и как их получить в буфер msg?

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

И да, не посылайте меня, пожалуйста, в сторону инди и TCPClient. Я хочу сделать все именно на Winsock и никак иначе. Версия библиотеки — 2.0.

Буду очень благодарен, если посоветуете ХОРОШЕЕ руководство по Winsock, желательно для Delphi, желательно на русском языке.

UPD: Я дико извиняюсь — изначально неверно сформулировал проблему! Правильная формулировака ниже!

Последний раз редактировалось Olgir, 29.08.2011 в 16:48 .

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

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