当前位置:   article > 正文

Socket设置INADDR_ANY后从数据包中解析出发送的目的地址和源地址

Socket设置INADDR_ANY后从数据包中解析出发送的目的地址和源地址

问题描述:

在Linux平台下可能存在多个网络接口(网口),创建Socket使用的地址为INADDR_ANY时,表示监听本地0.0.0.0地址,这表示如果本地有多个IP地址时,无论哪个设备发送UDP的套接字消息时,只要端口正确,你都可以捕获到该消息并进行处理。

举例:

例如:当设备A 存在三个网口时,每个网口都有不同的IP地址。
网口一的IP地址为:192.168.1.10
网口二的IP地址为:192.168.1.11
网口三的IP地址为:192.168.1.12
该设备存在服务程序并绑定INADDR_ANY3002端口后,进入监听状态,此时,有客户端程序B,使用Socket套接字向192.168.1.10和端口3002发送一个UDP消息,设备A都可以收到该UDP消息。

INADDR_ANY 其实就是泛指本机的意思,表示本机的所有IP。

使用示例:

    struct sockaddr_in server;
    // 设置监听地址
    server_addr.sin_family = AF_INET;
    // 所有地址0.0.0.0
    server_addr.sin_addr.s_addr = INADDR_ANY; 
    // 主机字节顺序转换成网络字节顺序
    server_addr.sin_port = htons(PORT);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

遇到问题:

众所周知,UDP(UserDatagramProtocol)消息是面向无连接的,一般我们服务端使用UDP监听时,是用recvfrom接口进行读取消息,但是,如果监听地址使用使用INADDR_ANY此时,我们无法通过此接口获取到发送方的IP地址,以及发送方发送到本地的IP地址(有点拗口)。


简单的说,就是我们无法获取到双方IP地址,在某些需求下,这种情况我们不可再使用recvfrom接口了。

解决办法:

创建Socket时添加参数,使用recvmsg替换recvfrom接口。

具体操作代码:


#include <stdio.h>
#include <stdint.h>  
#include <stdlib.h>
#include <unistd.h>  
#include <stdint.h>
#include <string.h>
#include <sys/uio.h> 
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define LOCAL_LISTEN_PORT   4006
#define RECV_BUFF_MAX_SIZE  4096

