::recv() и std::string
От: _chill Россия  
Дата: 26.11.04 17:36
Оценка:
Вот хочу принять данные с помошью блокирующего сокета и функции recv и положить их в std::string

Это выглядит примерно так:
std::string mySocket::recive()
{
   int i_ret = 0;
   char *buf[100];
   std::string result;

   while(true)
   {
      i_ret = ::recv(this->sock, buf, sizeof(buf),0);
      if(i_ret == 0)
      {
          throw(SocketExeption("Connection has been gracefully closed"));
      }
      if(i_ret == SOCKET_SRROR)
      {
          throw(SocketExeption("recv error"));
      }
      result += buf;
   }
   return result;
}

Я прекрасно понимаю почему на этом методе программа "подвисает", сокет ведь не переведен в неблокирующий режим и я не собираюсь его переводить в этот режим.
Как-нибудь можно узнать есть ли в системном буфере еще какие-нибудь данные или прием данных уже следует окончить?
Просто хочу чтобы все данные с сокета считались и метод вернул управление, но как это сделать не знаю...
Подскажите, пожалуйста...

Заранее спасибо, с уважением Chill
Re: ::recv() и std::string
От: zelyony  
Дата: 26.11.04 17:51
Оценка: 53 (2)
сколько байт можно прочитать не блокируясь помогает узнать
u_long availBytes = 0;
int wsaRes = ioctlsocket( hSocket, FIONREAD, &availBytes);
// обработка результата
// если не было ошибки, в availBytes - кол-во байт лежащих в буфере и которые можно прочитать не блокируясь
// в линухе вроде просто ioctcl(..)
// для датаграммных сокетов возвращается также кол-во доступных, но считывать придётся по-"датаграммно". система сама будет возвращать данные таким образом, к тому что, за один раз пару датаграмм не считать


> Я прекрасно понимаю почему на этом методе программа "подвисает", сокет ведь не переведен в неблокирующий режим и я не собираюсь его переводить в этот режим.

> Как-нибудь можно узнать есть ли в системном буфере еще какие-нибудь данные или прием данных уже следует окончить?
Posted via RSDN NNTP Server 1.9 delta
Re: ::recv() и std::string
От: adontz Грузия http://adontz.wordpress.com/
Дата: 26.11.04 18:47
Оценка: 14 (1)
Здравствуйте, _chill, Вы писали:

Вот выдрал. Кажись работало


STDMETHOD (Read)(LPVOID lpBuffer, ULONG bytesToRead, ULONG * lpBytesReaded)
    {
        // Local variables
        fd_set sockset;
        timeval time = {0, 1};
        // Code
        sockset.fd_count = 1;
        sockset.fd_array[0] = hSocket;
        if (select(0, &sockset, NULL, NULL, &time) != 0)
            {
                *lpBytesReaded = recv(hSocket, (LPSTR)lpBuffer, bytesToRead, 0);
                return S_OK;
            }
        else
            {
                *lpBytesReaded = 0;
                return E_PENDING;
            }
    }
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[2]: ::recv() и std::string
От: Olegator  
Дата: 27.11.04 09:28
Оценка: +1
Здравствуйте, zelyony, Вы писали:

Z>сколько байт можно прочитать не блокируясь помогает узнать


Как говорится здесь, "дурной стиль". Не используйте ioctlsocket с FIONREAD. А используйте лучше select или overlapped io. А ещё лучше передавайте размер сообщения в начале. Пример с select:

FD_SET fds_read;
FD_ZERO(&fds_read);
FD_SET(this->sock, &fds_read);
TIMEVAL tv = {5, 0};

char buf[4096];
std::string result;

int iRes = 0;
while(true)
{
    FD_SET t = fds_read;
    iRes = select(t.fd_count, &t, NULL, NULL, &tv);
    if(iRes == 0 || iRes == SOCKET_ERROR) break;

    iRes = recv(t.fd_array[0], buf, 4096, NULL);
    if(iRes == 0 || iRes == SOCKET_ERROR) break;
    result.append(buf, iRes);
}


