Java中的简单STUN客户端

5
我找到了几个Java STUN实现。 Java和哪些STUN库应该使用? 引用:
有以下几个:
- JSTUN:http://jstun.javawi.de/ - STUN:http://java.net/projects/stun 同时参见:Java的STUN,TURN,ICE库 但它们都是包含许多类的jar文件。我希望能够找到一些简单的形式,例如单个方法或至少单个小类。就像下面的Python代码。

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

如果能提供合理的解释为什么Java中的STUN如此庞大,那也是可以接受的。

3个回答

8
这是一个合理的问题。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();

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

接下来将上述内容与一些socket代码结合起来。最好的方法是在DiscoveryTest.java源文件中找到将上述内容与socket代码结合的最佳示例。你只需要这个类中的test1()方法的代码。


4
Java:
    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());

Kotlin
val sendMH = MessageHeader(MessageHeaderInterface.MessageHeaderType.BindingRequest)
sendMH.generateTransactionID()
println("sent transaction id: ${sendMH.transactionID.toHexString()}")

// add an empty ChangeRequest attribute. Not required by the standard,
// but JSTUN server requires it
val changeRequest = ChangeRequest()
sendMH.addMessageAttribute(changeRequest)
val data:ByteArray = sendMH.bytes

val s = DatagramSocket()
s.reuseAddress = true
val p = DatagramPacket(data,data.size,InetAddress.getByName("stun.l.google.com"),19302)
s.send(p)

val rp = DatagramPacket(ByteArray(32),32)
s.receive(rp)
val receiveMH = MessageHeader(MessageHeaderInterface.MessageHeaderType.BindingResponse)
try
{
    receiveMH.parseAttributes(rp.data)
}
catch (e:MessageAttributeParsingException)
{
    e.printStackTrace()
}
println("received transaction id: ${receiveMH.transactionID.toHexString()}")
val ma = receiveMH.getMessageAttribute(MessageAttributeInterface.MessageAttributeType.MappedAddress) as MappedAddress
println("${ma.address}:${ma.port}")

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接