赞
踩
在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_ANY和3002端口后,进入监听状态,此时,有客户端程序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);
众所周知,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; }
上述程序可以获取对应发送方的源IP地址和端口以及接收方路由表分配的IP地址。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。