为什么 Java 中的 STUN 如此庞大的合理答案也是可以接受的。
这是一个合理的问题。 STUN 的 99% 只是一个简单的回显/响应协议,客户端通过它与公共互联网之间的 NAT 来自我发现 IP 和端口映射。构建了a STUN library in C++,我有了一些见解。
让我们考虑一下 STUN 库需要什么:
一个消息编写器,它使用属性字段模式生成 STUN 消息,不仅允许字段以任何顺序出现,它还允许添加自定义属性。
可以读回此类消息并转换数据的消息解析器
结构合理的代码使用。它需要安全地执行此操作并避免未处理的异常。
用于发送/接收此类消息的套接字网络代码。 STUN 服务器在技术上需要监听 2 个 IP 和 2 个端口,这使得服务器的网络代码更加复杂。
如果我们只关心绑定请求和绑定响应,我们会
完毕。但 STUN RFC 也定义了一组 NAT 分类测试。因此,需要额外的状态机逻辑来完成任何此类库。
如果 STUN 库要一直使用协议提供的安全选项,则需要一定数量的加密代码来对消息进行散列和签名
因此,将所有这些组合到一个库中,任何人都可以将其用于 STUN 的所有不同目的,包括映射地址发现、NAT 分类和 ICE 协商,它开始快速发展。
您可以轻松地滚动一些对绑定请求的字节进行硬编码的套接字代码,然后进行一些破解解析以解析响应。这可能会满足您自己的需求,但永远不会以这种方式编写完善的开源库。
JSTUN 是一个好的开始。我与原作者分享了一些互操作和错误修复代码。他没有积极维护它,但它是 RFC 3489 的一个很好的实现。我什至破解了它一次以在 Android 上运行。
在 JSTUN 中生成 STUN 绑定请求。
MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
sendMH.generateTransactionID();
// add an empty ChangeRequest attribute. Not required by the standard, but JSTUN server requires it
ChangeRequest changeRequest = new ChangeRequest();
sendMH.addMessageAttribute(changeRequest);
byte[] data = sendMH.getBytes();
// not shown - sending the message
然后将响应解析回来:
byte [] receivedData = new byte[500];
// not shown - socket code that receives the messages into receivedData
receiveMH.parseAttributes(receivedData);
MappedAddress ma = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
然后将上面的内容与一些套接字代码结合起来。可以在 DiscoveryTest.java 源文件中找到将上述内容与套接字代码相结合的最佳示例。你真的只需要这个类的test1() 方法中的代码。