UDP 源端口在客户端 getsocketname() 和服务器 recv() 上不同
UDP Source port different on client getsocketname() and server recv()
嗨,我的问题很简单:
我尝试通过 TCP 连接建立 UDP 连接(因为多个客户端通过多个通道连接到服务器,我想通过主 TCP 连接识别相同的客户端)
我通过在客户端创建一个 TCP 和 UDP 套接字并通过 TCP 将 OS 随机分配的本地 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);
我收到一个完全不同的端口。
这不是解决问题的可靠方法。例如,NAT 会阻碍并重新映射您的端口,甚至是从您的客户端看到的本地 ip。你可能还需要做udp hole punching.
正确的方法是不假设 tcp 和 udp 有任何相互关系——它们是不同的管道。
编辑:例如:
- 客户端
connect()
到服务器的 public tcp 接口。
- 身份验证
- 服务器向客户端发送一个验证码(cookie)。
- 客户端
sendto()
服务器的 public udp 接口,带有 cookie。
- 服务器从
recvfrom()
. 获取 sockaddr
- 服务器关联,例如
sockaddr
到有效的 tcp 连接,使用 cookie。可选地 check/restrict 防止被其他人暴力攻击的 ip。
- 也就是说,现在你有一对连接 - 一个接受的 tcp 套接字和
sockaddr
用于 udp 通信。
PS。您可能还想对握手进行一些加密,但这超出了问题的范围。
嗨,我的问题很简单:
我尝试通过 TCP 连接建立 UDP 连接(因为多个客户端通过多个通道连接到服务器,我想通过主 TCP 连接识别相同的客户端)
我通过在客户端创建一个 TCP 和 UDP 套接字并通过 TCP 将 OS 随机分配的本地 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);
我收到一个完全不同的端口。
这不是解决问题的可靠方法。例如,NAT 会阻碍并重新映射您的端口,甚至是从您的客户端看到的本地 ip。你可能还需要做udp hole punching.
正确的方法是不假设 tcp 和 udp 有任何相互关系——它们是不同的管道。
编辑:例如:
- 客户端
connect()
到服务器的 public tcp 接口。 - 身份验证
- 服务器向客户端发送一个验证码(cookie)。
- 客户端
sendto()
服务器的 public udp 接口,带有 cookie。 - 服务器从
recvfrom()
. 获取 - 服务器关联,例如
sockaddr
到有效的 tcp 连接,使用 cookie。可选地 check/restrict 防止被其他人暴力攻击的 ip。 - 也就是说,现在你有一对连接 - 一个接受的 tcp 套接字和
sockaddr
用于 udp 通信。
sockaddr
PS。您可能还想对握手进行一些加密,但这超出了问题的范围。