С уважением,
Olegator
... << Rsdn@Home 1.1.4 beta 1 >>
Re: ::recv() и std::string
От: MaximE Великобритания  
Дата: 27.11.04 09:44
Оценка:
_chill wrote:

> Вот хочу принять данные с помошью блокирующего сокета и функции recv и положить их в std::string


Сколько именно данных ты хочешь принять?

Для TCP сокета два варианта: принять весь поток и читать сообщения из потока. Твой код делает первое. Вычитывание сообщений из потока зависит от используемого протокола (в HTTP, например, сообщения без body разделяются двумя <CR><LF>).

> Это выглядит примерно так:

>
> std::string mySocket::recive()
> {
>    int i_ret = 0;
>    char *buf[100];
>    std::string result;
>
>    while(true)
>    {
>       i_ret = ::recv(this->sock, buf, sizeof(buf),0);
>       if(i_ret == 0)
>       {
             //throw(SocketExeption("Connection has been gracefully closed"));
             // здесь, наверное, не нужно бросать исключение,
             // так как это нормальная и ожидаемая ситуация.
             // получение 0 из recv означает EOF - конец файла.
             // ты же не станешь бросать исключение, когда прочитаешь файл до конца?
             return result;
>       }
>       if(i_ret == SOCKET_SRROR)
>       {
>           throw(SocketExeption("recv error"));
>       }
>       //result += buf;
         // здесь серьезная ошибка - recv - не ориентирована на C-строки
         // и не добавляет 0 в конце считанных данных,
         // считанный поток может содержать 0 и ты потеряешь все данные после первого 0
         result->append(buf, i_ret);
>    }
>    return result;
> }
>

> Я прекрасно понимаю почему на этом методе программа "подвисает", сокет ведь не переведен в неблокирующий режим и я не собираюсь его переводить в этот режим.
> Как-нибудь можно узнать есть ли в системном буфере еще какие-нибудь данные или прием данных уже следует окончить?

Ты можешь это узнать, но это здесь бесполезно — пока сокет не закрыт отправляющей стороной ты не можешь узнать, будут ли еще поступать данные.

> Просто хочу чтобы все данные с сокета считались и метод вернул управление, но как это сделать не знаю...


Дожидайся пока recv вернет тебе 0.

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9 delta
Re[2]: ::recv() и std::string
От: _chill Россия  
Дата: 27.11.04 11:50
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>_chill wrote:


>> Вот хочу принять данные с помошью блокирующего сокета и функции recv и положить их в std::string


ME>Сколько именно данных ты хочешь принять?


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

void send(std::string);
std::string recive();

ME>Ты можешь это узнать, но это здесь бесполезно — пока сокет не закрыт отправляющей стороной ты не можешь узнать, будут ли еще поступать данные.

мне и не нужно узнавать это, мне нужно знать, что в данный момент все данные считаны... а все остальное пусть делает пользователь класса...

ME>Дожидайся пока recv вернет тебе 0.

это будет означать, что соединение завершено удаленной стороной, а мне не это нужно...


вот что получилось...


    std::string TcpSocket::recive()
    {
        u_long avail_bytes = 0;//количество байт для приема, находящихся на сокете
        int i_ret = 0;
        //Узнаем сколько байт надо считать из сокета
        //Если не было ошибки, то в avail_bytes будет содержаться количество байт для считывания из сокета
        //Для датаграммных сокетов возвращается также кол-во доступных, но считывать придётся по-"датаграммно". 
        //Система сама будет возвращать данные таким образом, к тому что, за один раз пару датаграмм не считать
        while (!avail_bytes)
        {
            i_ret = ioctlsocket(sock, FIONREAD, &avail_bytes);
            if(i_ret == SOCKET_ERROR)
            {
                throw(SocketExeption("ioctlsocket() error"));
            }
        }


        std::cout << "avail_bytes: " << avail_bytes << std::endl;
        char *buf = new char [avail_bytes+1];//выделяем память для получения данных из сокета

        i_ret = ::recv(sock, buf, avail_bytes, 0);//получаем данные
        if(i_ret == 0)
        {
            delete [] buf;//освобождаем память
            throw(SocketExeption("Connection has been gracefully closed"));
        }

        if(i_ret == SOCKET_ERROR)
        {
            delete [] buf;//освобождаем память
            throw(SocketExeption("recv() error"));
        }

        if(i_ret > 0)
        {
            buf[i_ret]='\0';
        }

        std::string result = buf;
        delete [] buf;//освобождаем память

        return result;
    }


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

