Question

I am stuck with my UDP talker app. The goal for the moment is to initialize the server, register a client and then proceed to send something to that client. I've worked my way through Beej's network guide and coded the following library implementation:

This inizializes the server

int init_udp_server(const char *port_string){

  /** Check the input data **/
  if(port_string == NULL)
    port_string = DEFAULT_PORT;

  /** Get the information for the server **/
  memset(&addrinfo_hints, 0, sizeof addrinfo_hints);
  /* Use either protocol (v4, v6) */
  addrinfo_hints.ai_family = AF_UNSPEC;
  /* Use UDP socket type */
  addrinfo_hints.ai_socktype = SOCK_DGRAM;
  /* Use system IP */
  addrinfo_hints.ai_flags = AI_PASSIVE;

  if( (ret = getaddrinfo(NULL, port_string, &addrinfo_hints, &addrinfo_server)) 
      != 0 ){
    printf("Server:getaddrinfo: %s\n", gai_strerror(ret));  
    return -1;
  }

  /** Loop through the list returned by getaddrinfo and get socket **/
  for( addrinfo_queue = addrinfo_server; addrinfo_queue != NULL; 
      addrinfo_queue = addrinfo_queue->ai_next){
    if((sockfd = socket(addrinfo_queue->ai_family,
            addrinfo_queue->ai_socktype, addrinfo_queue->ai_protocol)) == -1){
      error("Server: get socket failed");
      continue;
    }
    if(bind(sockfd, addrinfo_queue->ai_addr, addrinfo_queue->ai_addrlen)
        == -1){
      close(sockfd);
      error("Server: Bind to socket error");
      continue;
    }
    break;

  }
  /* If we got to addrinfo_queue == NULL, we did not get a valid socket */
  if(addrinfo_queue == NULL){
    error("Server: Could not bind a socket");
    return -1;
  }
  /* We do not need the addrinfo_server anymore */
  freeaddrinfo(addrinfo_server);
  return 0;
}

This registers the client

int udp_server_setup_client(const char *client_addr, const char *port_string, int     client_nr){


  /** Check the input data **/
  if(port_string == NULL)
    port_string = DEFAULT_PORT;
  if(client_addr == NULL){
    error("No valid client list");
    return -1;
  }
  if(client_nr < 0 || client_nr > 7){
    error("No valid client Nr.");
    return -1;
  } 

  memset(&addrinfo_hints, 0, sizeof addrinfo_hints);
  /* Use either protocol (v4, v6) */
  addrinfo_hints.ai_family = AF_UNSPEC;
  /* Use UDP socket type */
  addrinfo_hints.ai_socktype = SOCK_DGRAM;

  /* Get the information for the client */
  if( (ret = getaddrinfo( client_addr, port_string, &addrinfo_hints,
          &current)) != 0 ){
    printf("Client:getaddrinfo: %s\n", gai_strerror(ret));  
    return -1;
  }
  else{
    /* We read out the IP, kind of a nice check to see wheter all went fine */
    char ip4[INET_ADDRSTRLEN];
    struct sockaddr_in *sa = (struct sockaddr_in*) current->ai_addr;
    inet_ntop(AF_INET, &(sa->sin_addr),ip4, INET_ADDRSTRLEN);
    printf("Clients address: %s\n",ip4);
    addrinfo_clients[client_nr] = current;
  }
  return 0;
}

And finally this is for writing

int udp_server_write(const char *buffer, int buffer_size, int client_nr){
  /* Sanity check of the input */
  if(client_nr > (MAX_NR_CLIENTS - 1) || client_nr < 0){
    error("Not a valid client");
    return -1;
  }
  if(buffer == NULL){
    error("Not a valid buffer address");
    return -1;
  }
  /* Just so we type less */
  current = addrinfo_clients[client_nr];

  socklen = sizeof current->ai_addr; 
  if((ret = sendto(sockfd, (void*)buffer, buffer_size, 0,
        (sockaddr*)current->ai_addr, socklen)) == -1){
    printf("Failed to send message to client %i\n", client_nr);
    printf("Error Code: %s\n",gai_strerror(ret)); 
    return -1;    
    }
  else if(ret < buffer_size){
    printf("Wrote only %i of %i bytes\n", ret, buffer_size);
    return -1;
  }
  return ret;
}

I call the functions like this

init_udp_server("3334"); 

udp_server_setup_client("192.168.1.5", "3334", 0);

udp_server_write(send_buf, 256, 0);

As soon as sendto() is called I get an error: Failed to send message to client 0 Error Code: Bad value for ai_flags I checked it with gdb and found that the addrinfo struct is filled correctly, and the address of the client is valid. Any one an idea where to look? I am running out of ideas...

thanks, wenzlern

Was it helpful?

Solution

When calling sendto(), the last parameter is being set to sizeof current->ai_addr, which is wrong. current->ai_addr is defined as a sockaddr* pointer, so sizeof current->ai_addr will always return 4 on a 32-bit system and 8 on a 64-bit system. It just happens that IPv4 addresses are 4 bytes in size, so sizeof current->ai_addr will only work for IPv4 addresses on 32-bit systems, but will always fail for IPv6 addresses on 32-bit systems and all addresses on 64-bit systems. You need to use current->ai_addrlen instead of sizeof.

Also, passing -1 to gai_strerror() is not valid. It expects you to pass in a real error code, such as the return value of getaddrinfo() and getnameinfo(). sendto() does not return an actual error code. When it fails, you have to use WSAGetLastError() on Windows or errno on other systems to get the actual error code.

Try this:

if ((ret = sendto(sockfd, (char*)buffer, buffer_size, 0, (sockaddr*)current->ai_addr, current->ai_addrlen)) == -1)
{
    #ifdef _WIN32
    ret = WSAGetLastError();
    #else
    ret = errno;
    #endif

    printf("Failed to send message to client %i\n", client_nr);
    printf("Error Code: (%d) %s\n", ret, gai_strerror(ret)); 
    return -1;    
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top