UDP Socket编程

Java提供了对 TCP Socket 与 UDP Socket 的支持。TCP Socket 可以查看之前的一篇文章(TCP Socket编程)。

相比TCP,UDP是无连接的,只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份,因此UDP应用一般允许一定量的丢包、出错。但有些应用,如TFTP,如果需要则必须在应用层增加可靠机制。

绝大多数UDP应用都不需要可靠机制,甚至可能因为引入可靠机制而降低性能。流媒体、即时多媒体游戏和IP电话就是典型的UDP应用。如果某个应用需要很高的可靠性,可以用TCP来代替UDP。

Java通过 DatagramSocket 类提供对 UDP Socket 的支持,用 DatagramPacket 类包装发送或者接收的数据。

客户端UDP通信建立步骤:

  • 创建 DatagramSocket 实例,如果设置了端口号,则客户端会在该端口号上监听从服务器端发送的数据,否则会随机选择一个端口
  • 使用 DatagramSocket 的 send() 和 receive() 方法发送和接收 DatagramPacket 实例
  • 通信完成后,调用 DatagramSocket 实例的 close() 方法关闭套接字
UDP服务器为所有通信使用同一套接字,这与TCP服务器不同,TCP服务器会为每个成功返回的 accept() 方法创建一个新的套接字。

UDP Socket Demo

Server端:

public class UDPServer {

    private static byte[] temp = new byte[1024];
    private static String strSend = "hello,Client!";

    public static void server() throws IOException {
        //监听4445端口
        DatagramSocket socket = new DatagramSocket(4445);
        System.out.println("Server is ready to receive data!");
        DatagramPacket servRecv = new DatagramPacket(temp, 1024);
        boolean flag = true;
        while (flag) {
            //接收从客户端发送的消息
            socket.receive(servRecv);
            String strRecv = new String(servRecv.getData(), 0, servRecv.getLength());
            System.out.printf("%9s%s%n", "Receive: ", strRecv);
            //向客户端发送消息
            DatagramPacket servSend = new DatagramPacket(strSend.getBytes(), strSend.length(),
                    InetAddress.getLocalHost(), 3000);
            socket.send(servSend);
            System.out.printf("%9s%s%n", "Send: ", strSend);
            //重新设置缓冲数组大小
            servRecv.setLength(1024);
        }
        socket.close();
    }

    public static void main(String[] args) throws IOException {
        server();
    }
}

Client端:

public class UDPClient {

    private static final int TIMEOUT = 5000;

    public static String client(String str) throws IOException {
        // 监听3000端口
        DatagramSocket client = new DatagramSocket();
        byte[] temp = new byte[1024];
        // 设置超时时间
        client.setSoTimeout(TIMEOUT);
        // 向服务器的4445端口发送消息
        DatagramPacket ctSend = new DatagramPacket(str.getBytes(), str.length(), InetAddress.getLocalHost(), 4445);
        // 接收服务器端发送的消息
        DatagramPacket ctRecv = new DatagramPacket(temp, 1024);
        String strRecvd = null;
        boolean flag = true;
        while (flag) {
            System.out.println("Client is ready to send data!");
            client.send(ctSend);
            System.out.printf("%9s%s%n", "Send: ", str);
            try {
                //receive方法是阻塞的
                client.receive(ctRecv);
                String strRecv = new String(ctRecv.getData(), 0, ctRecv.getLength());
                System.out.printf("%9s%s%n", "Receive: ", strRecv);
                flag = false;
                strRecvd = new String(ctRecv.getData(), 0, ctRecv.getLength());
            } catch (SocketTimeoutException e) {
                System.out.println("服务器无响应!");
            }
        }
        client.close();
        return strRecvd;
    }

    public static void main(String[] args) throws IOException {
        client("hello,Server!");
    }
}

输出结果:

Server is ready to receive data!
Receive: hello,Server!
   Send: hello,Client!
Client is ready to send data! 
     Send: hello,Server! 
Receive: hello,Client!

注意:需要先启动Server,再启动Client


UDP消息边界

TCP采用面向流的传输,没有消息保护边界,如果发送端连续发送数据,接收端有可能在一次接收动作中接收两个或者更多的数据包。但是,对于数据传输频繁的程序来讲,使用TCP可能会容易粘包。

UDP采用面向消息的传输,存在消息保护边界,接收端一次只能接收发送端发出的一个数据包,因此UDP没有粘包问题。但是当发送数据量较小时,会增加发送和接收开销。因此,最好设置一个大小比较合适的数据包进行UDP数据的发送。


UDP数据包长度

从UDP数据报的报头可以看出,UDP的最大数据报长度是 2 16 − 1 2^{16}-1 2161个字节。由于UDP报头占8个字节,所以数据部分(数据包)的最大理论长度是 2 16 − 1 − 8 = 65527 2^{16}-1-8 = 65527 21618=65527

但是在传输过程中,UDP数据报是作为下层协议的数据字段进行传输的,它的长度受到下层IP层和数据链路层协议的制约。

IP数据报的最大长度与UDP相同,IP报头占20个字节,因此在IP层,UDP数据包的最大理论长度为65527-20 = 65507。当一个IP数据报封装成链路层的帧时,此数据报的总长度不能超过链路层规定的MTU(最大传送单元)值,最常用的以太网的MTU值是1500字节。若所传送的数据报长度超过此MTU值,会进行分片处理。

鉴于Internet上的标准MTU值为576字节,所以在进行Internet的UDP编程时,最好将UDP的数据长度控制在504字节(去除最长的IP首部60字节、4字节的富余量、UDP首部8字节)以内。

参考资料:

  • UDP Socket 编程
  • 浅谈UDP(数据包长度,收包能力,丢包及进程结构选择)
  • UDP包的边界
  • 维基百科—数据报协议
  • 计算机网络(第七版)- 谢希仁

你可能感兴趣的:(Protocol,Java)