и всем спасибо за ответы...

ME>--

ME>Maxim Yegorushkin
Re[3]: ::recv() и std::string
От: zelyony  
Дата: 27.11.04 12:10
Оценка: +1
в цикле ты тратишь процессорное время — нехорошо
если для начала тебе надо дождаться хоть каких-то данных, то вызываешь
recv
, а потом уже (если считалось больше 0) в цикле пока есть ещё данные их добавляешь к строке — будет лучше

> вот что получилось...

>
>
> std::string TcpSocket::recive()
> {
> u_long avail_bytes = 0;//количество байт для приема, находящихся на сокете
> int i_ret = 0;
> while (!avail_bytes)
> {
> i_ret = ioctlsocket(sock, FIONREAD, &avail_bytes);
> if(i_ret == SOCKET_ERROR)
> {
> throw(SocketExeption("ioctlsocket() error"));
> }
> }
>
Posted via RSDN NNTP Server 1.9 delta
Re[3]: ::recv() и std::string
От: MaximE Великобритания  
Дата: 27.11.04 12:34
Оценка:
_chill wrote:

>>> Вот хочу принять данные с помошью блокирующего сокета и функции recv и положить их в std::string

>
> ME>Сколько именно данных ты хочешь принять?
>
> если бы я знал... я просто пишу класс TcpSocket, у которого есть методы
>
> void send(std::string);
> std::string recive();

Для TCP сокета ты должен знать сколько тебе нужно получить или до каких пор нужно получать.

Возможные варианты:

Других вариантов нет.

> ME>Ты можешь это узнать, но это здесь бесполезно — пока сокет не закрыт отправляющей стороной ты не можешь узнать, будут ли еще поступать данные.

> мне и не нужно узнавать это, мне нужно знать, что в данный момент все данные считаны... а все остальное пусть делает пользователь класса...

Что значит "все данные"? Сдается мне, что ты имеешь ввиду только те данные, которые сейчас в буфере сокета.

[]

> вот что получилось...

>
>
>
>     std::string TcpSocket::recive()
>     {
>         u_long avail_bytes = 0;//количество байт для приема, находящихся на сокете
>         int i_ret = 0;
>         //Узнаем сколько байт надо считать из сокета
>         //Если не было ошибки, то в avail_bytes будет содержаться количество байт для считывания из сокета
>         //Для датаграммных сокетов возвращается также кол-во доступных, но считывать придётся по-"датаграммно".
>         //Система сама будет возвращать данные таким образом, к тому что, за один раз пару датаграмм не считать

             // причем здесь датаграммы??? у тебя TCP сокет

>         while (!avail_bytes)
             // получил (спин) блокировку
             // зачем это городить, если можно просто сделать блокирующий вызов read()???
             // или вернуть пустую строку, если в буфере пусто
>         {
>             i_ret = ioctlsocket(sock, FIONREAD, &avail_bytes);
>             if(i_ret == SOCKET_ERROR)
>             {
>                 throw(SocketExeption("ioctlsocket() error"));
>             }
>         }
>
>
>         std::cout << "avail_bytes: " << avail_bytes << std::endl;
>         //char *buf = new char [avail_bytes+1];//выделяем память для получения данных из сокета
             // лучше возьми vector - так у тебя гарантировано не будет утечек
             std::vector<char> buf(avail_bytes);

>         i_ret = ::recv(sock, &buf[0], avail_bytes, 0);//получаем данные
>         if(i_ret == 0)
>             throw(SocketExeption("Connection has been gracefully closed"));
>         if(i_ret == SOCKET_ERROR)
>             throw(SocketExeption("recv() error"));

             return std::string(&buf[0], buf.size());
>     }
>


--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9 delta
Re[4]: ::recv() и std::string
От: _chill Россия  
Дата: 29.11.04 22:28
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>_chill wrote:


>>>> Вот хочу принять данные с помошью блокирующего сокета и функции recv и положить их в std::string

>>
>> ME>Сколько именно данных ты хочешь принять?
>>
>> если бы я знал... я просто пишу класс TcpSocket, у которого есть методы
>>
>> void send(std::string);
>> std::string recive();

ME>Для TCP сокета ты должен знать сколько тебе нужно получить или до каких пор нужно получать.


ME>Возможные варианты:

ME>
ME>Других вариантов нет.


>> ME>Ты можешь это узнать, но это здесь бесполезно — пока сокет не закрыт отправляющей стороной ты не можешь узнать, будут ли еще поступать данные.

>> мне и не нужно узнавать это, мне нужно знать, что в данный момент все данные считаны... а все остальное пусть делает пользователь класса...

ME>Что значит "все данные"? Сдается мне, что ты имеешь ввиду только те данные, которые сейчас в буфере сокета.

Да именно это и имею ввиду...

кстати я метод немного изменил
std::vector<char> TcpSocket::recive();//вот так теперь, std::string не очень удачное решение, имхо, например при приеме бинарных данных...

ME>[]



ME>--

ME>Maxim Yegorushkin
Re[5]: ::recv() и std::string
От: _chill Россия  
Дата: 30.11.04 00:07
Оценка:
Мужики, а как вам вот такой вариант?
Чем он плох, чем хорош?
Вообщем напишите что думаете по поводу такой реализации...
и предложите плз еще свои варианты...
Заранее спасибо...
std::vector<char> TcpSocket::recive()
    {
        //ждем пока сокет будет готов для чтения, т.е. пока на него придут данные
        fd_set read_s;//множество
        FD_ZERO(&read_s);//обнуляем множество
        FD_SET(this->sock, &read_s);//заносим в него наш сокет
        int i_ret = 0;
        i_ret = ::select(read_s.fd_count, &read_s, NULL, NULL, NULL);//последний параметр - таймаут, не задан...
        if((i_ret == SOCKET_ERROR) || (i_ret == 0))//i_ret = 0 при истечении таймаута
        {
            throw(SocketExeption("select() error"));
        }

        //получаем количество байт, пришедших в буфер сокета
        u_long avail_bytes = 0;//количество байт для приема, находящихся на сокете
        //Узнаем сколько байт надо считать из сокета
        //Если не было ошибки, то в avail_bytes будет содержаться количество байт для считывания из сокета
        //Для датаграммных сокетов возвращается также кол-во доступных, но считывать придётся по-"датаграммно". 
        //Система сама будет возвращать данные таким образом, к тому что, за один раз пару датаграмм не считать
        i_ret = ioctlsocket(sock, FIONREAD, &avail_bytes);
        if(i_ret == SOCKET_ERROR)
        {
            throw(SocketExeption("ioctlsocket() error"));
        }

            //выделяем буфер под данные
        char *buf = new char [avail_bytes+1];//выделяем память для получения данных из сокета
        
        //получаем данные
        i_ret = ::recv(sock, buf, avail_bytes, 0);//получаем данные
        if(i_ret == 0)
        {
            delete [] buf;//освобождаем память
            connected = false;//соединение разорвано
            throw(SocketExeption("Connection has been gracefully closed"));
        }

        if(i_ret == SOCKET_ERROR)
        {
            delete [] buf;//освобождаем память
            throw(SocketExeption("recv() error"));
        }

        if(i_ret > 0)
        {
            buf[i_ret]='\0';
        }
        
        std::vector<char> v_result;
        std::copy(&buf[0], &buf[i_ret], std::back_inserter(v_result));//копируем буфер в std::vector<char>

        delete [] buf;//освобождаем память
        return v_result;
    }
Re[6]: ::recv() и std::string
От: MaximE Великобритания  
Дата: 30.11.04 08:08
Оценка:
_chill wrote:

> Мужики, а как вам вот такой вариант?

> Чем он плох, чем хорош?
> Вообщем напишите что думаете по поводу такой реализации...
> и предложите плз еще свои варианты...
> Заранее спасибо...
>
> std::vector<char> TcpSocket::recive()
>     {
>         //ждем пока сокет будет готов для чтения, т.е. пока на него придут данные
>         fd_set read_s;//множество
>         FD_ZERO(&read_s);//обнуляем множество
>         FD_SET(this->sock, &read_s);//заносим в него наш сокет
>         int i_ret = 0;
>         i_ret = ::select(read_s.fd_count, &read_s, NULL, NULL, NULL);//последний параметр - таймаут, не задан...
>         if((i_ret == SOCKET_ERROR) || (i_ret == 0))//i_ret = 0 при истечении таймаута
>         {
>             throw(SocketExeption("select() error"));
>         }
>
>         //получаем количество байт, пришедших в буфер сокета
>         u_long avail_bytes = 0;//количество байт для приема, находящихся на сокете
>         //Узнаем сколько байт надо считать из сокета
>         //Если не было ошибки, то в avail_bytes будет содержаться количество байт для считывания из сокета
>         //Для датаграммных сокетов возвращается также кол-во доступных, но считывать придётся по-"датаграммно".
>         //Система сама будет возвращать данные таким образом, к тому что, за один раз пару датаграмм не считать
>         i_ret = ioctlsocket(sock, FIONREAD, &avail_bytes);
>         if(i_ret == SOCKET_ERROR)
>         {
>             throw(SocketExeption("ioctlsocket() error"));
>         }
>
>             //выделяем буфер под данные
>         char *buf = new char [avail_bytes+1];//выделяем память для получения данных из сокета
>         
>         //получаем данные
>         i_ret = ::recv(sock, buf, avail_bytes, 0);//получаем данные
>         if(i_ret == 0)
>         {
>             delete [] buf;//освобождаем память
>             connected = false;//соединение разорвано
>             throw(SocketExeption("Connection has been gracefully closed"));
>         }
>
>         if(i_ret == SOCKET_ERROR)
>         {
>             delete [] buf;//освобождаем память
>             throw(SocketExeption("recv() error"));
>         }
>
>         if(i_ret > 0)
>         {
>             buf[i_ret]='\0';
>         }
>         
>         std::vector<char> v_result;
>         std::copy(&buf[0], &buf[i_ret], std::back_inserter(v_result));//копируем буфер в std::vector<char>
>
>         delete [] buf;//освобождаем память
>         return v_result;
>     }
>


Хреновый код.

Зачем ты выделяешь char[], читаешь в него, а затем копируешь его в вектор и уничтожаешь? Посмотри какая ерунда у тебя получилась с throw — тебе сначала приходится удалить массив, а только затем throw. Кроме того, в строке, где ты копируешь свой массив в вектор, если вектор бросит исключение твой массив никогда не будет удален.

Зачем ты добавляешь ноль в конец массива, когда он потом не копируется в вектор?

Выкинь свой динамаический char[] и сразу читай в vector, см. http://rsdn.ru/forum/?mid=920004
Автор: MaximE
Дата: 27.11.04
. Деструктор вектора освобождает память, поэтому тебе не придется руками его освобождать перед throw. Также, твой код станет более надежным, так как с вектором у тебя будет одно выделение памяти, тогда как сейчас — у тебя два выделения + копирование.

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9 delta
Re[6]: ::recv() и std::string
От: IGor_79 Украина  
Дата: 30.11.04 08:36
Оценка: 1 (1) +1 :)
Здравствуйте, _chill, Вы писали:

