【问题标题】:POSIX UDP socket not binding to correct IPPOSIX UDP 套接字未绑定到正确的 IP
【发布时间】:2016-01-23 17:15:52
【问题描述】:

我正在为大学编写一个项目,涉及使用 POSIX 套接字和 C++ 编写聊天客户端和服务器。

客户端应该使用 P2P 相互交谈,例如每个客户端都有自己的开放式 UDP 套接字,他通过该套接字发送和接收来自其他客户端的消息。

我的问题有两个:

  1. 我的 UDPSocket 类构造函数似乎完全忽略了端口号,无论参数如何都绑定到端口 65535。
  2. 端口绑定到 IP 255.255.255.255 而不是我自己的 IP (10.0.0.3),或者至少这是我调用 getpeername 时得到的。

据我所知,传递 INADDR_ANY 应该绑定到我的本地地址,传递端口号 0 应该让操作系统选择一个空闲端口,我做错了什么?

这是我的 UDPSocket 类的构造函数:

UDPSocket::UDPSocket(int port){
socket_fd = socket (AF_INET, SOCK_DGRAM, 0);

// clear the s_in struct
bzero((char *) &in, sizeof(in));  /* They say you must do this    */

//sets the sin address
in.sin_family = (short)AF_INET;
in.sin_addr.s_addr = htonl(INADDR_ANY);    /* WILDCARD */
in.sin_port = htons((u_short)port);

fsize = sizeof(from);

//bind the socket on the specified address
if(bind(socket_fd, (struct sockaddr *)&in, sizeof(in))<0){
    perror ("Error naming channel");
}
}

这是初始化:

m_Socket = new UDPSocket(0);

这是我用来检索绑定地址的方法:(UDPSocket继承Socket)

std::string Socket::GetSocketAddress()
{
    struct sockaddr_in  addr;
    int len = sizeof(addr);
    getpeername(socket_fd, (struct sockaddr*)&addr, (socklen_t*)&len);

    char ipAddressBuffer[50];
    memset(ipAddressBuffer, 0, sizeof(ipAddressBuffer));
    sprintf(ipAddressBuffer, "%s:%d", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));

    return ipAddressBuffer;
}

任何帮助将不胜感激, AVI。

【问题讨论】:

    标签: c++ sockets


    【解决方案1】:

    您正在使用 getpeername ,它为您提供已连接套接字的远程地址。如果查看getpeername()的返回值,应该是失败了。

    • 你需要使用getsockname()而不是getpeername()来获取你本地socket的地址
    • 您需要检查getsockname() 是否成功。

    请注意,您的套接字绑定到特殊的 0.0.0.0 地址,这意味着“所有本地接口”,因此 getsockname() 也将返回。

    【讨论】:

    • 感谢(非常)快速响应,尽管现在我正面临您似乎预测的情况 - 地址是 0.0.0.0 而不是我自己的地址(在这种特定情况下为 10.0.0.3) .如何绑定到我的本地 IP 地址?
    • 你需要吗?绑定到 0.0.0.0 将允许您在任何地方发送和接收数据包。
    • 它将如何工作?假设我在 2 台不同的 PC 上有我的服务器和 2 个客户端 A 和 B,如果我给客户端 A 客户端 B 的地址为“0.0.0.0:3456”,他是否能够向该地址发送消息并到达客户端 B?跨度>
    • 这意味着您实际上需要其他东西,您想将您的本地地址发送给您的同行之一。没有一个 API 可以找到“您的 IP 地址”,因为大多数系统可以有多个 IP 地址。您可以 A) 在您的套接字上调用 connect() 并使用您要发送数据包的 IP 地址,在您调用 connect() 之后,getsockname() 将返回将用作源地址的 IP 地址在该套接字上发送数据包时。 (但是,一旦您调用 connect(),该套接字只能将数据包发送到您 connect() 到的那个
    • 或 B),调用 getifaddrs() 并遍历所有地址并选择一个你想要的。例如您可以将“eth0”传递给您的程序并使用 getifaddrs() 来查找“eth0”的 IPv4 地址。
    【解决方案2】:

    回答更一般的问题“如何使用 UDP 建立对等通信”:

    对于 UDP 套接字,虽然您可以使用 connect,但您通常不希望这样做,因为这将您限制为每个套接字只有一个对等点。相反,您希望在每个对等方中使用单个未连接的 UDP 套接字,并通过 sendtorecvfrom 系统调用来发送和接收每个数据包具有不同地址的数据包。

    sendto 函数接受一个数据包和一个发送到的对等地址,而recvfrom 函数返回一个数据包和它来自的对等地址。使用单个套接字,无需与selectpoll 进行多路复用——您只需调用recvfrom 即可从任何来源获取下一个数据包。当你得到一个数据包时,你也得到了发送数据包(回)到的对等地址。

    在启动时,您的对等方将创建一个单独的套接字并将其绑定到INADDR_ANY(允许它在机器上的任何接口或广播地址上接收数据包)以及分配给您程序的特定端口或端口 0(允许操作系统选择任何未使用的端口)。在后一种情况下,您需要使用getsockname 来获取端口并将其报告给用户。一旦设置了套接字,对等程序就可以sendto 任何它知道的对等点,或者recvfrom 任何对等点(包括那些它还不知道的)。

    所以唯一棘手的部分是引导程序——让第一个数据包流动,以便对等方可以接收它们并找出要与之交谈的对等方地址。一种方法是在启动每个对等点时在命令行上指定对等点地址。您将在没有参数的情况下开始第一个(因为它还没有对等点)。它只会recvfrom(在套接字设置之后)从对等方获取数据包。以第一个地址作为参数开始第二个。它向第一个对等体发送一个(或多个)数据包,然后它会在收到第一个数据包后立即知道新的对等体。现在用命令行中前两个的地址启动第三个客户端...

    【讨论】:

    • 感谢您的回答,它似乎适合更一般的情况,尽管在我的具体情况下不太适合。
    猜你喜欢
    • 2014-05-27
    • 1970-01-01
    • 2023-02-12
    • 2015-07-20
    • 2021-06-13
    • 2015-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多