neztreh
neztreh

Reputation: 4231

C/C++ UDP Server Problem

I have here a UDP Server which listens on port 9001. I have defined a maximum chunk size of 4096 for receiving data buffer.

My problem is, when the client sends a large data, ex. 20000 bytes, I only received the chunk size I defined. I have an endless loop which will continuously received data from client.

Here is my code:

  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;
  }

Here is the UDP client code, written in 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

Am I doing it right? Please help.. Thanks in advance

Upvotes: 0

Views: 1570

Answers (3)

hamstergene
hamstergene

Reputation: 24429

I won't recommend sending large UDP packets. UDP packet data size is limited to about 1300 bytes (given that typical MTU of routers is 1500 or less), anything bigger is fragmented by router and then assembled back.

If any of the routers in the way does not allow fragments, all your packets will be discarded. If any fragment is lost, whole packet will be discarded, even though in many situations it's still possible to use the rest of the data.

A better way to transmit larger blocks, is to split them manually into pieces of ~1250 bytes, then glue pieces together at the receiver. Keep in mind that some of them can be lost or arrive out of order.

Upvotes: 1

Nikolai Fetissov
Nikolai Fetissov

Reputation: 84151

Reading from UDP socket dequeues exactly one datagram. If you give it a buffer smaller then the datagram size the rest is discarded. Further reading with return data from the next datagram. UDP doesn't do any concatenation/splitting of the data for you.

Maximum datagram size is 64KB (the size field in the UDP header is 16 bit), so you can send up to that much at a time with a singe write()/send()/sendto() call, most likely resulting in IP fragmentation. On the receiving end you have to match the accepting buffer size to the maximum size you are sending.

Upvotes: 2

Dipstick
Dipstick

Reputation: 10119

That is how recvfrom() works

The recvfrom() function shall return the length of the message written to the buffer pointed to by the buffer argument. For message-based sockets, such as SOCK_RAW, SOCK_DGRAM, and SOCK_SEQPACKET, the entire message shall be read in a single operation. If a message is too long to fit in the supplied buffer, and MSG_PEEK is not set in the flags argument, the excess bytes shall be discarded. For stream-based sockets, such as SOCK_STREAM, message boundaries shall be ignored. In this case, data shall be returned to the user as soon as it becomes available, and no data shall be discarded.

Either allocate a larger buffer or change the client to send smaller packets

Upvotes: 2

Related Questions