Такое ощущение, что вы правой рукой пытаетесь почесать левое ухо

1. Зачем вы используете select, если сокеты блокирующие?
Какая разница где заблокируется — на recv или на select?
2. ioctlsocket с флагом FIONREAD возвращает не размер всего
доступного буфера, а размер данных, которые могут быть
получены за ОДИН вызов recv

Поэтому, предлагаю убрать нафиг ioctlsocket и select. А
если вы боитесь блокировки — используйте неблокирущие сокеты.
Re[7]: ::recv() и std::string
От: _chill Россия  
Дата: 30.11.04 17:07
Оценка:
Здравствуйте, IGor_79, Вы писали:

IG_>Здравствуйте, _chill, Вы писали:


IG_>Такое ощущение, что вы правой рукой пытаетесь почесать левое ухо


Вы знаете, это у меня с легкостью получается, в отличие от программирования сокетов =)))

IG_>1. Зачем вы используете select, если сокеты блокирующие?

IG_>Какая разница где заблокируется — на recv или на select?
IG_>2. ioctlsocket с флагом FIONREAD возвращает не размер всего
IG_>доступного буфера, а размер данных, которые могут быть
IG_>получены за ОДИН вызов recv
так мне это и нужно... а если я вызову ioctlsocket без select, то возможна ситуация, когда данные еще не пришли... и ioctlsocket просто вернет 0...
в итоге такой код программы выполнится некорректно, данные будут потеряны...

