На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела FAQ в группе разделов С++.
1. Раздел FAQ предназначен для публикации готовых статей.
2. Здесь нельзя задавать вопросы, для этого существуют соответствующие разделы:
Чистый С++
Visual C++ / MFC / WTL / WinApi
Borland C++ Builder
COM / DCOM / ActiveX / ATL
Сопутствующие вопросы
3. Внимание, все темы и сообщения в разделе премодерируются. Любое сообщение или тема будут видны остальным участникам только после одобрения модератора.
Модераторы: B.V., Qraizer
  
> Считывание данных из сокета. , c таймаутом
    Те, кто работал с tcp-ip сокетами, знают про всякие подводные камни, связаные с этим.
    Приведу несколько из них.

    Первый: данные в tcp-ip стеке могут появляться не все сразу, а кусками. Если клиент послал
    нам данные с помощью одной функции send(), это совсем не значит, что они могут быть приняты одной
    функцией recv().
    Второй: если с отправителем данных что либо случилось, и он не смог данные полностью, то возможны зависоны на функции recv() (при использовании блокирующих сокетов).Аналогично для фукнции send().


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

    Вот, собственно, функции:

    ExpandedWrap disabled
       
       
      #define     _SOCKET_TIMEOUT    5000000     //величина таймаута в микросекундах
       
      int SendNBytes(int nSocket, char *pBuffer, int iMessageLength)
      {
        int     iRC         = 0;
        int     iSendStatus = 0;
        timeval SendTimeout;
       
        //установка величины таймаута
        SendTimeout.tv_sec  = 0;
        SendTimeout.tv_usec = _SOCKET_TIMEOUT;              
       
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(nSocket, &fds);
       
       
        //..до тех пор, пока нам нужно посылать данные...
        while(iMessageLength > 0)
        {
          iRC = select(0, NULL, &fds, NULL, &SendTimeout);
       
          //истек таймаут, возврат ошибки
          if(!iRC)
            return -1;
       
          //произошла ошибка
          if(iRC < 0)
            return WSAGetLastError();
       
          //отправить несколько байт
          iSendStatus = send(nSocket, pBuffer, iMessageLength, 0);  
                                                              
          //произошла ошибка в момент отправки данных
          if(iSendStatus < 0)
            return WSAGetLastError();
          else
          {
            //обновить буфер и счетчик
            iMessageLength -= iSendStatus;
            pBuffer += iSendStatus;
          }
        }
       
        return 0;
      }


    ExpandedWrap disabled
       
       
      //функция приема данных
      int ReceiveNBytes(int nSocket, char *pBuffer, int iStillToReceive)
      {
        int     iRC               = 0;
        int     iReceiveStatus    = 0;
        timeval ReceiveTimeout;
       
        //установка величины таймаута
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(nSocket, &fds);
       
        ReceiveTimeout.tv_sec  = 0;
        ReceiveTimeout.tv_usec = _SOCKET_TIMEOUT;             // 500 ms
       
        //..пока данные не посланы..
        while(iStillToReceive > 0)
        {
          iRC = select(0, &fds, NULL, NULL, &ReceiveTimeout);
          
          //выход по таймауту
          if(!iRC)
            return -1;
       
          //произошла какая то ошибка
          if(iRC < 0)
            return WSAGetLastError();
       
          //прием нескольких байт
          iReceiveStatus = recv(nSocket, pBuffer, iStillToReceive, 0);
       
          //произошла ошибка в момент функции recv()
          if(iReceiveStatus < 0)
          {
            return WSAGetLastError();
          }
          else
          {
            //изменили величину счетчика и буфер
            iStillToReceive -= iReceiveStatus;
            pBuffer += iReceiveStatus;
          }
        }
       
        return 0;
      }


    как пользоваться:

    Хорошим тоном считается посылка в начале основного сообщения информацию о длине сообщения.
    Нужно договориться (разработчикам клиента и сервера), что, например, первые 2 или 4 байта в начале
    сообщения будут длиной сообщения. Поэтому, полное сообщение из сокета можно получить примерно так:

    ExpandedWrap disabled
       
       
      TCHAR   *szMessage;
      TCHAR   szLength[4];
      int nLength;
       
      if(RecvNBytes(nSocket, szLength, 2) != 0)   //приняли первые 2 байта - длину
      {
        return FALSE;
      }
       
      nLength = atoi(szLength);       //преобразовали 2 байта в длину
      szMessage = new TCHAR[szLength];
       
      if(RecvNBytes(nSocket, szMessage, nLength) !=0) //приняли полное сообщение
      {
        return FALSE;
      }
    Сообщение отредактировано: AQL -
      А можно и WSASelectEvent, это вроде даж лучше будет, но вот это для винды, о других ОС сказать не могу.
        Цитата
        if(RecvNBytes(nSocket, szLength, 2) != 0) //приняли первые 2 байта - длину
        {
        return FALSE;
        }

        Почему 2 байта, а не 4!? :)
          Сколька хочешь, дарагой, хоть 20! ;) . В примере макс. длина мессаги - 100 символов.
          На самом деле там лучше использовать не atoi(szLength), а strtol с базисом 16, или ещё лучше - посылать (без преобразования в строку) байты числа nLength, здесь это НЕ сделано для наглядности, к тому же - со строкой понятно как работать на разных платформах, а вот, предположим, сборка из 4 -х байтов int на Apple и Intel проходит по разному ;) .
            И что на разныйх платформах у данного кода будут разные результаты?
            ExpandedWrap disabled
              LPTSTR  lpBuffer=NULL;
              lpBuffer+4=lpData;
              CopyMemory( lpBuffer, reinterpret_cast<char*>(&dwBytesToWrite), sizeof(DWORD));
              Клиент отправляет данные...
               
              Сервер получил данные...
              DWORD dwMsgSize=0;
              CopyMemory( reinterpret_cast<char*>(&dwMsgSize), lpBuffer, sizeof(DWORD));

            А если у тебя отправляется толко 100 символов, тогда можно было и 1 байт ;)
            Но для реального отправления мне кажется ве-таки лучше использовать первые 4 байта,
            я думаю ты сам понимаешь почему...
            Сообщение отредактировано: ViGOur -
              Дело в этом:
              ExpandedWrap disabled
                TCHAR szLength[4]; // в szLength - мусор
                int nLength;       // в nLength - тоже
                 
                if(RecvNBytes(nSocket, szLength, 2) != 0)  //приняли первые 2 байта - длину
                // но в других 2-х байтах все равно мусор!
                {
                  return FALSE;
                }
                 
                nLength = atoi(szLength);  //преобразовали 2 байта в длину
                // не 2, а непонятно сколько. первые два байта по делу, в остальных - неизвестно что,
                // а ведь еще это безобразие должно еще и нулем кончаться!
                Такой я ещё не пробовал, .. но вроде бы тоже не должно работать.
                Дело в том, что младший и старший байты на разных платформах перепутаны.
                Ты отправил клиенту первым символом - младший байт, а он решил - что это старший, .... ну и т.д.
                  2Uncle_Bob
                  Цитата
                  Дело в этом:

                  Эти пременные у него не изображены внутри какой-либо функции. Мож они глобальные? :blink:
                  Тогда всё чики-пики. :rolleyes:
                    Цитата

                    я думаю ты сам понимаешь почему...


                    это всего лишь иллюстрация, как пользоваться приведёнными функциями, кто как хочет, так и использует.
                      M
                      Peter, раздел ФАКа предназначен только для статей и вопросов связанных с ними. Твой вопрос я отделил в тему: Куча ошибок в файле sys/socket
                        Цитата ViGOur @ 28.05.04, 10:37
                        И что на разныйх платформах у данного кода будут разные результаты?
                        ExpandedWrap disabled
                          LPTSTR  lpBuffer=NULL;
                          lpBuffer+4=lpData;
                          CopyMemory( lpBuffer, reinterpret_cast<char*>(&dwBytesToWrite), sizeof(DWORD));
                          Клиент отправляет данные...
                           
                          Сервер получил данные...
                          DWORD dwMsgSize=0;
                          CopyMemory( reinterpret_cast<char*>(&dwMsgSize), lpBuffer, sizeof(DWORD));

                        А если у тебя отправляется толко 100 символов, тогда можно было и 1 байт ;)
                        Но для реального отправления мне кажется ве-таки лучше использовать первые 4 байта,
                        я думаю ты сам понимаешь почему...

                        #include <sys/types.h>
                        #include <netinet/in.h> /* if i remember right (on linux)*/

                        /* on send part of system */
                        ... put_length_into_packet(u_int16_t length)
                        length = htons(length);/ * convert host order to network order */
                        memcpy(buffer, &length, sizeof(length)); /* put 2 bytes into start of the packet */
                        ... /* prepare rest of rthe buffer */
                        send(fd, buffer, sizeof(buffer), 0);

                        /* on receive part of system */
                        ... get_packet(...)
                        u_int16_t length;
                        recv(fd, &length, sizeof(length), MSG_PEEK);
                        length = ntohs(length);
                        recv(fd, buffer, length, MSG_WAITALL);

                        For simplisity sake i omit check of recv and send return values
                        0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                        0 пользователей:


                        Рейтинг@Mail.ru
                        [ Script execution time: 0,0337 ]   [ 16 queries used ]   [ Generated: 26.04.24, 05:25 GMT ]