网络应用程序在Mac OS X上运行,无法在Linux上连接

时间:2014-09-11 20:34:27

标签: c linux macos posix

我在我的Mac上编写了一个网络应用程序,它应该在Linux上运行,因为我认为“嘿,POSIX是POSIX,它应该可以工作”,现在我收到连接到服务器的错误。

TCP_sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (TCP_sock < 0) {
  syserr("socket");
}
TCP_open = 1;

memset(&addr_hints, 0, sizeof(struct addrinfo));
addr_hints.ai_flags = 0;
addr_hints.ai_family = AF_UNSPEC;
addr_hints.ai_socktype = SOCK_STREAM;
addr_hints.ai_protocol = IPPROTO_TCP;
addr_hints.ai_addrlen = 0;
addr_hints.ai_addr = NULL;
addr_hints.ai_canonname = NULL;
addr_hints.ai_next = NULL;

rc = getaddrinfo(SERVER, PORT, &addr_hints, &addr_result);
if (rc != 0) {
  cleanup();
  fprintf(stderr, "rc=%d\n", rc);
  syserr("getaddrinfo: %s\n", gai_strerror(rc));
  continue;
}

if ((rc = connect(TCP_sock, addr_result->ai_addr, addr_result->ai_addrlen)) != 0) {
  syserr("connect: %s\n", gai_strerror(rc));
  cleanup();
  continue;
}

我收到了一个错误:

ERROR: connect: Bad value for ai_flags
(22; Invalid argument)

只是为了说清楚:

SERVER = "localhost"
PORT = "1234"

当然,在该PORT上有一个运行在localhost上的服务器。什么可能是错的?

2 个答案:

答案 0 :(得分:0)

根据this Wikipedia article on getaddrinfo,实施因系统而异。另外,OS X is not fully POSIX compliable(第一段)。它被认证为符合SUSv3,但这并不意味着它完全符合POSIX标准,并且它不一定实现与其他POSIX-y系统相同的通用功能。

因此,虽然'POSIX是POSIX',但OS X不是POSIX。您需要了解所调用方法的实现细节,并弄清楚它们之间的区别。

答案 1 :(得分:0)

这里有几个问题。

  1. gai_strerror只将从getaddrinfo返回的错误代码转换为字符串,并且您正在尝试转换connect返回的错误代码。失败时,connect应返回-1,您可以检查errno并使用strerror将其转换为字符串。所以它很可能与ai_flags无关。
  2. 与其他评论一样,您正在创建一个IPv6套接字,然后告诉getaddrinfo您可以使用IPv4或IPV6地址。将AF_UNSPEC更改为AF_INET6,它应该有所帮助。所以应该更好地工作的代码(考虑到#1和#2)

    TCP_sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
    if (TCP_sock < 0) {
      syserr("socket");
    }
    TCP_open = 1;
    
    memset(&addr_hints, 0, sizeof(struct addrinfo));
    addr_hints.ai_flags = 0;
    addr_hints.ai_family = AF_INET6;
    addr_hints.ai_socktype = SOCK_STREAM;
    addr_hints.ai_protocol = IPPROTO_TCP;
    addr_hints.ai_addrlen = 0;
    addr_hints.ai_addr = NULL;
    addr_hints.ai_canonname = NULL;
    addr_hints.ai_next = NULL;
    
    rc = getaddrinfo(SERVER, PORT, &addr_hints, &addr_result);
    if (rc != 0) {
      cleanup();
      fprintf(stderr, "rc=%d\n", rc);
      syserr("getaddrinfo: %s\n", gai_strerror(rc));
      continue;
    }
    
    if ((rc = connect(TCP_sock, addr_result->ai_addr, addr_result->ai_addrlen)) != 0) {
      syserr("connect: %s\n", strerror(errno));
      cleanup();
      continue;
    }
    
  3. 使用getaddrinfo的常用方法是先调用它,然后使用addrinfo结构中的信息创建套接字并连接它。 Linux手册页有一个很好的例子,这里有相关的摘录。

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
    hints.ai_flags = AI_PASSIVE;    /* For wildcard IP address */
    hints.ai_protocol = 0;          /* Any protocol */
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;
    
    s = getaddrinfo(NULL, argv[1], &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }
    
    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully bind(2).
       If socket(2) (or bind(2)) fails, we (close the socket
       and) try the next address. */
    
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype,
                rp->ai_protocol);
        if (sfd == -1)
            continue;
    
       if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
            break;                  /* Success */
    
       close(sfd);
    }
    
    if (rp == NULL) {               /* No address succeeded */
        fprintf(stderr, "Could not bind\n");
        exit(EXIT_FAILURE);
    }
    
    freeaddrinfo(result);           /* No longer needed */
    

    这里主要的是你使用addrinfo结构中的ai_family,ai_socktype和ai_protocol来创建适合在结构中与sockaddr一起使用的套接字。

  4. 至于为什么它适用于Mac OS X而不是Linux,我不确定,但我的预感是它与V4映射的IPv6套接字功能有关,它允许内核使用你的IPv6套接字与之交谈IPv4主机。