1

I'm new to c++ and need help. I use an UDP server to receive structure however i have problem to read it , the client send a structure I call : ChannAccessReq so the structure is send and the server receive it with RECVFROM and I use a general structure by reading the header (H1) of the struct only and then i do a read when a condition is fullfill with a more precise structure (temp2) for the buffer. However the client need to send the message twice , the first time it goes until recvfrom and the second it reach read() (i think) I tried all the day and wonder if its the size of the buffer ?

I think the most sensitive part is in the server with the recvfrom() who have a struct different from the read() just after..

I hope it's clear!

here is the server :

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "struct.h"
#include <iostream>
void DieWithError(char *err) {
   perror(err);
   exit(1);
}

typedef struct IntMsgHeaderType {
  uint8_t code ;      // Code message7
  uint8_t bourrage ;  // Octet de bourrage
  uint16_t ParamLength; // Longueur eventuel données complémentaires

} HeaderInt;

typedef struct TextMessage //TESTTESTTEST
{
     HeaderInt H;      // Code message7


};

int main(int argc, char *argv[])
{
     int sock;                        /* Socket */
     struct sockaddr_in echoServAddr; /* Local address */
     struct sockaddr_in echoClntAddr; /* Client address */
     unsigned int cliAddrLen;         /* Length of incoming message */
     unsigned short echoServPort;     /* Server port */
     int recvMsgSize;                 /* Size of received message */

   struct TextMessage * temp = (TextMessage *)malloc(sizeof(struct TextMessage));
   HeaderInt *H1 =(HeaderInt *)malloc(104+sizeof(HeaderInt));
    ChanAccesReq *temp2=(ChanAccesReq *)malloc(sizeof(ChanAccesReq));
  if (!argv[1]) {
      fprintf(stderr,"no port number provided");
     exit(1);
  }

      echoServPort = atoi(argv[1]);  /* First arg:  local port */

    /* Create socket for sending/receiving datagrams */
  if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
    DieWithError("socket() failed");

  /* Construct local address structure */
  memset(&echoServAddr, 0, sizeof(echoServAddr));   /* Zero out structure */
  echoServAddr.sin_family = AF_INET;                /* Internet address family */
  echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
  echoServAddr.sin_port = htons(echoServPort);      /* Local port */

  /* Bind to the local address */
  if (bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
     DieWithError("bind() failed");

  for (;;) /* Run forever */
  {

         cliAddrLen = sizeof(echoClntAddr);
         int nbrOctet;

         if (recvfrom(sock, H1, sizeof(*H1), 0,(struct sockaddr *) &echoClntAddr, &cliAddrLen)>0 && H1->code==1){


         //read(sock,H1,sizeof(*H1));
         std::cout<<"taille nbrOctet : "<<nbrOctet<<'\n';
         memset(&echoServAddr, 0, sizeof(echoServAddr));   /* Zero out structure */

         read(sock, temp2, sizeof(*temp2));
                //read(sock,temp2,sizeof(*temp2))>0;
                    std::cout<<unsigned(temp2->P.linkAddr)<<'\n';

        };

   }

        close(sock);
        return 0;
  }

and here the client

#include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <string.h>
  #include <strings.h>
  #include <arpa/inet.h>
  #include <netinet/in.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include "struct.h"

  void DieWithError(char *err) {
       perror(err);
       exit(1);
  }

typedef struct {

  char transMode ;
  uint8_t linkAddr;

} ChanAccessReqParam;


typedef struct {
HeaderInt H;
ChanAccessReqParam P;

} ChanAccesReq ;
  int main(int argc, char *argv[])
  {
       int sock;                        /* Socket descriptor */
       struct sockaddr_in echoServAddr; /* Echo server address */
       struct sockaddr_in fromAddr;     /* Source address of echo */
       unsigned short echoServPort;     /* Echo server port */
       unsigned int fromSize;           /* In-out of address size for recvfrom() */
       char *servIP;                    /* IP address of server */
       int structLen;                   /* Length of string to echo */
       int respStringLen;               /* Length of received response */


       if (!argv[1]) {
              fprintf(stderr,"No server IP sepcified at arg 1\n");
              exit(1);
       }

       else if (!argv[2]) {
              fprintf(stderr,"No port Number Sepcified at arg 2\n");
              exit(2);
       }




        ChanAccesReq test { 1 ,1,0,'c',15};
        

        servIP = argv[1];           /* First arg: server IP address (dotted quad) */
        echoServPort = atoi(argv[2]);  /* Use given port, if any */

        /* Create a datagram/UDP socket */
        if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
               DieWithError("socket() failed");

        /* Construct the server address structure */
        memset(&echoServAddr, 0, sizeof(echoServAddr));    /* Zero out structure */
        echoServAddr.sin_family = AF_INET;                 /* Internet addr family */
        echoServAddr.sin_addr.s_addr = inet_addr(servIP);  /* Server IP address */
        echoServAddr.sin_port   = htons(echoServPort);     /* Server port */

        int tempint = 0;


        tempint = sendto(sock, (ChanAccesInd*)&test, 10+(sizeof(test)), 0, (struct sockaddr *)
         &echoServAddr, sizeof(echoServAddr));

        if (tempint == -1 ) {

               printf("Sent struct size: %d\n", tempint);
               DieWithError("sendto() sent a different number of bytes than expected\n");
        }

        close(sock);
        exit(0);
}

Thank you for your help

et11enne
  • 377
  • 1
  • 11
  • Unrelated note: This is not C++ code. It is C code with the occasional IOstream. – user4581301 Apr 08 '21 at 17:35
  • agree xd i update – et11enne Apr 08 '21 at 17:37
  • Recommendation: Pay a bit more attraction to the values returned by `recvfrom` and `read`. Yes, a negative number is outright failure, but zero and the positive numbers contain important information like the number of bytes read from the socket. UDP is more forgiving than TCP, you get a one-and-only-one complete message, but that message still may not be exactly what you expected. – user4581301 Apr 08 '21 at 18:01
  • @user4581301 thanks for your help , i will change that , do you have any idea on how to help me ? I know its two different types but im sure there is a way that i dont have to send twice the message since all the informations are the same each time i send – et11enne Apr 08 '21 at 18:05
  • Nothing jumped out at me on the quick read, and I'm rocking a Windows box right now so I can't compile and wash the program through a debugger. Speaking of debuggers, you should be able to use whatever debugger came with your tools to take a stroll through the server and see exactly what it does. Best case you realize the problem and fix it. Worst case you have more information to add to the question. – user4581301 Apr 08 '21 at 18:09
  • Mind you, I was wondering why the 104 in `malloc(104 + sizeof(HeaderInt))`? If you're going to read `sizeof(*H1)`, you're never going to use it. And if I think about that a bit longer, that may be your problem. – user4581301 Apr 08 '21 at 18:13
  • I thought for a moment it would be helpfull if i have a bigger size for my buffer , i tried to add a lot and tried to remove it doesn't change a thing , not sure the problem there , by the way thank you very much – et11enne Apr 08 '21 at 18:20

1 Answers1

1

you can use a flag on recvfrom like "PEEK" see the modified code here :

 recvfrom(sock, HeaderStruct, sizeof(*HeaderStruct), MSG_PEEK,(struct sockaddr *) &echoClntAddr, &cliAddrLen);

and the full code here

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "struct.h"
#include <iostream>


void GesCanSrvRecvMsg(){

     int sock;                        // Socket
     struct sockaddr_in echoServAddr; // Adresse local
     struct sockaddr_in echoClntAddr; // Adresse Client
     unsigned int cliAddrLen;         // Longeur des messages entrant
     unsigned short echoServPort;     // Port Serveur
     unsigned recvMsgSize;                 // Taille message reçu

    //Creation allocation memoire necessaire pour recevoir les structures
   HeaderInt *HeaderStruct=(HeaderInt *)malloc(sizeof(HeaderInt));
   ChanAccesReq *ChanReqStruct=(ChanAccesReq *)malloc(sizeof(ChanReqStruct));
   TransTimeReq *TransTReqStruct=(TransTimeReq *)malloc(sizeof(TransTReqStruct));
   TransCancelReq *TransCReqStruct=(TransCancelReq *)malloc(sizeof(TransCReqStruct));
    //Port definie a 13000

   echoServPort = atoi("13000");  //Premier argument port local
  //Creation socket
  if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
    Error("socket() failed");

  //Construct local address structure
  memset(&echoServAddr, 0, sizeof(echoServAddr));
  echoServAddr.sin_family = AF_INET;
  echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  echoServAddr.sin_port = htons(echoServPort);

  //Bind a ladresse
  if (bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
     Error("bind() failed");

  while(1) //Boucle infinie
  {

         cliAddrLen = sizeof(echoClntAddr);
         HeaderStruct->code=0;

         /*Attente des messages en ne regarde que l'entete Header
         Flag -> MSG_PEEK pour eviter un double envoie du client*/
         recvfrom(sock, HeaderStruct, sizeof(*HeaderStruct), MSG_PEEK,(struct sockaddr *) &echoClntAddr, &cliAddrLen);
         std::cout<<"coucou"<<'\n';
         char* subscIPAddr=inet_ntoa(echoClntAddr.sin_addr); // adresse iP Client



         if (unsigned(HeaderStruct->code)==ChanAccessReqc){ //Si code == ChanAccessReqC (1)
            recv(sock,ChanReqStruct,sizeof(*ChanReqStruct),0);
            CSMAChnAccessLstReq.push_back(*ChanReqStruct); //Placement dans liste partagée avec GesCanSlot


        }

        else if (unsigned(HeaderStruct->code==TransTimeReqc)){ //Si code == TransTimeReqc (3)
            recv(sock,TransTReqStruct,sizeof(*TransTReqStruct),0);
            CSMATimeTransmLstReq.push_back(*TransTReqStruct);

        }
        else if (unsigned(HeaderStruct->code==TransCancelReqc)){ //Si code == TransCancelReqc (3)
            recv(sock,TransCReqStruct,sizeof(*TransCReqStruct),0);
            TransacCancelLst.push_back(*TransCReqStruct);
        }

        else {
            Error("Error");
        }
   }
   close(sock);
};

int main(int argc, char *argv[])
{


        GesCanSrvRecvMsg();


        return 0;
  }

et11enne
  • 377
  • 1
  • 11
  • Just be cautioned that the next `read` or `recv` might read a different datagram. UDP datagrams are always discardable and the implementation is free to throw them away any time it feels like it, so the message you peeked at might be discarded before you get a chance to `read` or `recv` it. You might then `read` or `recv` a subsequent datagram. – David Schwartz Apr 12 '21 at 10:58
  • Thanks @DavidSchwartz for the detail but since it's the same socket and that we just do a second read on the same message we should not have any problems ? He just use a differents structure ( as i understand) to analyze his buffer ? – et11enne Apr 12 '21 at 11:19
  • 1
    Why do you think he won't have any problems? Is it because you can't think of any way it could happen? You can't create a guarantee from your inability to think of ways something can go wrong. You either have a guarantee or you don't. And there is no guarantee a UDP datagram won't be discarded. I can think of ways this can happen, and have seen it happen, but that's because I've been programming for decades. The way to avoid needing deep expertise and experience is to only rely on guarantees you actually have and understand that when they say datagrams are discardable, it's a fact. – David Schwartz Apr 12 '21 at 11:23