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

Socket address showing 0.0.0.0, causing failure to send message

我一直在使用 Beejs Network 示例,介绍了一些自定义项。特别是,我试图使用单一结构来存储与 communications/sockets 相关的必要信息。我想我在填充 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 结构方法,因为我的目的是使代码更加模块化,我可以在其中传递包含所有必要通信信息的结构。

这一行

COM_client.addr = *entry;

"tries" 复制一个 struct addrinfo,它实际上是这样做的,但因为它包含指针,而 "only" 复制指针的值。这些指针指向的内存已由 getaddrinfo() 分配,因此将通过调用 freeaddrinfo() 解除分配,之后将指针留在副本内。

要解决此问题,您需要执行 struct addrinfo 的 "deep copy"。

例如可以这样做:

/* 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 时,您观察到内存内容的模糊 sudden/unexpected 变化,这最常见的原因是通过写入 and/or 读取 "wrong" 内存来搞乱内存管理。这有时发生在内存中的这些变化变得明显之前 and/or 在代码中(表面上)与您在内存中观察到此类变化的位置完全无关。