TcpSocket sock;
sock.connect("pop.mail.ru", 110);
sock.recive();//куда-то все выводим, только вот сервер не успевает мне отправить данные, до вызова recive...
sock.send("text");


а если я буду использовать только recv, как мне узнать что буфер уже пуст?
например, такой код:
int i_ret;
char ch;
std::vector<char> v_recv;
while(true)
{
    i_ret = ::recv(sock, &ch, 1, 0);
    if(i_ret == SOCKET_ERROR)
    {
         throw(SocketExeption("recv() error"));
    }
    if(i_ret == 0)
    {
         throw(SocketExeption("соеднение прервано удаленной стороной"));//как вот с этим быть?
    }
    v_recv.push_back(ch);
}

где тут ставить break?
как узнать что в буфере сокета уже пусто?
if(i_ret == 0) — по MSDN это получается как раз то, что у меня в exeption'е написано... "соеднение прервано удаленной стороной"


IG_>Поэтому, предлагаю убрать нафиг ioctlsocket и select. А

IG_>если вы боитесь блокировки — используйте неблокирущие сокеты.
Re[8]: ::recv() и std::string
От: butcher Россия http://bu7cher.blogspot.com
Дата: 01.12.04 06:06
Оценка:
Здравствуйте, _chill, Вы писали:

