4

我发现了几个 java STUN 实现

Java 和我应该使用哪些 Stun 库?

另请参阅:STUN、TURN、ICE 库 for Java

但它是有许多类的罐子。我希望以单一方法或至少一个小类的形式找到一些简单的东西。就像下面的python代码一样。

https://github.com/jtriley/pystun/blob/develop/stun/init .py _

为什么 Java 中的 STUN 如此庞大的合理答案也是可以接受的。

4

3 回答 3

7

为什么 Java 中的 STUN 如此庞大的合理答案也是可以接受的。

这是一个合理的问题。STUN 的 99% 只是一个简单的回显/响应协议,客户端通过它与公共互联网之间的 NAT 来自我发现 IP 和端口映射。在 C++ 中构建了一个 STUN 库后,我有了一些见解。

让我们考虑一下 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()这个类的方法中的代码。

于 2014-12-20T20:48:26.840 回答
3
    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();


    s = new DatagramSocket();
    s.setReuseAddress(true);

    DatagramPacket p = new DatagramPacket(data, data.length, InetAddress.getByName("stun.l.google.com"), 19302);
    s.send(p);

    DatagramPacket rp;

    rp = new DatagramPacket(new byte[32], 32);

    s.receive(rp);
    MessageHeader receiveMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingResponse);
    // System.out.println(receiveMH.getTransactionID().toString() + "Size:"
    // + receiveMH.getTransactionID().length);
    receiveMH.parseAttributes(rp.getData());
    MappedAddress ma = (MappedAddress) receiveMH
            .getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
    System.out.println(ma.getAddress()+" "+ma.getPort());
于 2017-05-02T18:32:14.080 回答
2
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import de.javawi.jstun.attribute.ChangeRequest;
import de.javawi.jstun.attribute.ChangedAddress;
import de.javawi.jstun.attribute.ErrorCode;
import de.javawi.jstun.attribute.MappedAddress;
import de.javawi.jstun.attribute.MessageAttribute;
import de.javawi.jstun.attribute.MessageAttributeException;
import de.javawi.jstun.attribute.MessageAttributeParsingException;
import de.javawi.jstun.header.MessageHeader;
import de.javawi.jstun.header.MessageHeaderParsingException;
import de.javawi.jstun.util.UtilityException;


public class StunTest { public static void main(String[] args) throws UtilityException, IOException {


        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();


        DatagramSocket s = new DatagramSocket();
        s.setReuseAddress(true);

        DatagramPacket p = new DatagramPacket(data, data.length, InetAddress.getByName("stun.l.google.com"), 19302);
        s.send(p);

        DatagramPacket rp;

        rp = new DatagramPacket(new byte[32], 32);

        s.receive(rp);
        MessageHeader receiveMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingResponse);
        // System.out.println(receiveMH.getTransactionID().toString() + "Size:"
        // + receiveMH.getTransactionID().length);
        try {
                receiveMH.parseAttributes(rp.getData());
        } catch (MessageAttributeParsingException e) {
                e.printStackTrace();
        }
        MappedAddress ma = (MappedAddress) receiveMH
                .getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
        System.out.println(ma.getAddress()+" "+ma.getPort());
    }
}
于 2019-08-05T00:17:25.100 回答