【问题标题】:Raw sockets: sendto() and recvfrom() not working原始套接字:sendto() 和 recvfrom() 不起作用
【发布时间】:2016-02-05 11:53:03
【问题描述】:

我正在尝试使用 RAW 套接字编写客户端/服务器应用程序。

有多个问题:

  1. 客户端使用sendto()方法向服务器发送消息时,sendto()返回错误invalid argument em> 方法。 为什么会出现此错误消息?ERROR 1 部分下标记了相应的代码。 sendto()的代码在本帖内注释。

  2. 由于我已经注释了发送消息部分,客户端应该等待消息; recvfrom() 是一个阻塞系统调用。相反,recvfrom() 总是返回消息 E这条消息是从哪里来的?。对应的代码标记为ERROR 2

  3. 如果我将 socket() 中的 protocol (3rd) 参数更改为 0IPPROTO_RAW我收到Protocol not supported error。 为什么会出现这些错误?

操作系统是Ubuntu

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    #include <sys/socket.h> // For the socket () etc. functions.
    #include <netinet/in.h> // For IPv4 data struct..
    #include <string.h>     // For memset.
    #include <arpa/inet.h>  // For inet_pton ().

    #define BUF_SIZE 30

    void main ()

    {
        int rst; // Return status of functions.
    /**************** Create a socket. *******************************/
    int sfd; // Socket file descriptor.
    sfd = socket (AF_INET, SOCK_RAW, IPPROTO_UDP); /* 
            * AF_INET --> IPv4, SOCK_RAW for Raw socket,  
            * 0 --> for any protocol. */
    if (sfd == -1) 
    {
        perror ("Client: socket error");
        exit (1);
    }


    /*********** Server's address ***********************************/
    struct sockaddr_in srv_addr;
    socklen_t addrlen = sizeof (struct sockaddr_in); 

    // Initializing the server's address to zero.
    memset (&srv_addr, 0, addrlen); 

    srv_addr.sin_family = AF_INET;    // Address is in IPv4 format.
    // srv_addr.sin_port   = htons (0);  // Port number of the server. 


    rst = inet_pton (AF_INET, "127.0.0.1", &srv_addr.sin_addr); /* Note
            * that third field should point to an in_addr (in6_addr). */
    if (rst <= 0)
    {
        perror ("Client Presentation to network address conversion.\n");
        exit (1);
    }        



    /****************** ERROR 1 ************************************
    ******************* Sending message to the server. *************/
    const int flags = 0;
    const char *msg = "Hello";
    /* rst = sendto (sfd, msg, strlen(msg)+1, flags, 
                    (struct sockaddr *) &srv_addr, 
                    sizeof (struct sockaddr_in));
    if (rst < 0)
    {
        perror ("Client: Sendto function call failed");
        exit (1);
    }
    else 
        printf ("Client: Sent data size = %d\n", rst);

    */


    /******************* ERROR 2 ***********************************
     ******************* Receiving message from server. ************/
    // Initializing the server's address to zero.
    memset (&srv_addr, 0, addrlen); 

    char buf[BUF_SIZE] = {'\0'};

    rst = recvfrom (sfd, buf, BUF_SIZE, flags, 
                    (struct sockaddr *) &srv_addr, 
                    &addrlen);
    if (rst < 0)
    {
        perror ("Client: couldn't receive");
        exit (1);
    }
    printf ("Message from server = |%s|\n", buf);

    /* Address of the server. */
    const char *buf2 = inet_ntop (AF_INET, 
                       (struct sockaddr *) &srv_addr, buf, BUF_SIZE);
    if (buf2 == NULL)
    {
        perror ("Client: Conversion of sender's address to presentation failed");
        exit (1);
    }

    printf ("Servers address,  = %s\n", buf2);
    close (sfd);

}

【问题讨论】:

  • 您的“ERROR 1”对我来说很好用。如果您需要进一步的调试帮助,您可以使用 strace 工具运行程序并发布输出。请记住,原始套接字接收您的机器接收的每个 IP 数据包。也许您正在接收 DNS 或 SSH 数据包等。您打印出的“E”是 IP 标头的 1.字节。 IPv4 数据包中的 1 字节是 0x45,在 ascii 中被解释为 E。因此,请记住,由于您使用 RAW 套接字,因此您还会收到 IP 标头。如果您使用的是 IPPROTO_RAW,则需要创建一个您发送的有效 IP 标头。

标签: c linux sockets network-programming raw-sockets


【解决方案1】:

SOCK_RAW 不适用于 UDP。 SOCK_DGRAM 是正确的。教程见:

a tutorial from Rutgers

【讨论】:

  • 我需要使用 RAW 套接字IPPROTO_UDP 需要更改为 IPPROTO_RAW。但正如第 3 点所述,它会产生 Invalid Protocol 错误。
【解决方案2】:

编辑:忽略了 srv_addr 的初始化...抱歉。

使用 AF_INET + SOCK_RAW 套接字,您可以发送任何内容 - 有效负载只是添加到 IP 层之上。 IPPROTO_UDP 只是告诉内核下一层将是什么(您的有效负载被添加到的层)以及 IP 标头的协议字段必须设置为哪个值。所以要保持保存(如果你要发送原始数据)将协议设置为不常用的东西)。

您需要创建原始套接字的权限。这通常意味着:以 root 身份启动,创建套接字,然后删除权限。

q2:这是您发送给自己的信息(强烈表明您的代码以某种方式有效)。 'E' 只是 IP 标头中的第一个字节 (0x45) - 版本 4 和标头长度 5。只需转储整个缓冲区...,例如。

printf ("Message from server = |");
    for (i = 0; i < rst; i++)
        printf("%c", isprint(buf[i]) ? buf[i] : '?') ;
printf ("|\n");

第三季度:

0 表示:猜测通常使用什么(例如 INET + DGRAM -> TCP)。当您指定 raw 时,内核无法为下一层选择通用协议。

IPPROTO_RAW 应该可以工作(见@nos 的评论)

【讨论】:

  • socket(AF_INET, SOCK_RAW, IPPROTO_RAW) 应该可以工作,它只是将您向下移动一层,如 here 所记录的那样
猜你喜欢
  • 2012-06-15
  • 1970-01-01
  • 1970-01-01
  • 2015-01-09
  • 1970-01-01
  • 1970-01-01
  • 2021-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多