套接字sendto()返回EINVAL [英] Sockets sendto() returning EINVAL

查看:229
本文介绍了套接字sendto()返回EINVAL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试用C发送UDP数据包.我有以下sendto():

I'm trying to send a UDP packet in C. I have the following 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));
}

但是,它返回一个错误,指出参数无效.看着联机帮助页,我无法真正分辨出哪个是无效参数.有什么想法吗?

However, it's returning an error saying there's an invalid argument. Looking at the manpage I can't really tell which one is the invalid argument. Any ideas?

这是我所有的代码

#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;
}

运行程序将给出以下输出:

Running the program gives the following output:

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

推荐答案

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

As Barmar points out, one reason for the EINVAL is the incorrect:

  sizeof(struct sockaddr *)

给出指针的大小.请参见套接字编程:sendto始终会因errno 22(EINVAL)而失败.

which gives the size of a pointer. See Socket programming: sendto always fails with errno 22 (EINVAL).

第二个原因可能是sin_portgetaddrinfo返回的原因是0.将其更改为80会清除EINVAL,如:

The second reason seems to be sin_port, which getaddrinfo returns as 0. Changing it to 80 say clears up the EINVAL, as in:

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

此处的端口80不是HTTP,而是(对于UDP)(是UDP)是Google的实验QUIC Chromium.

Here port 80 here is not HTTP, but instead (for UDP) is Google's experimental QUIC Chromium.

Wikipedia http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers 指出该端口对于UDP 保留的 ,0表示,对于TCP,作为用于指定系统分配的(动态)端口的编程技术",被非正式地保留.

Wikipedia http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers states that Port 0 is for UDP reserved, and for TCP is unofficially reserved as a "programming technique for specifying system-allocated (dynamic) ports".

顺便说一句(并参考原始问题),您可能不需要麻烦变量ip.您要将ret->ai_addr强制转换为struct sockaddr_in *,然后再次返回为其原始类型.

And as an aside (and referring to the original question), you may not need bother with the variable ip. You are casting ret->ai_addr to struct sockaddr_in *, and then back again to its original type.

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

And, as Remy Lebeau points out, it is better to use the service parameter of getaddrinfo. So putting this all together, your code could look more like:

  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) {
  // ...

这篇关于套接字sendto()返回EINVAL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