Проблема с сервером C / C ++ UDP - PullRequest
0 голосов
/ 15 августа 2011

У меня здесь есть UDP-сервер, который прослушивает порт 9001. Я определил максимальный размер куска 4096 для приема буфера данных.

Моя проблема в том, что когда клиент отправляет большие данные, например,20000 байт, я получил только определенный размер чанка.У меня есть бесконечный цикл, который будет непрерывно получать данные от клиента.

Вот мой код:

  void TdaServer::StartServer()
  {
      #ifdef LOGGING_ENABLED
      LOG_MSG("WARNING: StartServer called");
      #endif

       struct sockaddr_in cli;
       //int socketId;
       socklen_t size;

       socketId=socket(AF_INET, SOCK_DGRAM, 0);
       if (socketId < 0)
       {
          #ifdef LOGGING_ENABLED
          LOG_MSG("ERROR: failed to create socket!");
          #endif
       } 


       int length = sizeof(server);
       bzero(&server,length);
       server.sin_family        =AF_INET;
       server.sin_addr.s_addr   =INADDR_ANY;
       int port = atoi(Config::GetEnv("nfc_demo_port").c_str());
       if(port==0)
          port = 9001;

       server.sin_port          =htons(port);

       if (bind(socketId,(struct sockaddr *)&server,length)<0) 
       {
          #ifdef LOGGING_ENABLED
          LOG_MSG("Error binding!");
          #endif
       }

       size = sizeof(struct sockaddr_in); 

       thread_parm_t *parm=NULL;
       parm             = new thread_parm_t;
       parm->socketId   = socketId;
       parm->client     = cli;
       parm->size       = size;
       #ifdef LOGGING_ENABLED
       LOG_MSG("TDA server started, socket id: %i, port: %d", socketId, port);
       #endif

       pthread_attr_t attr;
       pthread_t clientthread;
       pthread_attr_init(&attr);
       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 
       pthread_create(&clientthread, &attr, Receive, (void *)parm);
  }

  void *Receive(void *parm)
  {
    thread_parm_t *p = (thread_parm_t *)parm;
    char buffer[MAX_CHUNK_SIZE+1]; //MAX_CHUNK_SIZE = 4096
    string data="";
    extern int errno;
    while(true)
    {
        #ifdef LOGGING_ENABLED
        LOG_MSG("-TdaServer waiting for data...");
        #endif

        int n = recvfrom(p->socketId,buffer,MAX_CHUNK_SIZE,0,
                (struct sockaddr *)&p->client, &p->size); //wasn't able to receive large data ex. 20000 bytes

        #ifdef LOGGING_ENABLED
        LOG_MSG("-TdaServer received data size: %d", n);
        #endif
        if(n>0)
        {
            data = data.append(string(buffer).substr(0, n));
            #ifdef LOGGING_ENABLED
            LOG_MSG("-TdaServer [%s] n: %d < mcs: %d, %s", inet_ntoa(p->client.sin_addr), n, MAX_CHUNK_SIZE, (n<MAX_CHUNK_SIZE?"true":"false"));
            #endif

            if(n<MAX_CHUNK_SIZE)//received complete
            {
                #ifdef LOGGING_ENABLED
                LOG_MSG("-TdaServer received data size: %d, complete!", n);
                #endif

                TcbMgrConnection::SendResponse(data.c_str());
                data = "";
            }           
        }
        else if(n<0)
        {
            #ifdef LOGGING_ENABLED
            LOG_MSG("TdaServer: Error reading from socket");
            #endif
        }
        else
        {
            if(n==0)
            {
                #ifdef LOGGING_ENABLED
                LOG_MSG("IPServer: client %d disconnected", p->socketId);
                #endif              
                //break;
            }
        }
    }
    close(p->socketId);
    return NULL;
  }

Вот код клиента UDP, написанный на VB.NET

Private Sub cmdSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdSend.Click

    Dim pRet As Integer

    Try
        GLOIP = IPAddress.Parse(txtIP.Text)
        GLOINTPORT = txtPort.Text
        udpClient.Connect(GLOIP, GLOINTPORT)
        bytCommand = Encoding.ASCII.GetBytes(txtMessage.Text)
        pRet = udpClient.Send(bytCommand, bytCommand.Length)
        Console.WriteLine("No of bytes send " & pRet)
        txtInfo.Text = "INFORMATION" & vbCrLf & "No of bytes send " & pRet
    Catch ex As Exception
        Console.WriteLine(ex.Message)
        txtInfo.Text = txtInfo.Text & vbCrLf & ex.Message
    End Try
End Sub

Я правильно делаю?Пожалуйста, помогите .. Заранее спасибо

Ответы [ 3 ]

2 голосов
/ 15 августа 2011

Чтение из сокета UDP исключает из очереди только одну дейтаграмму .Если вы дадите ему буфер меньше размера дейтаграммы, остальные отбрасываются.Дальнейшее чтение с данными возврата из дейтаграммы next .UDP не выполняет никакой конкатенации / разделения данных для вас.

Максимальный размер дейтаграммы составляет 64 КБ (поле размера в заголовке UDP составляет 16 бит), поэтому вы можете отправлять до такого количества одновременно.с одним вызовом write() / send() / sendto(), что, скорее всего, приведет к фрагментации IP .На принимающей стороне вы должны сопоставить размер принимающего буфера с максимальным размером, который вы отправляете.

2 голосов
/ 15 августа 2011

Вот так работает recvfrom ()

Функция recvfrom () возвращает длину сообщения, записанного в буфер, на который указывает аргумент buffer.Для сокетов на основе сообщений, таких как SOCK_RAW, SOCK_DGRAM и SOCK_SEQPACKET, все сообщение должно быть прочитано за одну операцию.Если сообщение слишком длинное, чтобы поместиться в предоставленном буфере, и MSG_PEEK не установлен в аргументе flags, лишние байты должны быть отброшены.Для потоковых сокетов, таких как SOCK_STREAM, границы сообщений должны игнорироваться.В этом случае данные должны быть возвращены пользователю, как только они станут доступны, и никакие данные не будут отброшены.

Либо выделите больший буфер, либо измените клиента для отправки меньших пакетов

1 голос
/ 15 августа 2011

Я не буду рекомендовать отправку больших пакетов UDP.Размер данных пакета UDP ограничен примерно 1300 байтами (учитывая, что типичный MTU маршрутизаторов равен 1500 или меньше), все большее фрагментируется маршрутизатором и затем собирается обратно.

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

Лучший способ передать большие блоки - это разделить их вручную на части~ 1250 байт, затем склеиваем кусочки в приемнике.Имейте в виду, что некоторые из них могут быть потеряны или доставлены из строя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...