套接字地址显示为0.0.0.0,导致发送消息失败

时间:2018-07-16 04:08:57

标签: c sockets network-programming getaddrinfo

我一直在研究Beejs Network的示例,介绍了一些自定义项。特别是,我试图使用一个结构来存储与通信/套接字相关的必要信息。我认为我无法填充addrinfo结构并将其与sendto一起用于UDP套接字。贝娄是我的代码,可以很好地编译,但是由于下面列出的消息而失败

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

// Definitions
#define COM_MSG_SIZE 1024
#define COM_HOST_SIZE 128


struct com_socket
{
    char *type;
    int descriptor;
    struct addrinfo addr;
};


void COM_error(char *msg) {
    perror(msg);
    exit(0);
}


int main()
{

    int status;
    struct com_socket COM_client;
    char addr_str[COM_HOST_SIZE];

    // ---------------------------------------------
    // Initialize socket
    COM_client.type = "UDP";
    char *hostname = "192.168.0.110";
    char *port_num = "4000";

    printf("Creating socket...");
    if(strcmp(COM_client.type, "UDP") == 0)
    {
        COM_client.descriptor = socket(AF_INET, SOCK_DGRAM, 0);
    }

    // Error check
    if(COM_client.descriptor < 0)
    {
        COM_error("  ERROR opening socket");
    }
    printf("  Success\n");


    //------------------------------------------------------------------------------------------

    // Define hints
    struct addrinfo hints;

    hints.ai_family = AF_INET; // AF_UNSPEC "unspecified" or can use IPv6 = AF_INET6, IPv4 =  AF_INET
    hints.ai_socktype = SOCK_DGRAM; // Socket type: SOCK_STREAM or SOCK_DGRAM or 0 = auto
    hints.ai_flags = AI_CANONNAME;
    hints.ai_protocol = 0; // 0 = auto
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_addrlen = 0;
    hints.ai_next = NULL;

    // Get the linked list of address info
    struct addrinfo *host_list;
    printf("Building host address list...");
    status = getaddrinfo(hostname,port_num,&hints,&host_list);

    // returns 0 if succeeds
    if (status != 0)
    {
        COM_error("  ERROR getaddrinfo: %s\n");
    }
    printf("  Success\n");


    //------------------------------------------------------------------------------------------
    // Select address

    int count = 1;
    struct addrinfo *entry;

    // Loop through each entry in the "linked list" and pull the necessary one
    for (entry = host_list; entry != NULL; entry = entry->ai_next)
    {

        // Print the list of potential IP addresses
        if( NULL == inet_ntop( AF_INET, &((struct sockaddr_in *) entry->ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) )
        {
            COM_error("  ERROR with inet_ntop\n");
        }
        printf("  Address entry %d: %s",count,addr_str);

        // Update counter
        count = count + 1;

        // Choose which one to copy
        if(strncmp(addr_str,"192.",(size_t) 4) == 0)
        {

            //memcpy(COM_client.addr,entry, sizeof(struct addrinfo));
            COM_client.addr = *entry;
//          COM_client.addr.ai_addr = entry->ai_addr;
//          COM_client.addr.ai_addrlen = entry->ai_addrlen;
//          COM_client.addr.ai_canonname = entry->ai_canonname;
//          COM_client.addr.ai_family = entry->ai_family;
//          COM_client.addr.ai_flags = entry->ai_flags;
//          COM_client.addr.ai_protocol = entry->ai_protocol;
//          COM_client.addr.ai_socktype = entry->ai_socktype;

            if( inet_ntop( AF_INET, &((struct sockaddr_in *) COM_client.addr.ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) == NULL )
            {
                COM_error("  ERROR with arguments to inet_ntop\n");
            }

            printf("   <--------- selected* (%s) \n",addr_str);
            break;
        }
        else
        {
            printf("\n");
        }
    }

    // Clean
    freeaddrinfo(host_list);


    //-------------------------------------------------------


    char *buffer;
    char msg[COM_MSG_SIZE];
    strncpy(msg,"BEGIN",COM_MSG_SIZE);


    printf("ENTER `COM_msg_send` address length %d\n",COM_client.addr.ai_addrlen);

    buffer = calloc(COM_MSG_SIZE+1, sizeof(char));

    printf("AFTER calloc `COM_msg_send` address length %d\n",COM_client.addr.ai_addrlen);

    // Check to see if we were successful
    if (buffer == NULL)
    {
        printf("ERROR Could not allocate required memory\n");
        exit(1);
    }

    // Copy message to buffer
    strncpy(buffer,msg,COM_MSG_SIZE);
    printf("Message input: %s   Message to be sent: %s\n",msg,buffer);


    if( inet_ntop( AF_INET, &((struct sockaddr_in *) COM_client.addr.ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) == NULL )
    {
        COM_error("  ERROR with arguments to inet_ntop\n");
    }
    printf("SEND to address (%s) \n",addr_str);


    // Send the buffer to the destination address
    if(strcmp(COM_client.type, "UDP") == 0)
    {
        status = sendto(COM_client.descriptor, buffer, strlen(buffer), 0, COM_client.addr.ai_addr, COM_client.addr.ai_addrlen);

        // Error check
        if (status < 0)
        {
            COM_error("ERROR could not send message");
        }

    }

    // Free buffer memory
    free(buffer);

    //---------------------------------------------------------
    close(COM_client.descriptor);

    return 0;
}

以下是显示打印语句中的消息以及失败的输出

Creating socket...  Success
Building host address list...  Success
  Address entry 1: 192.168.0.110   <--------- selected* (192.168.0.110) 
ENTER `COM_msg_send` address length 16
AFTER calloc `COM_msg_send` address length 16
Message input: BEGIN   Message to be sent: BEGIN
L1 = 16  L2 = 16 
SEND to address (0.0.0.0) 
ERROR could not send message: Invalid argument

显示SEND to address (0.0.0.0)时,似乎存储在结构COM_client中的地址有问题。具体来说,我认为我在这部分上遇到了麻烦

            //memcpy(COM_client.addr,entry, sizeof(struct addrinfo));
            COM_client.addr = *entry;
//          COM_client.addr.ai_addr = entry->ai_addr;
//          COM_client.addr.ai_addrlen = entry->ai_addrlen;
//          COM_client.addr.ai_canonname = entry->ai_canonname;
//          COM_client.addr.ai_family = entry->ai_family;
//          COM_client.addr.ai_flags = entry->ai_flags;
//          COM_client.addr.ai_protocol = entry->ai_protocol;
//          COM_client.addr.ai_socktype = entry->ai_socktype;    

如您所见,我已经尝试了各种方法,但都失败了。我想继续使用COM_client结构方法,因为我的意图是使代码更具模块化,在其中我可以传递包含所有必要通信信息的结构。

1 个答案:

答案 0 :(得分:0)

此行

COM_client.addr = *entry;

“尝试”以复制struct addrinfo,实际上确实如此,但是它包含指针,而“ only”则复制指针的值。这些指针指向的内存已由getaddrinfo()分配,因此将通过对freeaddrinfo()的调用而被释放,之后将指针留在复制悬挂中。

要解决此问题,您需要对struct addrinfo进行“深度复制”。

例如,可以这样做:

/* Does a deep copy to where pdst point from where pscr points to. 
   Returns 0 on success and -1 on error. Sets errno. */   

int addrinfo_copy(struct addrinfo * pdst, struct addrinfo * psrc)
{
  int result = 0; /* Be optimistic. */

  assert(pdst);
  assert(psrc);

  *pdst = *pscr; /* Copy all. Note that the pointer elements copied
                   need to be recreated. See below ... */
  do
  {
    pdst->ai_addr = malloc(psrc->ai_addrlen);
    if (!pdst->ai_addr)
    {
      result = -1;
      break;
    }

    memcpy(pdst->ai_addr, psrc->ai_addr, psrc->ai_addrlen);

    pdst->ai_canonname = strdup(psrc->ai_canonname); /* Assumes POSIX. */
    if (!pdst->ai_canonname)
    {
      result = -1;
      break;
    }
  } while (0);

  return result;       
}

要摆脱此类副本,您需要这样的东西:

/* Deallocates and sets to a 0/NULL what had been created by 
   addrinfo_copy(). */

void addrinfo_free(struct addrinfo * p)
{
  assert(p);

  free(p->ai_addr);
  free(p->canonname);

  memset(p, 0, sizeof *p); /* In fact not necessary. */
}

像这样使用它:

  struct addrinfo * entry, * entry_copy;

  /* Set entry to something returned by getaddrinfo (). */
  ...

  if (-1 = addrinfo_copy(entry_copy, entry))
  {
    perror("addrinfo_copy() failed"):
    exit(EXIT_FAILURE);
  }

  /* Deallocate all results returned by getaddrinfo(). */
  freeaddrinfo(...);

  /* Still use entry_copy here. */
  ...

  /* Clean up. */
  addrinfo_free(entry_copy);

作为最后的提示:

如果在执行C时发现内存内容突然/意外发生了明显的变化,则这可能是由于通过写入和/或读取“错误的”内存而搞乱了内存管理。这几次发生在内存变化明显和/或代码(看似)完全与您观察到内存变化的位置无关之前很久。