Java网络:UDP DatagramSocket
DatagramSocket是Java的通过UDP(而非TCP)进行网络通信的机制。 UDP仍位于IP之上。我们可以使用Java的DatagramSocket来发送和接收UPD数据报。
UDP与TCP
UDP的工作原理与TCP略有不同。通过TCP发送数据时,首先要创建一个连接。一旦建立了TCP连接,TCP就会保证数据到达另一端,否则它将告诉我们发生了错误。
使用UDP,我们只需将数据包(数据报)发送到网络上的某个IP地址。我们不能保证数据会到达。我们也无法保证UDP数据包到达接收方的顺序。这意味着UDP比TCP具有更少的协议开销(无流完整性检查)。
UDP适用于数据传输,在传输过程中是否丢失数据包并不重要。例如,想象一下通过互联网传输实况电视信号。我们希望信号尽可能接近现场。因此,如果丢失了一两帧,则不必担心。我们不希望延迟直播,只是确保所有帧都在客户端显示。我们宁愿跳过错过的帧,并始终直接移至最新的帧。
监视摄像机通过互联网广播也可能是这种情况。当我们尝试监视当前时,谁会关心过去发生的事情。我们不想只因为要向监视相机的人显示所有画面而最终落后现实30秒。摄像机记录的存储有些不同。将图像从相机录制到磁盘时,我们可能不想丢掉一帧。我们可能希望稍微延迟一下,而不要让那些帧退回去检查是否发生了重要的事情。
通过DatagramSocket发送数据
要通过Java的DatagramSocket发送数据,我们必须首先创建一个DatagramPacket。这是完成的方式:
byte[] buffer = new byte[65508]; InetAddress address = InetAddress.getByName("Hyman.com"); DatagramPacket packet = new DatagramPacket( buffer, buffer.length, address, 9000);
字节缓冲区(字节数组)是要在UDP数据报中发送的数据。上述缓冲区的长度为65508字节,是我们可以在单个UDP数据包中发送的最大数据量。
给DatagramPacket构造函数的长度是要发送的缓冲区中数据的长度。在该数量的数据之后,缓冲区中的所有数据都将被忽略。
InetAddress实例包含要将UDP数据包发送到的节点(例如服务器)的地址。 " InetAddress"类代表IP地址(Internet地址)。 getByName()方法返回一个InetAddress实例,该实例的IP地址与给定的主机名匹配。
port参数是服务器要接收其列出数据的UDP端口。 UDP和TCP端口不相同。一台计算机可以有不同的进程进行监听,例如同时在UDP和TCP中使用端口80。
要发送" DatagramPacket",我们必须创建一个旨在发送数据的" DatagramSocket"。这是完成的方式:
DatagramSocket datagramSocket = new DatagramSocket();
要发送数据,请调用send()
方法,如下所示:
datagramSocket.send(packet);
这是一个完整的示例:
DatagramSocket datagramSocket = new DatagramSocket(); byte[] buffer = "0123456789".getBytes(); InetAddress receiverAddress = InetAddress.getLocalHost(); DatagramPacket packet = new DatagramPacket( buffer, buffer.length, receiverAddress, 80); datagramSocket.send(packet);
通过DatagramSocket接收数据
通过" DatagramSocket"接收数据的方法是先创建一个" DatagramPacket",然后通过" DatagramSocket"的" receive()"方法将数据接收到其中。这是一个例子:
DatagramSocket datagramSocket = new DatagramSocket(80); byte[] buffer = new byte[10]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); datagramSocket.receive(packet);
注意,如何使用传递给其构造函数的参数值80来实例化" DatagramSocket"。这个参数是UDP端口,DatagramSocket
用来接收UDP数据包。如前所述,TCP和UDP端口不相同,因此不会重叠。我们可以有两个不同的进程在TCP和UDP端口80上侦听,而不会发生任何冲突。
其次,创建一个字节缓冲区和一个" DatagramPacket"。请注意," DatagramPacket"如何不具有有关要向其发送数据的节点的信息,就像创建" DatagramPacket"来发送数据时一样。这是因为我们将使用DatagramPacket来接收数据,而不是发送数据。因此,不需要目的地地址。
最后,调用DatagramSocket的receive()方法。该方法将阻塞,直到接收到" DatagramPacket"。
接收到的数据位于" DatagramPacket"的字节缓冲区中。可以通过调用以下方法获得此缓冲区:
byte[] buffer = packet.getData();
我们需要确定缓冲区中接收了多少数据。我们使用的协议应指定每个UDP数据包发送多少数据,或者指定我们可以查找的数据结束标记。
真正的服务器程序可能会在循环中调用receive()
方法,并将所有接收到的DatagramPacket
传递给工作线程池,就像TCP服务器处理传入连接一样。