关于so??ckets:Java中的UDP认为UDP有”连接”

UDP in Java thinks that UDP has "connections"

Java 中的 UDP 认为 UDP 有"连接"。这让我感到惊讶,因为我来自 C 背景,我一直使用 UDP 作为一种即发即弃的协议类型。

在 Java 中测试 UDP 时,我注意到如果远程 UDP 端口未在侦听,我会在尝试发送任何内容之前在 Java 中收到错误。

为了能够判断远程 UDP 端口是否正在侦听,Java 做了什么(没有我要求它)?

(下面的代码在套接字的接收线程中运行。发送在不同的线程中完成。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    try {
        socket = new DatagramSocket(udpPort);
        socket.connect(udpAddr, udpPort);
    } catch (SocketException e) {
        Log.d(TAG,"disconnected", e);
    }
    ...
    while (true) {
        // TODO: don't create a new datagram for each iteration
        DatagramPacket packet = new DatagramPacket(new byte[BUF_SIZE], BUF_SIZE);
        try {
            socket.receive(packet); // line 106
        } catch (IOException e) {
            Log.d(TAG,"couldn't recv", e);
        }
        ...

如果远程套接字未在侦听,

会产生以下错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
java.net.PortUnreachableException:
        at libcore.io.IoBridge.maybeThrowAfterRecvfrom(IoBridge.java:556)
        at libcore.io.IoBridge.recvfrom(IoBridge.java:516)
        at java.net.PlainDatagramSocketImpl.doRecv(PlainDatagramSocketImpl.java:161)
        at java.net.PlainDatagramSocketImpl.receive(PlainDatagramSocketImpl.java:169)
        at java.net.DatagramSocket.receive(DatagramSocket.java:253)
        at com.example.mypkg.MyClass.run(MyClass.java:106)
        at java.lang.Thread.run(Thread.java:856)
 Caused by: libcore.io.ErrnoException: recvfrom failed: ECONNREFUSED (Connection refused)
        at libcore.io.Posix.recvfromBytes(Native Method)
        at libcore.io.Posix.recvfrom(Posix.java:131)
        at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)
        ...


UDP in Java thinks that UDP has"connections".

不,它没有,但是 UDP(不管 Java)确实有连接的套接字。不是一回事。

This surprised me, coming from a C background where I had always used UDP as a fire-and-forget type of protocol.

你也可以在 C 中 connect() 一个 UDP 套接字。查一下。您所描述的与Java无关。

When testing UDP in Java, I noticed that if the remote UDP port is not listening, I get an error in Java before I attempt to send anything.

那是因为你连接了套接字。这样做的副作用之一是传入的 ICMP 消息可以以错误的形式路由回发送套接字。

What does Java do (without me asking it to) in order to be able to tell whether a remote UDP port is listening?

它调用 BSD Sockets connect() 方法。


首先,很明显这不是使用真正的Java实现的。"libcore.io"包不是 Java SE 库的一部分。这些是 Android 堆栈跟踪。 (这不会改变任何东西......但它可以。)

好的,让我们从异常开始。 java.net.PortUnreachableException 的 javadoc 说:

"Signals that an ICMP Port Unreachable message has been received on a connected datagram."

对于 DatagramSocket.connect(...):

"If the remote destination to which the socket is connected does not exist, or is otherwise unreachable, and if an ICMP destination unreachable packet has been received for that address, then a subsequent call to send or receive may throw a PortUnreachableException. Note, there is no guarantee that the exception will be thrown."

这就是我认为发生的事情。在创建 incoming 套接字之前,客户端系统上的某些东西已在该端口上向服务器发送了一个 UDP 数据包,并且服务器已响应 ICMP 端口不可达。然后创建并连接您的套接字,然后调用 receive。这会执行 recvfrom 系统调用,并且网络堆栈会以 ECONREFUSED 错误代码进行响应...Java 将其变成 PortUnreachableException,

这是否意味着 UDP 是面向连接的?

不是真的,IMO。它只是报告它收到一条 ICMP 消息以响应之前发生的事情。

connect 方法和"连接的套接字"/"连接的数据报"用语怎么样?

IMO,这只是一些笨拙的措辞。"连接"实际上只是指数据报套接字已绑定到特定远程地址和端口的事实......这样您就可以在不指定IP和端口1的情况下发送和接收数据报。

这些"连接"非常脆弱,当然不等于使 UDP"面向连接"。

What does Java do (without me asking it to) in order to be able to tell whether a remote UDP port is listening?

它什么也没做。 Java 只是从先前的 ICMP 消息中报告信息。

1 - 实际上,它的意义远不止于此。例如,绑定告诉客户端操作系统缓冲来自该主机/端口的 UDP 数据包,并将 UDP 数据包(和 ICMP 通知)路由到应用程序。它还告诉它不要以无法访问的 ICMP 端口进行响应。


UDP 服务器需要监听本地端口。

这是服务器的代码Stubbing。

1
2
3
4
5
6
7
8
9
10
11
12
13
int portNumber = 59123;
DatagramSocket server = new DatagramSocket(portNumber);

// read incoming packets
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while(true)
{
   server.receive(packet);
   byte[] data = packet.getData();
   String text = new String(data, 0, packet.getLength());

   echo(packet.getAddress().getHostAddress() +":" + packet.getPort() +" received: '" + text +"'");
}