Java UDP通信:DatagramSocket和DatagramPacket

来源:互联网 发布:网络黄金是什么 编辑:程序博客网 时间:2024/06/07 02:51

  UDP 是在IP网络上收发数据的传输层协议,其速度快但不可靠。为什么会使用这种不可靠的协议呢?许多应用保持最快的速度比保证每一位数据都正确更为重要,例如实时音频或视频丢失数据只会作为干扰出现并且可以容忍;另外一些应用,利用UDP数据传输可靠性就需要在应用中进行控制。DNS、NFS、TFTP等都可以配置使用UDP协议。
  我们都知道TCP是面向连接的,所谓连接就是在端点通信前建立起一条“虚电路”,利用逻辑标识来控制路由寻路,这个逻辑标识记录了目的IP、端口、虚电路标识等信息。UDP是无需连接,每个传输单元就称为数据包,数据包上就记录了相应的路由信息,且前后传输的数据包可不相关,这就要求各数据包中记录路由信息。而TCP建立好虚电路后,每个传输单元是不需要关心这些信息的。面向连接的TCP有诸多优点,如可靠性、拥塞可控等,但是其仅能用于端点间通信而不能用于广播或者组播通信,后两者还需要UDP发挥作用。
  Java用两个类实现UDP:DatagramPacket和DatagramSocket,前者将数据字节填充到UDP包,后者收发UDP包。用法很简单,DatagramSocket收发DatagramPacket即可。与TCP不同,UDP的socket并没有客户端和服务端的区别而统一应用此对象。下面给出利用UDP单播通信的daytime示意:

  • UDPServer.java daytime服务端
import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.SocketException;import java.net.SocketTimeoutException;import java.util.Date;/** * Created by Cheney Hwang on 2017/5/9. */public class UDPServer {    public final static int PORT = 5555;    public static void main(String[] args) {        byte[] buffer = new byte[64];        //侦听某个UDP端口        try (DatagramSocket server = new DatagramSocket(PORT)) {            server.setSoTimeout(10000);            while (true) {                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);                try {                    server.receive(packet);//10s超时接收                    byte[] now = new Date().toString().getBytes("US-ASCII");                    //组织数据,并传入请求方socket地址作为回应地址                    DatagramPacket outgoingPacket = new DatagramPacket(now, now.length, packet.getAddress(), packet.getPort());                    server.send(outgoingPacket);                } catch (SocketTimeoutException ex) {                    System.out.println("Timeout!");//超时                } catch (IOException ex) {                    ex.printStackTrace();                }            }        } catch (SocketException ex) {            ex.printStackTrace();        }    }}

  UDP向底层IP数据添加少部分内容:源端口、目的端口、IP数据部分长度和可选校验和,这些总共占用8字节。端口、地址、数据长度和数据等信息都可以从DatagramPacket中提取或者向其设置,以上UDPServer.java就是提取这些信息而获取客户端信息并作为回应地址的。UDP包中数据理论最大为65507(65535-20IP基本头-8UDP头),但实际上几乎总是比这个少得多,在很多平台上往往限制在8KB,因此某些程序依赖于发送超过8KB数据的UDP包要多加小心,大多数情况下更大的包都会被简单地截取位8KB数据,为保证最大的安全性,UDP数据部分尽量保持在512B以下。下面是与UDPServer.java通信的示例客户端:

  • UDPClient.java
import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.SocketTimeoutException;/** * Created by Cheney Hwang on 2017/5/9. */public class UDPClient {    public final static int PORT = 5555;    public final static String HOST = "localhost";    public static void main(String[] args) {        try (DatagramSocket socket = new DatagramSocket()) {//绑定端口由系统分配            //DatagramSocket依赖于数据报DatagramPacket收发数据,byte[1]是为了触发服务端            DatagramPacket packet = new DatagramPacket(new byte[1], 1, InetAddress.getByName(HOST), PORT);            socket.setSoTimeout(10000);//receive数据阻塞等待10s,超过这个时间仍未收到数据说明丢失了            socket.send(packet);            DatagramPacket incomePacket = new DatagramPacket(new byte[64], 64);            try {                socket.receive(incomePacket);//阻塞接收,10s超时                byte[] nowByte = new byte[incomePacket.getLength()];                System.arraycopy(incomePacket.getData(), 0, nowByte, 0, incomePacket.getLength());                String now = new String(nowByte, "US-ASCII");                System.out.println(now);            } catch (SocketTimeoutException ex) {                System.out.println("Timeout!");            }        } catch (IOException ex) {            return;        }    }}

  观察上面服务端和客户端的UDP通信示例,可以发现判断DatagramPacket是发数据还是收数据依据的是其构造差异。发数据在构造函数中需要指定目的Socket地址,而收数据并不需要(在生产环境中必须要考虑这边的安全策略)。类似地,DatagramSocket在默认构造时常用于客户端——其并不关心某个本地端口,由系统指定即可;而指定端口的构造常用于服务端,意义不言而喻。
  这边还有个UDP应用场景需要注意的问题,UDP服务器一般不会与客户端做太多的交互,通常可以在连接线程中直接响应客户端。不过如果需要做大量处理的话,可以创建处理线程来做此工作。

0 0
原创粉丝点击