【问题标题】:UDP Source port different on client getsocketname() and server recv()客户端 getsocketname() 和服务器 recv() 上的 UDP 源端口不同
【发布时间】:2015-10-24 16:31:56
【问题描述】:

您好,我的问题很简单:

我尝试通过 TCP 连接建立 UDP 连接(因为多个客户端通过多个通道连接到服务器,我想通过主 TCP 连接识别相同的客户端)

我通过在客户端上创建一个 TCP 和 UDP 套接字并通过 TCP 将操作系统随机分配的本地 UDP 端口传输到服务器来做到这一点。 (在 Windows 上,我通过 getsocketname() 在 sendto() 之后获取端口)。 在服务器上,我使用这个端口将 UDP 数据包发送到这个确切的客户端。 不幸的是,我真正必须发送到的端口与我发送到服务器的本地端口不同。

例如:

我的客户端获得分配的本地 UDP 端口 56423。通过已建立的 TCP 连接将其发送到我的服务器。服务器尝试向该端口发送 UDP 数据包 -> 失败。 当我使用标准方法通过 UDP 套接字上的 recv() 检索服务器上的端口时,它报告源端口 30299。(我必须提前发送 UDP 数据包以获取该信息,但我想避免实现 UDP连接握手)

这怎么可能?

据我了解,UDP 只有一个目标端口和一个源端口。所以它应该通过将客户端本地端口发送到服务器来工作。 中间可能有使用备用端口的代理服务吗?

编辑: 一些代码:

在客户端: (s32 = int, c8 = char)

if(m_WinSock == INVALID_SOCKET)
    return;

struct sockaddr_storage addr;
s32 len = sizeof(sockaddr);

getsockname(m_WinSock, (SOCKADDR*)&addr, &len);
c8 ipstr[INET6_ADDRSTRLEN + 1];
s32 port;
if(addr.ss_family == AF_INET)
{
    struct sockaddr_in *s = (struct sockaddr_in *)&addr;
    m_uLocalPort = ntohs(s->sin_port);
    inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
}
else
{ // AF_INET6
    struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
    m_uLocalPort = ntohs(s->sin6_port);
    inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
}

m_uLocalPort 之后通过 TCP 传输。

在服务器上:

    SOCKADDR_IN address;
    ZeroMemory(&address, sizeof(address));
    address.sin_addr = addrin;
    address.sin_port = htons((u16)remoteport);
    address.sin_family = AF_INET;

remoteport 是客户端发送的端口。

从客户端发送数据包后在服务器上使用此代码:

struct sockaddr_storage addr;
s32 len = sizeof(sockaddr);
s32 bytes = recvfrom(m_WinSock, m_RecvBuffer, NET_MAX_UDP_SIZE, 0, (SOCKADDR*)&addr, &len);
m_RecvAddress = *(struct sockaddr_in *)&addr;

s32 port = ntohs(m_RecvAddress.sin_port);
printf("recv from port %i\n", port);

我收到一个完全不同的端口。

【问题讨论】:

  • 如何发送和接收 UDP 端口?您是否在某处忘记了htons/ntohs?端口30299(在您的示例中)是 TCP 连接源端口还是目标端口?
  • 另外,如果服务端有一个固定的端口号,客户端只使用sendto中的服务端地址和固定的端口号,不是更简单吗?
  • 我通过首先建立的有效 TCP 连接发送端口。服务器有一个固定的端口号(通过 bind())客户端发送到。我猜想应该是这样使用 htons / ntohs 的。它只改变字节顺序,但字节顺序不是问题,看看 56423 和 30299 的字节它们完全不同。在服务器上,我想将 UDP 包发送到正确的客户端。为此,我需要客户端的(随机)UDP 端口。这些端口肯定是 UDP 套接字的这些端口。我可以在我的任务管理器中看到这些作为 UDP 监听端口绑定到我的应用程序
  • 再次,如何您发送端口号?请显示一些代码,最好是Minimal, Complete, and Verifiable Example
  • 我编辑了我的问题

标签: c++ sockets udp winsock ports


【解决方案1】:

这不是解决问题的可靠方法。例如,NAT 可能会妨碍并重新映射您的端口,甚至是从您的客户端看到的本地 ip。您可能还需要进行 udp 打孔。

正确的方法是不假设 tcp 和 udp 有任何关联 - 它们是不同的管道。

编辑:例如:

  1. 客户端 connect() 到服务器的公共 tcp 接口。
  2. 身份验证
  3. 服务器向客户端发送一个验证码 (cookie)。
  4. 客户端sendto()服务器的公共udp接口,带有cookie。
  5. 服务器从recvfrom()获取sockaddr
  6. 使用 cookie 的服务器关联关系,例如 sockaddr 与 revalent tcp 连接。可选地检查/限制 ip 以防止其他人的牛逼。
  7. 也就是说,现在您有了一对连接 - 一个接受的 tcp 套接字以及用于 udp 通信的 sockaddr

PS。您可能还想对握手进行一些加密,但这超出了问题的范围。

【讨论】:

  • 我想将它们用作不同的管道。但是我想通过这些管道与同一个客户端进行通信,除了我的方法之外,如何实现呢?
  • 你应该在服务器端有公共入口(tcp&udp),让客户端单独发起通信,并将它们与cookie和ip地址等相关联。另一方面,您可以告诉您的 NAT(或路由器)在 udp 端口​​范围上“通过”,并希望您的 isp 没有什么好笑的。
  • 我有公共入口,例如我在 TCP 端口 5000、5001 和 UDP 5002 上公开监听。现在,当客户端连接时,我想在我的服务器上创建一个 Peer 对象并存储适当的连接信息所有通道(包括本地 udp 端口​​)。所以稍后我可以决定如何将数据传输到这个客户端。您说我必须使用 recv 在顶部创建 UDP 握手,而不是通过不同的通道传输本地端口?然后尝试以某种方式找出哪个连接尝试属于哪个客户端?
  • 是的。您应该从clientserver 发出一次sendto() 以启动udp 通信,以便服务器具有正确的udp 联系点。这也起到 udp 打孔的作用,因此来自服务器的数据包可以一路传输,包括任何 isp 翻译,到达客户端计算机。
  • 一些进一步的信息。您的方法是合乎逻辑的,RTSP 确实在做类似的事情。但是,当 ISP 和您的路由器开始进行端口重新映射时,这也造成了麻烦,因为 RTSP 他们有特殊的端口安排,并且支持 RTSP 的 NAT 实际上正在监视 TCP 通信并特别处理这些端口。但是对于自定义协议,您应该采用复杂的方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-24
  • 2020-06-22
相关资源
最近更新 更多