int main(int argc, char* argv[])
{
	/*
	// Create Variables
	*/
	struct iovec iove[1];
	struct cmsghdr* cmhp;
	struct msghdr message;
	/* Source IP address used by the client when sending packets */
	struct sockaddr_in saddr;
    /* Destination IP address that the client sends to the server */
	struct sockaddr_in daddr;
    /* Local listening address information */
    struct sockaddr_in server_addr;
    /* Used to point to the obtained local address information */
	struct in_pktinfo *pktinfo = NULL;	 
	
	/* Calculates the necessary whitespace for a secondary data object. */
	char buff[CMSG_SPACE(sizeof(struct in_pktinfo) + CMSG_SPACE(sizeof(int)))] = {0};

	/* Control information */
	struct cmsghdr *cmh = (struct cmsghdr *)buff;

	int on = 1;
    int ret, fd, addr_len, recv_len;
	/* Message cache */
	char buffer[BUFFER_MAX_SIZE] = {0};
	
	/* initialize */
	addr_len = sizeof(struct sockaddr_in);
    memset(&buffer, 0, sizeof(buffer));
    memset(&msg, 0, sizeof(struct msghdr));
    memset(&iov, 0, sizeof(struct iovec));
    memset(&saddr, 0, sizeof(struct sockaddr_in));
    memset(&daddr, 0, sizeof(struct sockaddr_in)); 
	
	// Set listening address
    server_addr.sin_family = AF_INET;
    // All addresses are 0.0.0.0
    server_addr.sin_addr.s_addr = INADDR_ANY; 
    server_addr.sin_port = htons(PORT);

	// Create a UDP socket : SOCK_DGRAM expressed as UDP
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd == -1) 
    {
        printf("create socket fail \r\n");
        return 1;
    }    

	// Set the SO_REUSEADDR attribute to enable address multiplexing and bind multiple addresses to the same port
    if ((ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) != 0) 
    {
        log("setsockopt reuseaddr fail, ret : %d,error : %d \r\n", ret, errno);
        close(fd);
        return 1;
    }
 	/* Set the IP_PKTINFO property - to obtain information about the packet */
	if (0 != setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on))) 
    {
        printf("setsockopt ip_pktinfo fail, errno : %d \r\n", errno);
        close(fd);
        return 1;  
    }

    // Bind the local listening address
    if (0 != bind(fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in))) 
    {
        printf("bind local listening addr fail,errno : %d \r\n", errno);
        close(fd);
        return 1;
    }    

	// Cyclic receiving information
	while(1)
	{
		/* Number of buffers */
		message.msg_iovlen = 1;
		/* Address of multiple I/O buffers */
		message.msg_iov = &iov[0];
		/* Address of secondary data */
		message.msg_control = cmh;
		/* Protocol address of the message Source address of the stored message, port */
		message.msg_name = &saddr;
		/* Length of address */
		message.msg_namelen = addr_len;
		/* Length of auxiliary data */
		message.msg_controllen = sizeof(buff);  
		
		// Information sent by the client
		iove[0].iov_base = &buffer;
		// Length of the message sent by the client
		iove[0].iov_len = sizeof(buffer);
		
		/* recvmsg : udp reflector
		sockfd: represents the socket file descriptor that needs to receive the message.
		msg: A pointer to an msghdr structure that holds the received message and related information.
		flags: Call option, which specifies the behavior of the function */
		recv_len = recvmsg(fd, &message, 0);
		if (recv_len > 0)
        {
        	printf("Read Client buffer:[%s]!\n",buffer);

			/* Sets the location of secondary data */
			message.msg_control = cmh;
            /* Sets the size of the secondary data */
            message.msg_controllen = sizeof(buff);
			
			/* CMSG_FIRSTHDR() 
			This macro is used to return a struct cmsghdr pointer to the first firsthDR in the FirsthDR buffer.
			The input value is a pointer to the struct msghdr structure */
			
			/* CMSG_NXTHDR() 
			This struct cmsghdr pointer is used to return the next attached data object. 
			This macro takes two input parameters:A pointer to the struct msghdr structure , 
			A pointer to the current struct cmsghdr
			If there is no next attached data object, the macro returns NULL.
			*/
			for (cmhp = CMSG_FIRSTHDR(&message); cmhp; cmhp = CMSG_NXTHDR(&message, cmhp)) 
            {
            	/* cmsg_level - Original protocol */
            	if (cmhp->cmsg_level == IPPROTO_IP) 
                {
                	/* cmsg_type: type of control information */
                	if(cmhp->cmsg_type == IP_PKTINFO)
                	{
	                	/* CMSG_DATA()
						The  macro accepts a pointer to the cmsghdr structure.
						The returned pointer value points to the first byte of the subsidiary data following the header and,
						if present, the padding byte.*/
						pktinfo = (struct in_pktinfo *)CMSG_DATA(cmhp);
                        if(pktinfo != NULL)
                        {
                            daddr.sin_family = AF_INET;
                            daddr.sin_addr = pktinfo->ipi_addr;

                            /* Source address and port of the packet */
                            printf("saddr : %u:%u:%u:%u:%hu \r\n", NIPQUAD(saddr.sin_addr), ntohs(saddr.sin_port));

                            /* The header identifies the destination address */
                            /*If the message sent by the client is sent by the broadcast, the content of daddr.sin_addr is the broadcast address.
							But ipi_spec_dst is still the IP address received by the server */
                            printf("daddr : %u:%u:%u:%u \r\n", NIPQUAD(daddr.sin_addr));
                            /* Route destination address */
                            printf("daddr : %u:%u:%u:%u \r\n", NIPQUAD(pktinfo->ipi_spec_dst));
                        }
                        else
                        {
                            printf("Para CMSGHDR Fail!\n");
                        }
                	}
                }
            }
		}
		memset(buff, 0, sizeof(buff));
        memset(buffer, 0, sizeof(buffer));
        memset(&saddr, 0, sizeof(struct sockaddr_in));
        memset(&daddr, 0, sizeof(struct sockaddr_in));        
	}
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179

上述程序可以获取对应发送方的源IP地址和端口以及接收方路由表分配的IP地址。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
  

闽ICP备14008679号