_>так мне это и нужно... а если я вызову ioctlsocket без select, то возможна ситуация, когда данные еще не пришли... и ioctlsocket просто вернет 0...

_>в итоге такой код программы выполнится некорректно, данные будут потеряны...
мдя.. а если код переписать, чтобы он корректно выполнялся? Или, что написано в программе, не вырубишь и топорами?

_>
_>TcpSocket sock;
_>sock.connect("pop.mail.ru", 110);
_>sock.recive();//куда-то все выводим, только вот сервер не успевает мне отправить данные, до вызова recive...
/* а вы используйте блокирующие сокеты, тогда recv подождёт (тогда весь мир подождёт :)*/
_>sock.send("text");
_>


_>а если я буду использовать только recv, как мне узнать что буфер уже пуст?

Зачем вам знать что буфер уже пуст?
_>например, такой код:
_>
_>int i_ret;
_>char ch;
_>std::vector<char> v_recv;
_>while(true)
_>{
_>    i_ret = ::recv(sock, &ch, 1, 0);
_>    if(i_ret == SOCKET_ERROR)
_>    {
_>         throw(SocketExeption("recv() error"));
_>    }
_>    if(i_ret == 0)
_>    {
/* 
    сюда вы попадёте только в одном случае, когда противоположная сторона закроет соединение
    и вы прочитаете все данные, которые она послала.
*/
_>         throw(SocketExeption("соеднение прервано удаленной стороной"));//как вот с этим быть?
_>    }
_>    v_recv.push_back(ch);
_>}
_>

