• 困扰我三天的问题


    困扰我三天的问题

           IP_RECVERR (since Linux 2.2)
                  Enable extended reliable error message passing.  When enabled on a  datagram  socket,  all
                  generated  errors  will  be queued in a per-socket error queue.  When the user receives an
                  error from a socket operation, the errors can be received by calling recvmsg(2)  with  the
                  MSG_ERRQUEUE  flag  set.   The  sock_extended_err  structure  describing the error will be
                  passed in an ancillary message with the type IP_RECVERR and the level IPPROTO_IP.  This is
                  useful  for  reliable error handling on unconnected sockets.  The received data portion of
                  the error queue contains the error packet.
    
                  The IP_RECVERR control message contains a sock_extended_err structure:
    
                      #define SO_EE_ORIGIN_NONE    0
                      #define SO_EE_ORIGIN_LOCAL   1
                      #define SO_EE_ORIGIN_ICMP    2
                      #define SO_EE_ORIGIN_ICMP6   3
    
                      struct sock_extended_err {
                          uint32_t ee_errno;   /* error number */
                          uint8_t  ee_origin;  /* where the error originated */
                          uint8_t  ee_type;    /* type */
                          uint8_t  ee_code;    /* code */
                          uint8_t  ee_pad;
                          uint32_t ee_info;    /* additional information */
                          uint32_t ee_data;    /* other data */
                          /* More data may follow */
                      };
    
                      struct sockaddr *SO_EE_OFFENDER(struct sock_extended_err *);
    
                  ee_errno contains the errno number of the queued error.  ee_origin is the origin  code  of
                  where   the  error  originated.   The  other  fields  are  protocol-specific.   The  macro
                  SO_EE_OFFENDER returns a pointer to the address of the  network  object  where  the  error
                  originated  from  given a pointer to the ancillary message.  If this address is not known,
                  the sa_family member of the sockaddr contains AF_UNSPEC and the other fields of the  sock‐
                  addr are undefined.
    
                  IP  uses the sock_extended_err structure as follows: ee_origin is set to SO_EE_ORIGIN_ICMP
                  for errors received as an ICMP packet, or SO_EE_ORIGIN_LOCAL for locally generated errors.
                  Unknown  values  should  be  ignored.   ee_type and ee_code are set from the type and code
                  fields of the ICMP header.  ee_info contains the discovered MTU for EMSGSIZE errors.   The
                  message  also contains the sockaddr_in of the node caused the error, which can be accessed
                  with the SO_EE_OFFENDER macro.  The sin_family field  of  the  SO_EE_OFFENDER  address  is
                  AF_UNSPEC when the source was unknown.  When the error originated from the network, all IP
                  options (IP_OPTIONS, IP_TTL, etc.) enabled on the socket and contained in the error packet
                  are  passed  as control messages.  The payload of the packet causing the error is returned
                  as normal payload.  Note that TCP has no error queue; MSG_ERRQUEUE  is  not  permitted  on
                  SOCK_STREAM  sockets.   IP_RECVERR is valid for TCP, but all errors are returned by socket
                  function return or SO_ERROR only.
    
                  For raw sockets, IP_RECVERR enables passing of all received ICMP errors  to  the  applica‐
                  tion, otherwise errors are only reported on connected sockets
    
                  It sets or retrieves an integer boolean flag.  IP_RECVERR defaults to off.
    

    顺便复习一发recvmsg

       recvmsg()
           The recvmsg() call uses a msghdr structure to minimize the number of directly supplied arguments.
           This structure is defined as follows in <sys/socket.h>:
    
               struct iovec {                    /* Scatter/gather array items */
                   void  *iov_base;              /* Starting address */
                   size_t iov_len;               /* Number of bytes to transfer */
               };
    
               struct msghdr {
                   void         *msg_name;       /* optional address */
                   socklen_t     msg_namelen;    /* size of address */
                   struct iovec *msg_iov;        /* scatter/gather array */
                   size_t        msg_iovlen;     /* # elements in msg_iov */
                   void         *msg_control;    /* ancillary data, see below */
                   size_t        msg_controllen; /* ancillary data buffer len */
                   int           msg_flags;      /* flags on received message */
               };
    
           The  msg_name field points to a caller-allocated buffer that is used to return the source address
           if the socket is unconnected.  The caller should set msg_namelen  to  the  size  of  this  buffer
           before  this call; upon return from a successful call, msg_namelen will contain the length of the
           returned address.  If the application does not need to know the source address, msg_name  can  be
           specified as NULL.
    
           The fields msg_iov and msg_iovlen describe scatter-gather locations, as discussed in readv(2).
    
           The  field  msg_control,  which  has length msg_controllen, points to a buffer for other protocol
           control-related messages or miscellaneous ancillary data.  When  recvmsg()  is  called,  msg_con‐
           trollen should contain the length of the available buffer in msg_control; upon return from a suc‐
           cessful call it will contain the length of the control message sequence.
    
           The messages are of the form:
    
               struct cmsghdr {
                   size_t cmsg_len;    /* Data byte count, including header
                                          (type is socklen_t in POSIX) */
                   int    cmsg_level;  /* Originating protocol */
                   int    cmsg_type;   /* Protocol-specific type */
               /* followed by
                   unsigned char cmsg_data[]; */
               };
    
           Ancillary data should be accessed only by the macros defined in cmsg(3).
    
           As an example, Linux uses this ancillary data mechanism to pass extended errors, IP  options,  or
           file descriptors over UNIX domain sockets.
    
           The msg_flags field in the msghdr is set on return of recvmsg().  It can contain several flags:
    

    2.1.1.5 Blocking Read

    Reading from the error queue is always a non-blocking operation. To block waiting on a timestamp, use poll or select. poll() will return POLLERR in pollfd.revents if any >data is ready on the error queue. There is no need to pass this flag in pollfd.events. This flag is ignored on request. See also "man 2 poll".

    我们提到在对端主机上没有创建指定的UDP套接字时,我们向其发送一个UDP包,会得到一个目的端口不可达的ICMP出错报文。但内核在处理完该报文后,给应用程序仅仅返回一个ECONNREFUSED错误号,所以应用程序能知道的全部信息就是连接被拒绝,至于为什么被拒绝,没有办法知道。我们可以通过套接字选项的设置,让内核返回更为详细的出错信息,以利于调试程序,发现问题。下面是通过套接字选项传递扩展出错信息的一个示例程序。

    /*************************************************************************
        > File Name: extendederrorDemo.cc
        > Author: ngkimbing
        > Mail: ngkimbing@foxmail.com
        > Created Time: Sat 14 Mar 2020 01:51:12 PM CST
     ************************************************************************/
    #include "unp.h"
    #include <errno.h>
    #include <linux/errqueue.h>
    #include <linux/types.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/ioctl.h>
    #include <sys/socket.h>
    
    #include <arpa/inet.h>
    #include <unistd.h>
    
    int ip_control_msg(struct cmsghdr *msg) {
        int ret = 0;
        switch (msg->cmsg_type) {
            case IP_RECVERR: {
                struct sock_extended_err *exterr;
                exterr = (struct sock_extended_err *)(CMSG_DATA(msg));
                printf("ee_errno: %u
    ", exterr->ee_errno);
                printf("ee_origin: %u
    ", exterr->ee_origin);
                printf("ee_type: %u
    ", exterr->ee_type);
                printf("ee_code: %u
    ", exterr->ee_code);
                printf("ee_pad: %u
    ", exterr->ee_pad);
                printf("ee_info: %u
    ", exterr->ee_info);
                printf("ee_data: %u
    ", exterr->ee_data);
            }
                ret = -1;
                break;
            default: break;
        }
        return ret;
    }
    
    int control_msg(struct msghdr *msg) {
        int             ret         = 0;
        struct cmsghdr *control_msg = CMSG_FIRSTHDR(msg);
        while (control_msg != NULL) {
            switch (control_msg->cmsg_level) {
                case SOL_IP: ret = ip_control_msg(control_msg); break;
                default: break;
            }
            control_msg = CMSG_NXTHDR(msg, control_msg);
        }
        return ret;
    }
    
    #define MY_IPPROTO_UDP IPPROTO_UDP
    #define MY_PF_INET AF_INET
    
    int main() {
        int                i;
        struct sockaddr_in dest;
        dest = makeAddr("172.23.0.1", 16000);
        // dest = makeAddr("8.8.8.8", 33445);
    
    
        int fd = socket(MY_PF_INET, SOCK_DGRAM, 0);
        if (fd < 0) {
            perror("socket");
            return -1;
        }
        if (connect(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
            perror("connect");
            return -1;
        }
    
        int val = 1;
        // 最关键的地方!!
        if (setsockopt(fd, IPPROTO_IP, IP_RECVERR, &val, sizeof(val)) == -1) {
            perror("setsockopt");
            return -1;
        }
    
        char sendbuf[] = {"whatever"};
    
        int bwrite = send(fd, sendbuf, sizeof(sendbuf), 0);
        if (bwrite == -1) {
            perror("send");
            return -1;
        }
    
    
        char          buf[1024];
        char          control_buf[1024];
        struct msghdr msg;
        struct iovec  iov = {buf, 1024};
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov        = &iov;
        msg.msg_iovlen     = 1;
        msg.msg_control    = &control_buf;
        msg.msg_controllen = 1024;
    
        sleep(3);
    
        /*
         * Reading from the error queue is always a non-blocking operation. To block
         * waiting on a timestamp, use poll or select. poll() will return POLLERR in
         * pollfd.revents if any data is ready on the error queue. There is no need
         * to pass this flag in pollfd.events. This flag is ignored on request. See
         * also "man 2 poll".
         */
        int bread = recvmsg(fd, &msg, MSG_ERRQUEUE);
        if (bread == -1) {
            perror("recv");
            return -1;
        }
        if (control_msg(&msg) >= 0)
            printf("successed!
    ");
        else
            printf("failed!
    ");
    
        close(fd);
        return 0;
    }
    
    
  • 相关阅读:
    角学习教程
    用AngularJS指令扩展HTML
    MVC 6动态导航菜单从数据库
    从Angular5和ASP开始。网络核心
    .NET中的音乐符号
    在Blazor的音乐符号-第二部分
    ASP。NET Core 2.1:集成VMD.RESTApiResponseWrapper。REST API应用程序的核心
    AsyncHttpClient
    Mina
    Volley
  • 原文地址:https://www.cnblogs.com/Kimbing-Ng/p/12492974.html
Copyright © 2020-2023  润新知