Java的UDP相关

Java的UDP接收

1. 代码

1.1 UDP.java

UDP.java用于实现接收数据、开始和结束接收等。

代码如下:

//Edit at 180514:修改了同一个Thread重复使用带来的异常

package udputils;

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

public class UDP {
    private DatagramSocket udp = null;
    private UdpHandler mUdpHandler;
    private volatile boolean mRecFlag = false;
    private int mBufferLen = 4096;
    private Thread mRecThr;

    public void setmUdpHandler(UdpHandler mUdpHandler) {
        this.mUdpHandler = mUdpHandler;
    }

    public int getmBufferLen() {
        return mBufferLen;
    }

    public void setmBufferLen(int mBufferLen) {
        this.mBufferLen = mBufferLen;
    }

    public void setUdp(DatagramSocket udp) {
        this.udp = udp;
    }

    public void startReceive() {
        mRecFlag = true;
        mRecThr = new Thread(new Runnable() {

            public void run() {
                // TODO Auto-generated method stub
                if (udp != null && !udp.isClosed()) {
                    while (mRecFlag) {
                        byte receiveBuf[] = new byte[mBufferLen];
                        DatagramPacket inPacket = new DatagramPacket(receiveBuf, receiveBuf.length);

                        try {
                            udp.receive(inPacket);
                            if (mUdpHandler != null) {
                                mUdpHandler.doAfterReceive(inPacket);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                } else {
                    if (mUdpHandler != null) {
                        mUdpHandler.doWhenUdpIsNotSet();
                        mRecFlag = false;
                    }
                }
            }
        });
        mRecThr.start();  //WARNING:同一个线程启动两次,会出现异常IllegalThreadStateException
    }

    public void stopReceive() {
        mRecFlag = false;
        udp.close();
        try {
            mRecThr.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        udp = null;
    }

    public boolean isReceiving() {
        return mRecFlag;
    }
}

1.2 UdpHandler.java

本文件为回调接口,这里只预留了两个接口:接收到数据后的处理和DatagramSocket出错的接口。

代码如下:

package udputils;

import java.net.DatagramPacket;

public interface UdpHandler {
    public void doAfterReceive(DatagramPacket received);
    public void doWhenUdpIsNotSet();
}

1.3 调用方法

以下是测试该接收功能的java文件的内容:

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

import udputils.UDP;
import udputils.UdpHandler;

public class udpTest {

    private static volatile boolean isLoop = true;

    private static class MyUdpHandler implements UdpHandler {
        public void doAfterReceive(DatagramPacket received) {
            System.out.println("=============================================");
            System.out.println("Received data From Host: "
                    + received.getAddress().getHostAddress());
            System.out.println("Host Name: "
                    + received.getAddress().getHostName());
            System.out.println("Port: " + received.getPort());
            System.out.println("Data Length: " + received.getLength());
            // Manually get data...
            byte[] d = new byte[received.getLength()];
            System.arraycopy(received.getData(), 0, d, 0, received.getLength());
            String recStr = new String(d);
            System.out.println("Data: " + recStr);
            if (recStr.contains("close")) {
                isLoop = false;
            }
        }

        public void doWhenUdpIsNotSet() {
            // TODO Auto-generated method stub
            System.out.println("UDP Set Error!!");
        }
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        UDP u = new UDP();
        DatagramSocket ds;
        try {
            ds = new DatagramSocket(22493);
            u.setUdp(ds); // Set DatagramSocket
            u.setmUdpHandler(new MyUdpHandler()); // Set ReceiveHandler
            u.startReceive();
            System.out.println("Start Receive...");
            while (isLoop)
                ;
            u.stopReceive();
            System.out.println("Stop Receive!");
        } catch (SocketException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

如上所述,调用该UDP类进行UDP接收的步骤如下:

  1. 新建一个UDP类的实例:

    UDP u = new UDP();
  2. 新建一个进行UDP连接的DatagramSocket对象(根据不同需要调用不同的构造函数),使用setUdp()方法传入UDP对象:

    DatagramSocket ds;
    ds = new DatagramSocket(22493);  //This method should surrounded by "try ... catch ..."
    u.setUdp(ds); // Set DatagramSocket to UDP object.
  3. 新建一个内部类实现UdpHandler接口,并将该类的实例使用setmUdpHandler方法传入UDP对象中:

    private static class MyUdpHandler implements UdpHandler {
    public void doAfterReceive(DatagramPacket received) {
        System.out.println("=============================================");
        System.out.println("Received data From Host: "
                + received.getAddress().getHostAddress());
        System.out.println("Host Name: "
                + received.getAddress().getHostName());
        System.out.println("Port: " + received.getPort());
        System.out.println("Data Length: " + received.getLength());
        // Manually get data...
        byte[] d = new byte[received.getLength()];
        System.arraycopy(received.getData(), 0, d, 0, received.getLength());
        String recStr = new String(d);
        System.out.println("Data: " + recStr);
        if (recStr.contains("close")) {
            isLoop = false;
        }
    }
    
    public void doWhenUdpIsNotSet() {
        // TODO Auto-generated method stub
        System.out.println("UDP Set Error!!");
    }
    }
    
    ...
       u.setmUdpHandler(new MyUdpHandler()); // Set ReceiveHandler
    ...

    本接口的doAfterReceive()方法用于处理接收到的DatagramPacket对象。

  4. 使用如下方法开始或停止接收:

    开始接收:

    startReceive()

    停止接收:

    stopReceive()

    ==WARNING==

    stopReceive()停止接收后,UDP对象内的DatagramSocket对象会被置为null。

    因此,在再次开始接收前,需要==重新新建一个DatagramSocket对象并设置进入UDP对象中==。

2. 代码解析

在这里主要关注两个类:DatagramSocketDatagramPacket

2.1 DatagramSocket类

DatagramSocket类(下面简称DS)用于实现UDP协议的数据收发工作。

2.1.1 构造函数

DS类的构造函数有下列几种形式:

构造函数含义
DatagramSocket()创建一个DatagramSocket对象
并将该对象绑定到本机默认IP地址、本机所有可用端口中随机选择的某个端口。
DatagramSocket(int port)创建一个DatagramSocket对象
并将该对象绑定到本机默认IP地址指定端口。
DatagramSocket(int port,InetAddress laddr)创建一个DatagramSocket对象
并将该对象绑定到指定IP地址指定端口。

根据实际情况选择需要使用的构造函数即可。

例如在本例中使用了指定端口号的构造函数。

2.1.2 发送与接收数据

在建立了DS对象后,可以通过如下方法来接受/发送数据:

receive(DatagramPacket p):从该DatagramSocket中接收数据报。

send(DatagramPacket p):以该DatagramSocket对象向外发送数据报。

注意:

​ 这里的发送、接收方法均为耗时操作(`receive方法是阻塞方法),因此请合理使用多线程编程。

在这里,DS对象只提供了一个数据发送/接收的平台,不关心接受到的数据从哪里来,也不关心要发送的数据的目的地。

这些信息都在DatagramPacket 中。

2.1.3 关闭DS对象

可使用close()方法关闭DS对象。

注意:

DS对象执行了close()方法后,无法再重新打开。

​ 根据Javadoc,任何堵塞DS对象的receive()方法上的线程,在该DS对象执行了close()方法后,会抛出异常SocketException。

2.2 DatagramPacket类

DP类是实际发送、接收时涉及的数据包对象。

该对象除了要传输的数据外,还包含了传输的目的地/来源。

2.2.1 构造函数
构造函数含义
DatagramPacket(byte[] buf,int length)以一个空数组来创建用于 接收 DatagramSocket中的数据的DatagramPacket对象
数据最大长度为length
DatagramPacket(byte[] buf, int offset, int length)以一个空数组来创建用于 接收 DS中的数据的DP对象
将数据放入数组时从数组的offset处开始
数据最大长度为length
DatagramPacket(byte[] buf, int length, InetAddress addr, int port)以一个包含数据的数组来创建用于发送的DP对象
在创建用于发送的DP对象时,需要指定目的IP地址和端口
发送数据长度为length
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)同上,参数offset说明要发送的数据在buf数组里的起始位置
2.2.2 获取数据、IP地址

InetAddress getAddress():当程序准备发送此数据报时,该方法返回此数据报的目标机器的IP地址;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的IP地址。

int getPort():当程序准备发送此数据报时,该方法返回此数据报的目标机器的端口;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的端口。

SocketAddress getSocketAddress():当程序准备发送此数据报时,该方法返回此数据报的目标SocketAddress;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的SocketAddress。

int getLength():接收到数据的长度。

byte[] getData():获取DP类里的字节数组对象。注意:此处获取到数组的长度不一定是接收到数据的长度,需要配合getLength()方法得到实际的数据内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值