这是一个合理的问题。STUN的99%只是一个简单的回显/响应协议,用于客户端自我发现其与公共互联网之间的NAT IP和端口映射。作为一个在C++中构建
STUN库的人,我有一些见解。
让我们思考一下STUN库需要满足什么条件:
- 一个消息编写器,可以生成带有属性字段模式的STUN消息,这种模式不仅允许任意顺序的字段出现,而且还可以添加自定义属性。
- 一个消息解析器,可以读取这样的消息并将其转换为代码使用的数据结构。它需要以安全方式执行此操作,并避免未处理的异常。
- 套接字网络代码,用于发送/接收此类消息。技术上要求STUN服务器监听2个IP和2个端口,因此这使得服务器的网络代码更加复杂。
- 如果我们只关心绑定请求和绑定响应,那么我们就完成了。但是STUN RFC还定义了一组NAT分类测试。因此,需要额外的状态机逻辑才能使任何此类库完整。
- 如果STUN库要充分利用协议提供的安全选项,就需要一些密码代码来对消息进行哈希和签名。
因此,将所有这些组合成一个库,供任何人用于STUN的不同目的,包括映射地址发现、NAT分类和ICE协商,它很快就会变得非常庞大。
您可以轻松地编写一些套接字代码,硬编码绑定请求的字节,然后进行一些修改以解析响应。这可能能够满足您自己的需求,但是一个良好的开源库永远不会以这种方式编写。
JSTUN是一个很好的起点。我已经与原作者分享了一些interop和错误修复代码。他没有积极维护它,但它是RFC 3489的一个很好的实现。我甚至曾经对其进行过修改,以便在Android上运行。
在JSTUN中生成STUN绑定请求。
MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
sendMH.generateTransactionID();
ChangeRequest changeRequest = new ChangeRequest();
sendMH.addMessageAttribute(changeRequest);
byte[] data = sendMH.getBytes();
然后解析返回的响应:
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)
接下来将上述内容与一些socket代码结合起来。最好的方法是在DiscoveryTest.java源文件中找到将上述内容与socket代码结合的最佳示例。你只需要这个类中的test1()
方法的代码。