如何获取用于连接到我的服务器的 IP 地址?
How to get the IP address used to connect to my server?
我有一个服务器监听多个设备(LAN、WiFi,可能还有一些 VPN)。
侦听绑定套接字 (AF_INET) 绑定 INADDR_ANY 以在所有接口上公开。
然后客户端连接并开始交换。
此时,如何获取有关使用哪个设备进行连接的信息?
等价地:如何获取客户端用于连接的 IP 地址?
编辑:
按照@Barmar 的建议,我尝试了以下操作:
#include <sys/socket.h>
#include <netinet/ip.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
void error(char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
unsigned clilen;
struct sockaddr_in serv_addr, cli_addr;
struct sockaddr_in myaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR creating socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 5666;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd, 5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
clilen = sizeof(myaddr);
if (getsockname(newsockfd, (struct sockaddr *)&myaddr, &clilen))
error("ERROR in getsockname");
unsigned int i = myaddr.sin_addr.s_addr;
int p = myaddr.sin_port;
int a = i & 0xff;
i >>= 8;
int b = i & 0xff;
i >>= 8;
int c = i & 0xff;
i >>= 8;
int d = i & 0xff;
printf("my address is: %d.%d.%d.%d:%d\n", a, b,c, d, p);
return 0;
}
...并且(经过一些调试后)它有效。
请注意,端口不是预期值 (5666),而是一致的 8726。
如果您使用的是 SOCK_STREAM
套接字(即 TCP),而不是 SOCK_DGRAM
(即 UDP),请在 accept()
返回的套接字上调用 getsockname()
。
如果您使用的是数据报套接字,我认为没有一种可移植的方法可以使用单个套接字来实现。传统的解决方案是为每个本地地址绑定一个单独的套接字,而不是使用 INADDR_ANY
,并使用 epoll()
或 select()
在所有这些地址上等待消息。您可以根据接收到消息的套接字来判断他们连接到哪个地址。
Linux 和 Windows 有一个扩展,可以让你更容易地做到这一点,通过在套接字上设置 IP_PKTINFO
选项并使用 recvmsg()
接收一个包。在msgheader.msg_control
中可以得到数据包的目的地址。参见 Get destination address of a received UDP packet
我有一个服务器监听多个设备(LAN、WiFi,可能还有一些 VPN)。
侦听绑定套接字 (AF_INET) 绑定 INADDR_ANY 以在所有接口上公开。
然后客户端连接并开始交换。
此时,如何获取有关使用哪个设备进行连接的信息?
等价地:如何获取客户端用于连接的 IP 地址?
编辑: 按照@Barmar 的建议,我尝试了以下操作:
#include <sys/socket.h>
#include <netinet/ip.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
void error(char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
unsigned clilen;
struct sockaddr_in serv_addr, cli_addr;
struct sockaddr_in myaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR creating socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 5666;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd, 5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
clilen = sizeof(myaddr);
if (getsockname(newsockfd, (struct sockaddr *)&myaddr, &clilen))
error("ERROR in getsockname");
unsigned int i = myaddr.sin_addr.s_addr;
int p = myaddr.sin_port;
int a = i & 0xff;
i >>= 8;
int b = i & 0xff;
i >>= 8;
int c = i & 0xff;
i >>= 8;
int d = i & 0xff;
printf("my address is: %d.%d.%d.%d:%d\n", a, b,c, d, p);
return 0;
}
...并且(经过一些调试后)它有效。
请注意,端口不是预期值 (5666),而是一致的 8726。
如果您使用的是 SOCK_STREAM
套接字(即 TCP),而不是 SOCK_DGRAM
(即 UDP),请在 accept()
返回的套接字上调用 getsockname()
。
如果您使用的是数据报套接字,我认为没有一种可移植的方法可以使用单个套接字来实现。传统的解决方案是为每个本地地址绑定一个单独的套接字,而不是使用 INADDR_ANY
,并使用 epoll()
或 select()
在所有这些地址上等待消息。您可以根据接收到消息的套接字来判断他们连接到哪个地址。
Linux 和 Windows 有一个扩展,可以让你更容易地做到这一点,通过在套接字上设置 IP_PKTINFO
选项并使用 recvmsg()
接收一个包。在msgheader.msg_control
中可以得到数据包的目的地址。参见 Get destination address of a received UDP packet