- 您有多个网络适配器。
- 将UDP套接字绑定到本地端口,而不指定地址。
- 在其中一个适配器上接收数据包。
如何获取接收数据包的适配器的本地IP地址?
问题是,“接收方适配器的IP地址是什么?” 而不是我们在中获取的发送方地址。
receive_from( ..., &senderAddr, ... );
调用。
IPAddress FindLocalIPAddressOfIncomingPacket( senderAddr )
{
foreach( adapter in EnumAllNetworkAdapters() )
{
adapterSubnet = adapter.subnetmask & adapter.ipaddress;
senderSubnet = adapter.subnetmask & senderAddr;
if( adapterSubnet == senderSubnet )
{
return adapter.ipaddress;
}
}
}
你好,
我假设你已经使用INADDR_ANY进行了绑定以指定地址。
如果是这样的话,那么INADDR_ANY的语义是创建一个UDP套接字,并在所有接口上指定端口。该套接字将接收发送到指定端口的所有接口的数据包。
在使用此套接字发送数据时,将使用编号最低的接口。发件人地址字段设置为使用的第一个出站接口的IP地址。
首个出站接口定义为执行ifconfig -a时的序列。它可能是eth0。
希望对你有所帮助。
祝好, 罗布
//create socket and bind to local address:INADDR_ANY:
int s = socket(PF_INET,SOCK_DGRAM,0);
bind(s,(struct sockaddr *)&myAddr,sizeof(myAddr)) ;
// set option
int onFlag=1;
int ret = setsockopt(s,IPPROTO_IP,IP_PKTINFO,&onFlag,sizeof(onFlag));
// prepare buffers
// receive data buffer
char dataBuf[1024] ;
struct iovec iov = {
.iov_base=dataBuf,
.iov_len=sizeof(dataBuf)
} ;
// control buffer
char cBuf[1024] ;
// message
struct msghdr msg = {
.msg_name=NULL, // to receive peer addr with struct sockaddr_in
.msg_namelen=0, // sizeof(struct sockaddr_in)
.msg_iov=&iov,
.msg_iovlen=1,
.msg_control=cBuf,
.msg_controllen=sizeof(cBuf)
} ;
while(1) {
// reset buffers
msg.msg_iov[0].iov_base = dataBuf ;
msg.msg_iov[0].iov_len = sizeof(dataBuf) ;
msg.msg_control = cBuf ;
msg.msg_controllen = sizeof(cBuf) ;
// receive
recvmsg(s,&msg,0);
for( struct cmsghdr* pcmsg=CMSG_FIRSTHDR(&msg);
pcmsg!=NULL; pcmsg=CMSG_NXTHDR(&msg,pcmsg) ) {
if(pcmsg->cmsg_level==IPPROTO_IP && pcmsg->cmsg_type==IP_PKTINFO) {
struct in_pktinfo * pktinfo=(struct in_pktinfo *)CMSG_DATA(pcmsg);
printf("ifindex=%d ip=%s\n", pktinfo->ipi_ifindex, inet_ntoa(pktinfo->ipi_addr)) ;
}
}
}
在非对称路由环境下,以下内容无法正常工作。
您可以先将SO_REUSEADDR设置为true。
BOOL bOptVal = 1;
setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (char *)&boOptVal, sizeof(bOptVal));
在receive_from( ..., &remoteAddr, ... );
之后,创建另一个套接字,并连接到remoteAddr。然后调用getsockname可以获取IP地址。
SOCKET skNew = socket( )
// Same local address and port as that of your first socket
// INADDR_ANY
bind(skNew, , )
// set SO_REUSEADDR to true again
setsockopt(skNew, SOL_SOCKET, SO_REUSEADDR, (char *)&boOptVal, sizeof(bOptVal));
// connect back
connect(skNew, remoteAddr)
// get local address of the socket
getsocketname(skNew, )
ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len);
ssize_t recvmsg(int socket, struct msghdr *message, int flags);
[..] 如果address不是空指针且socket不是面向连接的,则填充消息的源地址。
实际代码:
int nbytes = recvfrom(sock, buf, MAXBUFSIZE, MSG_WAITALL, (struct sockaddr *)&bindaddr, &addrlen);
fprintf(stdout, "读取 %d 字节,本地地址为 %s\n", nbytes, inet_ntoa(bindaddr.sin_addr.s_addr));
希望这有所帮助。
试试这个:
gethostbyname("localhost");