【问题标题】:Sockets sendto() returning EINVAL套接字 sendto() 返回 EINVAL
【发布时间】:2014-05-23 13:23:07
【问题描述】:

我正在尝试在 C 中发送一个 UDP 数据包。我有以下sendto()

char* msg = "Hello";

//ret is the return value of getaddrinfo, the address is AF_INET (IPv4)
//and the sock_type is SOCK_DGRAM (UDP)
struct sockaddr_in *ip = (struct sockaddr_in *)ret->ai_addr;

if ((sendto(sock, msg, strlen(msg), 0, (struct sockaddr *)ip, 
                sizeof(struct sockaddr *))) != -1) {
    printf("msg sent successfully");
} else {
    printf("Error sending msg: %s\n", strerror(errno));
}

但是,它返回一个错误,说明存在无效参数。查看联机帮助页,我真的无法判断哪一个是无效参数。有什么想法吗?

编辑:这是我所有的代码

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

int main(int argc, const char *argv[])
{
    /*
     * Help the technically challenged among us who have no idea
     * what on God's green Earth they are doing with this thing.
    */
    if (argc != 2) {
        printf("usage: routetracer <ip address or hostname>\n");
        return -1;
    }

    /*
     * hints- parameters for return value of getaddrinfo
     * ret- return value of getaddrinfo
     */
    struct addrinfo hints, *ret;
    int status;
    char ipv4[INET_ADDRSTRLEN];
    int ttl = 0;
    char* msg = "Hello";
    int last_hop = 0;

    //define what we want from getaddrinfo
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET; //IPv4
    hints.ai_socktype = SOCK_DGRAM; //UDP packets

    //call getaddrinfo to fill ret, w/ error chk
    if ((status = getaddrinfo(argv[1], NULL, &hints, &ret)) != 0) {
        printf("getaddrinfo: %s\n", gai_strerror(status));
        return -1;
    }

    //extract IPv4 address from ret
    struct sockaddr_in* ip = (struct sockaddr_in *)ret->ai_addr;

    //convert address from pure numbers to something easier to read
    inet_ntop(ret->ai_family, &(ip->sin_addr), ipv4, INET_ADDRSTRLEN);   

    //kindly inform the user of which hostname they are connecting to
    printf("Route for: %s\n", ipv4);

    //create a socket
    int sock = socket(ret->ai_family, ret->ai_socktype, ret->ai_protocol);

    ttl = 1;
    if ((setsockopt(sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) != -1) {
        printf("TTL set successfully\n");
    } else {
        printf("Error setting TTL: %s\n", strerror(errno));
    }

    if ((sendto(sock, msg, strlen(msg), 0, ret->ai_addr,
                    ret->ai_addrlen)) != -1) {
        printf("msg sent successfully");
    } else {
      printf("Error sending msg: %s\n", strerror(errno));
    }
    return 0;
}

运行程序给出以下输出:

$ ./routetracer www.google.com
Route for: 173.194.46.82
TTL set successfully
Error sending msg: Invalid argument

【问题讨论】:

    标签: c sockets udp ipv4 sendto


    【解决方案1】:

    正如 Barmar 指出的那样,一个 EINVAL 的原因是不正确的:

      sizeof(struct sockaddr *)
    

    给出指针的大小。见Socket programming: sendto always fails with errno 22 (EINVAL)

    第二个原因似乎是sin_portgetaddrinfo 返回为0。将其更改为 80 say 将清除 EINVAL,如下所示:

      ((struct sockaddr_in *)ret->ai_addr)->sin_port = htons(80); // test
    

    这里的 80 端口不是 HTTP,而是(对于 UDP)是 Google 的实验性 QUIC Chromium。

    Wikipedia http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers 声明端口 0 用于 UDP保留,而 TCP 被非正式地保留为“用于指定系统分配(动态)端口的编程技术”。

    顺便说一句(并参考原始问题),您可能不需要为变量ip 操心。您将ret-&gt;ai_addr 转换为struct sockaddr_in *,然后再次返回 到其原始类型。

    而且,正如 Remy Lebeau 所指出的,最好使用 getaddrinfoservice 参数。因此,将所有这些放在一起,您的代码可能看起来更像:

      memset(&hints, 0, sizeof(hints));
      hints.ai_family = AF_INET; //IPv4
      hints.ai_socktype = SOCK_DGRAM; //UDP packets
      if ((status = getaddrinfo(argv[1], "80", &hints, &ret)) != 0) {
          printf("getaddrinfo: %s\n", gai_strerror(status));
          return -1;
      }
      assert(ret->ai_family   == AF_INET);     // guaranteed
      assert(ret->ai_socktype == SOCK_DGRAM);  // guaranteed
      assert(((struct sockaddr_in *)ret->ai_addr)->sin_port == htons(80)); // guaranteed
      // ...
      if ((sendto(sock, msg, strlen(msg), 0, ret->ai_addr, ret->ai_addrlen)) != -1) {
      // ...
    

    【讨论】:

    • 我有 ip 因为我需要一个struct sockaddr_in* 来调用inet_ntop()。但是,我使用了您的建议,它仍然给我同样的错误。我将使用迄今为止的所有代码(包括您的建议)编辑我的原始帖子。
    • @Hugo:我已经离开了。感谢您提供完整的代码。这个问题很容易被复制。希望这个新建议有所帮助。
    • 分配sin_port值时需要使用htons()
    • @RemyLebeau:哎呀!谢谢。
    • 你可以在getaddrinfo()service参数中传递一个可选的端口号/服务,然后sin_port会为你初始化。
    【解决方案2】:

    试试:

    if ((sendto(sock, msg, strlen(msg), 0, (struct sockaddr *)ip, 
                    sizeof(struct sockaddr_in))) != -1) {
    

    你给它的是指针的大小,而不是结构的大小。并且它必须是特定的结构类型,而不是泛型类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-29
      • 1970-01-01
      • 1970-01-01
      • 2012-08-02
      • 2014-07-02
      • 2015-07-27
      • 2013-01-10
      相关资源
      最近更新 更多