我正在编写一个 UDP 测试客户端/服务器,我想让它通过防火墙。据说我需要做的就是让双方发送到正确的 IP 和服务器。获取 IP 不是问题,但我如何让客户端随机选择一个空闲端口并将其报告给用户?我最终希望它连接到匹配服务器,但现在我需要一个简单的工作原型(prototype),并且我想计算端口号,以便我的 friend /测试人员可以通过 IM 向我发送 #,以便我们可以测试。
如何获取端口号? 抱歉这么长的描述。我注意到当我不给出描述时人们告诉我不要做我所要求的事情:(
最佳答案
用高科技术语来说,这实际上是一个非常棘手的问题,甚至是一对棘手的问题。根据防火墙的配置,它通常会允许来自 IP 端点上的另一个端点的响应作为请求的来源。所以...如果您的 friend 使用诸如 recvfrom()
系统调用之类的方式接收 UDP 数据报,则地址参数将接收要响应的 IP 端点信息。因此另一端应该能够使用相同的寻址信息通过 sendto()
进行响应。像这样的东西:
/* initiator */
struct sockaddr_in hisaddr;
memset(&hisaddr, 0, sizeof(hisaddr));
hisaddr.sin_addr.s_addr = htonl(target_ip);
hisaddr.sin_port = htons(target_port);
sendto(sd, msg_ptr, msg_sz, 0, (struct sockaddr*)&hisaddr, sizeof(hisaddr));
/* receiver */
struct sockaddr_in peeraddr;
socklen_t peer_sz = sizeof(peeraddr);
recvfrom(sd, buf_ptr, buf_sz, 0, (struct sockaddr*)&peeraddr, &peer_sz);
/* build response */
sendto(sd, msg_ptr, msg_sz, 0, (struct sockaddr*)&peeraddr, peer_sz);
另一端的 peeraddr
将是您的外部地址,或更准确地说,是您的防火墙的 IP 地址及其选择使用的端口号。您在代码中指定的端口号可能与您的 friend 必须向其发送数据的端口完全不同。最终,您选择使用哪个端口可能并不重要,因为防火墙可能在完全不同的端口上发送和接收 - 这就是 Network Address Translation就是这样。我建议阅读RFC3235了解有关如何克服这一障碍的一些技巧。
恕我直言,最好的方法是:
- 让操作系统通过调用
bind()
选择端口端口号为零或完全跳过绑定(bind) - 让客户端从套接字层接收地址信息(例如
recvfrom()
的第五个和第六个参数) - 客户端将响应发送到上一步中检索到的端点
- 调整防火墙配置,直到前面的步骤起作用
当然,所有的魔力都在最后一步。如果您可以禁用 NAT 或确保防火墙永远不会切换端口,那么确定端口号并绑定(bind)到它也将起作用。您可能需要查看 %WINDIR%\system32\drivers\etc\services
(或 /etc/services
取决于您的操作系统倾向)以了解保留或通常使用哪些端口号。
关于sockets - 如何获得空闲的套接字端口? C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/803012/