Ну этот код гораздо лучше первого

Нет ничего невозможного..
Re[9]: ::recv() и std::string
От: _chill Россия  
Дата: 01.12.04 12:57
Оценка:
Здравствуйте, butcher, Вы писали:

B>Здравствуйте, _chill, Вы писали:


_>>так мне это и нужно... а если я вызову ioctlsocket без select, то возможна ситуация, когда данные еще не пришли... и ioctlsocket просто вернет 0...

_>>в итоге такой код программы выполнится некорректно, данные будут потеряны...
B>мдя.. а если код переписать, чтобы он корректно выполнялся? Или, что написано в программе, не вырубишь и топорами?

_>>
_>>TcpSocket sock;
_>>sock.connect("pop.mail.ru", 110);
_>>sock.recive();//куда-то все выводим, только вот сервер не успевает мне отправить данные, до вызова recive...
B>/* а вы используйте блокирующие сокеты, тогда recv подождёт (тогда весь мир подождёт :)*/
_>>sock.send("text");
_>>


_>>а если я буду использовать только recv, как мне узнать что буфер уже пуст?

B>Зачем вам знать что буфер уже пуст?
а как еще можно скачать все данные, что пришли на сокет после моего send?
или это вообще не нужно и всегда скачивать до разделителя?
в принципе с pop3/smtp/ftp так оно и есть, нужно качать до определенного разделителя...
а потом только слать следующий запрос... но разве во всех протоколах так?
я хочу сделать класс, который можно будет использовать в проектах, чтобы не делать работу с скоетами для каждого проекта заново...

_>>например, такой код:

_>>
_>>int i_ret;
_>>char ch;
_>>std::vector<char> v_recv;
_>>while(true)
_>>{
_>>    i_ret = ::recv(sock, &ch, 1, 0);
_>>    if(i_ret == SOCKET_ERROR)
_>>    {
_>>         throw(SocketExeption("recv() error"));
_>>    }
_>>    if(i_ret == 0)
_>>    {
B>/* 
B>    сюда вы попадёте только в одном случае, когда противоположная сторона закроет соединение
B>    и вы прочитаете все данные, которые она послала.
B>*/
_>>         throw(SocketExeption("соеднение прервано удаленной стороной"));//как вот с этим быть?
_>>    }
_>>    v_recv.push_back(ch);
_>>}
_>>

B>Ну этот код гораздо лучше первого
да, только он виснет...
точнее получается, что recv блокируется, как только данные заканчиваются...
Re[10]: ::recv() и std::string
От: IGor_79 Украина  
Дата: 01.12.04 13:29
Оценка:
Здравствуйте, _chill,

Ага, кажись понял вашу проблему

Вы писали:

_>Здравствуйте, butcher, Вы писали:


_>а как еще можно скачать все данные, что пришли на сокет после моего send?

_>или это вообще не нужно и всегда скачивать до разделителя?
а разве в цикле вы не выкачали все данные?
_>в принципе с pop3/smtp/ftp так оно и есть, нужно качать до определенного разделителя...
не зря ведь наверное
_>а потом только слать следующий запрос... но разве во всех протоколах так?
в известных мне — да. Если же нет разделителя или размера, тогда остается
только висеть в recv и ждать...
_>я хочу сделать класс, который можно будет использовать в проектах, чтобы не делать работу с скоетами для каждого проекта заново...
похвально

_>>>например, такой код:

_>>>
_>>>int i_ret;
_>>>char ch;
_>>>std::vector<char> v_recv;
_>>>while(true)
_>>>{
_>>>    i_ret = ::recv(sock, &ch, 1, 0);
_>>>    if(i_ret == SOCKET_ERROR)
_>>>    {
_>>>         throw(SocketExeption("recv() error"));
_>>>    }
_>>>    if(i_ret == 0)
_>>>    {
B>>/* 
B>>    сюда вы попадёте только в одном случае, когда противоположная сторона закроет соединение
B>>    и вы прочитаете все данные, которые она послала.
B>>*/
_>>>         throw(SocketExeption("соеднение прервано удаленной стороной"));//как вот с этим быть?
_>>>    }
_>>>    v_recv.push_back(ch);
_>>>}
_>>>

