Java实现UDP通信

/ 网络编程 / 0 条评论 / 617浏览

UDP

UDP是一种面向无连接的协议,因此,在通信时发送端和接收端不用建立连接。UDP通信的过程就像快递快递公司投递一样,从地点A向地点B运输快递,我们要传输的数据就是这些快递包裹,在运输过程中需要包装盒或者集装箱等来对包裹进行包装再运输,UDP通信的过程需要对数据进行打包,然后再从一个地方发送到另一个地方。

java.net.DatagramPacket

在UDP通信的过程中,我们需要一个打包盒来对数据进行封装,jdk中提供DatagramPacket类来对数据进行封装,按照常规,下面来简单看一下这个类的基本信息:

继承结构:

java.lang.Object
java.net.DatagramPacket

类定义:

public final class DatagramPacket
extends Object

构造方法:

public DatagramPacket(byte[] buf, int length)
public DatagramPacket(byte[] buf, int length, InetAddress address, int port)
public DatagramPacket(byte[] buf, int offset, int length)
public DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
public DatagramPacket(byte[] buf, int length, SocketAddress address)
public DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)

主要方法均为常规的getter和setter方法(所有成员函数为同步方法),其成员函数如下:

/*
 * The fields of this class are package-private since DatagramSocketImpl
 * classes needs to access them.
 */
byte[] buf; // 数据缓冲区
int offset; //  数据偏移量
int length; //  数据长度
int bufLength;  //  缓冲区长度
InetAddress address;    //  通信机器的ip,从这台机器接收数据或将要往这台机器发送
int port;   //  通信机器的端口,从这台机器接收数据或将要往这台机器发送

java.net.DatagramSocket

在运输货物的时候需要中转站或者运送工具,在通信中也可以说是基站,那么在UDP通信时也需要这样的“基站”,jdk提供了DatagramSocket来起到“基站”的作用。先来看看这个类的基本信息:

继承结构:

java.lang.Object
java.net.DatagramSocket

类定义:

public class DatagramSocket
extends Object
implements Closeable

构造方法:

public DatagramSocket() throws SocketException
protected DatagramSocket(DatagramSocketImpl impl)
public DatagramSocket(int port) throws SocketException
public DatagramSocket(int port, InetAddress laddr) throws SocketException
public DatagramSocket(SocketAddress bindaddr) throws SocketException

主要方法(不包括普通的getter和setter方法):

判断套接字是否关闭:public boolean isClosed()
接收数据报:public void receive(DatagramPacket p) throws IOException
发送数据报:public void send(DatagramPacket p) throws IOException
关闭套接字:public void close()
将数据报绑定到特定的地址和端口:public void bind(SocketAddress addr) throws SocketException
断开套接字,停止传输:public void disconnect()

UDP通信代码实现

发送端

下面代码运用上面的两个类来进行实现发送数据:

package language;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;

/**
 * @Author beifengtz
 * @Site www.beifengtz.com
 * @Date Created in 22:26 2019/2/1
 * @Description:
 */
public class UDPSender {
    public static void main(String[] args) throws Exception{
        byte[] data = "你好啊!这是由UDP通信发送过来的数据。".getBytes();    //  发送的数据内容
        InetAddress address = Inet4Address.getByName("localhost");          //  接收方地址
        int port = 9999;        //  接收方端口
        DatagramPacket datagramPacket = new DatagramPacket(data,data.length,address,port);  //  创建数据报,对数据进行打包
        DatagramSocket datagramSocket = new DatagramSocket();   //  建立传输套接字(建立“基站”)
        datagramSocket.send(datagramPacket);    //  发送数据报
        datagramSocket.close(); //  关闭流
    }
}

接收端

接收UDP数据报的代码如下:

package language;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * @Author beifengtz
 * @Site www.beifengtz.com
 * @Date Created in 23:03 2019/2/1
 * @Description:
 */
public class UDPReceiver {
    public static void main(String[] args) throws Exception{
        DatagramSocket datagramSocket = new DatagramSocket(9999);       //  建立“基站”接收9999端口传来的数据报
        System.out.println("开始准备接收数据,监听端口:9999");
        byte[] buf = new byte[1024];    //  创建接收数据的缓存区
        DatagramPacket datagramPacket = new DatagramPacket(buf,buf.length); //  创建接收数据的容器
        datagramSocket.receive(datagramPacket); //  开始接收数据,并将数据装入容器
        System.out.println("接收来自:"+datagramPacket.getSocketAddress()+"的数据");
        System.out.println("接收到的数据内容:"+new String(datagramPacket.getData(),0,datagramPacket.getLength()));  //  将接收到的数据转为字符串打印,因为前面传的也是字符串所以这里直接转换,如果在正式使用中是其他数据可以由字节数据进行转换
        System.out.println("接收到的数据长度:"+datagramPacket.getLength());
        System.out.println("发送方的端口:"+datagramPacket.getPort());
        datagramSocket.close(); //  关闭流
    }
}

最后测试的输出结果如下:

开始准备接收数据,监听端口:9999
接收来自:/127.0.0.1:56665的数据
接收到的数据内容:你好啊!这是由UDP通信发送过来的数据。
接收到的数据长度:54
接收的端口:56665

当然上面的程序只是能接收一次并且只能发送一次,但是实际情况中接收方都要实时监听着端口,并且要有能力处理多个发送方发过来的数据,所以需要对接收方进行改进,创建多线程来进行处理数据。

package language;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * @Author beifengtz
 * @Site www.beifengtz.com
 * @Date Created in 23:03 2019/2/1
 * @Description:
 */
public class UDPReceiver {
    static class MyThread implements Runnable{
        private DatagramSocket datagramSocket;
        private DatagramPacket datagramPacket;
        public MyThread(DatagramSocket datagramSocket,DatagramPacket datagramPacket){
            this.datagramSocket = datagramSocket;
            this.datagramPacket = datagramPacket;
        }

        @Override
        public void run() {
            try {
                System.out.println("接收来自:"+datagramPacket.getSocketAddress()+"的数据");
                System.out.println("接收到的数据内容:"+new String(datagramPacket.getData(),0,datagramPacket.getLength()));  //  将接收到的数据转为字符串打印,因为前面传的也是字符串所以这里直接转换,如果在正式使用中是其他数据可以由字节数据进行转换
                System.out.println("接收到的数据长度:"+datagramPacket.getLength());
                System.out.println("发送方的端口:"+datagramPacket.getPort());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Thread.currentThread().interrupt();
            }
        }
    }
    public static void main(String[] args) throws Exception{
        DatagramSocket datagramSocket = new DatagramSocket(9999);       //  建立“基站”接收9999端口传来的数据报
        System.out.println("开始准备接收数据,监听端口:9999");
        boolean flag = true;
        while (flag){
            byte[] buf = new byte[1024];    //  创建接收数据的缓存区
            DatagramPacket datagramPacket = new DatagramPacket(buf,buf.length); //  创建接收数据的容器
            datagramSocket.receive(datagramPacket); //  开始接收数据,并将数据装入容器
            new Thread(new MyThread(datagramSocket,datagramPacket)).start();   //  每当接收一个数据就创建一个新的线程去处理
        }
        datagramSocket.close(); //  关闭流
        System.out.println("接收服务已关闭");
    }
}