原始套接字:sendto()和recvfrom()无法正常工作

时间:2016-02-05 11:53:03

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

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

存在多个问题:

  1. 当客户端使用 sendto()方法向服务器发送消息时, sendto()返回错误无效参数 方法。 为何出现此错误消息?。相应的代码标记在 ERROR 1 部分下。 sendto()的代码在这篇文章中发表了评论。

  2. 由于我评论了发送消息部分,客户端应该等待消息; recvfrom()是阻塞系统调用。相反, recvfrom()会始终返回消息 E 此消息从何处到达?。相应的代码标记为 ERROR 2

  3. 如果我将套接字()中的 protocol (3rd)参数更改为 0 IPPROTO_RAW 我得到协议不支持错误。 为什么会出现这些错误?

  4. 操作系统 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);
    
    }
    

2 个答案:

答案 0 :(得分:0)

SOCK_RAW不适用于UDP。 SOCK_DGRAM是正确的。有关教程,请参阅:

a tutorial from Rutgers

答案 1 :(得分:0)

编辑:忽略了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");

Q 3:

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

IPPROTO_RAW应该有效(见@nos的评论)