B>>Ну этот код гораздо лучше первого
_>да, только он виснет...
_>точнее получается, что recv блокируется, как только данные заканчиваются...
дык, вы же блокирующий режим используете...есесно виснет. При определенных
раскладах ваш вариант тоже повиснет, на select.

Советую почитать литературу , тогда вопросы подобного рода сами отпадут

Вот цитатка из "Network Programming For Microsoft Windows":

The problem with this code is that the recv function might never return if no data is pending because the statement says to return only after reading some bytes from the system's input buffer. Some programmers might be tempted to peek for the necessary number of bytes in the system's buffer by using the MSG_PEEK flag in recv or by calling ioctlsocket with the FIONREAD option. Peeking for data without actually reading the data (reading the data actually removes it from the system's buffer) is considered bad programming practice and should be avoided at all costs. The overhead associated with peeking is great because one or more system calls are necessary just to check the number of bytes available. Then, of course, there is the overhead of making the actual recv call that removes the data from the system buffer. What can be done to avoid this? The idea is to prevent the application from totally freezing because of lack of data (either from network problems or from client problems) without continually peeking at the system network buffers. One method is to separate the application into a reading thread and a computation thread. Both threads share a common data buffer. Access to this buffer is protected through the use of a synchronization object, such as an event or a mutex. The purpose of the reading thread is to continually read data from the network and place it in the shared buffer. When the reading thread has read the minimum amount of data necessary for the computation thread to do its work, it can signal an event that notifies the computation thread to begin. The computation thread then removes a chunk of data from the buffer and performs the necessary calculations.


Да и еще, читать посимвольно из буфера — решение хреновое
(откровенно говоря)
Re[11]: ::recv() и std::string
От: _chill Россия  
Дата: 01.12.04 13:34
Оценка:
Здравствуйте, IGor_79, Вы писали:

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

IG_>Да и еще, читать посимвольно из буфера — решение хреновое

IG_>(откровенно говоря)
знаю... но как вы тогда предлогаете считать все в std::vector<char>
не использую промежуточного буфера char* и std::copy?
Re[12]: ::recv() и std::string
От: MaximE Великобритания  
Дата: 01.12.04 13:49
Оценка:
_chill wrote:

> IG_>Да и еще, читать посимвольно из буфера — решение хреновое

> IG_>(откровенно говоря)
> знаю... но как вы тогда предлогаете считать все в std::vector<char>
> не использую промежуточного буфера char* и std::copy?

Ты читаешь, что тебе пишут? http://rsdn.ru/forum/?mid=920004
Автор: MaximE
Дата: 27.11.04


std::vector<char> buf(avail_bytes);
i_ret = ::recv(sock, &buf[0], buf.size(), 0);


--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9 delta
Re[13]: ::recv() и std::string
От: _chill Россия  
Дата: 01.12.04 14:10
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>_chill wrote:


>> IG_>Да и еще, читать посимвольно из буфера — решение хреновое

>> IG_>(откровенно говоря)
>> знаю... но как вы тогда предлогаете считать все в std::vector<char>
>> не использую промежуточного буфера char* и std::copy?

ME>Ты читаешь, что тебе пишут? http://rsdn.ru/forum/?mid=920004
Автор: MaximE
Дата: 27.11.04


ME>
ME>std::vector<char> buf(avail_bytes);
ME>i_ret = ::recv(sock, &buf[0], buf.size(), 0);
ME>

черт, проглядел... спасибо =)))
просто думал, что это мой код как цитата и даже не смотрел его...

ME>--

ME>Maxim Yegorushkin
Re[14]: ::recv() и std::string
От: _chill Россия  
Дата: 01.12.04 20:06
Оценка:
Мужики, а как получить данные до какого-нибудь разделителя?
как я понял надо побайтово считывать...
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.