基于UDP协议的网络编程

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u014285882/article/details/89791092

UDP协议

UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。
UDP协议是面向非连接的协议,没有建立连接的过程, 因此通信效率很高。

它在通信实例两端各建立一个Socket, 这两个Socket之间没有虚拟链路, 这两个Socket知识发送,接收数据报的对象。
Java提供了 DatagramSocket对象作为UDP协议的Socket。

DatagramSocket

DatagramSocket本身只是码头,不维护状态, 不能产生IO流, 作用是接收和发送数据报, 使用 DatagramPacket代表数据报文。

DatagramSocket 的构造器:
DatagramSocket():创建一个实例, 绑定到本机默认IP, 本机可用端口中随机选择一个端口。
DatagramSocket(int port): 本机默认IP, 指定端口
DatagramSocket(int port, InetAddress addr): 绑定到指定ip,port。

receive(DatagramPacket p): 接收数据
send(DatagramPacket p): 发送数据

DatagramPacket 的构造器, 指定一个空数组, 长度,字节数, 还可以指定ip地址和端口
服务端接收到 DatagramPacket 时,可以通过下面方法获取ip和port
InetAddress getAddress() : 发送时,返回目标地址, 接收时, 返回发送主机ip
int getPort(): 返回 port
SocketAddress getSocketAddress(): 返回SocketAddress, 包括ip和port。

Server端:

public class UdpServerTest {
    private int port = 9002;
    //每个报文大小为4KB
    private int dataLength = 4096;
    //接收数据的数组
    byte[] inBuff = new byte[dataLength];
    //接收数据对象
    private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
    //发送数据对象
    private DatagramPacket outPacket;

    public void init() throws IOException{
        try(
                DatagramSocket socket = new DatagramSocket(port);
                ){
            for (int i = 0; i < 100; i++){
                //接收
                socket.receive(inPacket);
                // inPackage.getData 和 inBuff 是否是同一个数组
                System.out.println(inBuff == inPacket.getData());
                // 接收数据打印
                System.out.println(new String(inBuff, 0, inPacket.getLength()));

                byte[] sendData = "udp server".getBytes();
                //发送数据
                outPacket = new DatagramPacket(sendData, sendData.length, inPacket.getSocketAddress());
                socket.send(outPacket);
            }

        }
    }

    public static void main(String[] args) throws IOException{
        new UdpServerTest().init();
    }
}

client 端

public class UdpClientTest {
    private int port = 9002;
    private int dataLength = 4096;
    byte[] inBuff = new byte[dataLength];
    private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
    private DatagramPacket outPacket;

    public void init() throws IOException {
        try(
                DatagramSocket socket = new DatagramSocket();
        ){
            outPacket = new DatagramPacket(new byte[0], 0, InetAddress.getByName("127.0.0.1") ,port);
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()){
                byte[] buff = scanner.nextLine().getBytes();
                outPacket.setData(buff);
                socket.send(outPacket);
                socket.receive(inPacket);

                System.out.println(new String(inBuff, 0, inPacket.getLength()));
            }

        }
    }

    public static void main(String[] args) throws IOException{
        new UdpClientTest().init();
    }
}

MulticastSocket

DatagramSocket只允许数据报文发送给指定的目标地址, 而MulticastSocket可以将数据报以广播方式发送到多个客户端。

IP协议为多点广播提供了这批特殊的IP地址,这些IP地址的范围是:224.0.0.0至239.255.255.255.

public
class MulticastSocket extends DatagramSocket {
    /**
     * Create a multicast socket.
     *
     * <p>If there is a security manager,
     * its {@code checkListen} method is first called
     * with 0 as its argument to ensure the operation is allowed.
     * This could result in a SecurityException.
     * <p>
     * When the socket is created the
     * {@link DatagramSocket#setReuseAddress(boolean)} method is
     * called to enable the SO_REUSEADDR socket option.
     *
     * @exception IOException if an I/O exception occurs
     * while creating the MulticastSocket
     * @exception  SecurityException  if a security manager exists and its
     *             {@code checkListen} method doesn't allow the operation.
     * @see SecurityManager#checkListen
     * @see java.net.DatagramSocket#setReuseAddress(boolean)
     */
    public MulticastSocket() throws IOException {
        this(new InetSocketAddress(0));
    }

    /**
     * Create a multicast socket and bind it to a specific port.
     *
     * <p>If there is a security manager,
     * its {@code checkListen} method is first called
     * with the {@code port} argument
     * as its argument to ensure the operation is allowed.
     * This could result in a SecurityException.
     * <p>
     * When the socket is created the
     * {@link DatagramSocket#setReuseAddress(boolean)} method is
     * called to enable the SO_REUSEADDR socket option.
     *
     * @param port port to use
     * @exception IOException if an I/O exception occurs
     * while creating the MulticastSocket
     * @exception  SecurityException  if a security manager exists and its
     *             {@code checkListen} method doesn't allow the operation.
     * @see SecurityManager#checkListen
     * @see java.net.DatagramSocket#setReuseAddress(boolean)
     */
    public MulticastSocket(int port) throws IOException {
        this(new InetSocketAddress(port));
    }

    /**
     * Create a MulticastSocket bound to the specified socket address.
     * <p>
     * Or, if the address is {@code null}, create an unbound socket.
     *
     * <p>If there is a security manager,
     * its {@code checkListen} method is first called
     * with the SocketAddress port as its argument to ensure the operation is allowed.
     * This could result in a SecurityException.
     * <p>
     * When the socket is created the
     * {@link DatagramSocket#setReuseAddress(boolean)} method is
     * called to enable the SO_REUSEADDR socket option.
     *
     * @param bindaddr Socket address to bind to, or {@code null} for
     *                 an unbound socket.
     * @exception IOException if an I/O exception occurs
     * while creating the MulticastSocket
     * @exception  SecurityException  if a security manager exists and its
     *             {@code checkListen} method doesn't allow the operation.
     * @see SecurityManager#checkListen
     * @see java.net.DatagramSocket#setReuseAddress(boolean)
     *
     * @since 1.4
     */
    public MulticastSocket(SocketAddress bindaddr) throws IOException {
        super((SocketAddress) null);

        // Enable SO_REUSEADDR before binding
        setReuseAddress(true);

        if (bindaddr != null) {
            try {
                bind(bindaddr);
            } finally {
                if (!isBound())
                    close();
            }
        }
    }

有上面三个构造函数。
创建对象后,要加入多点广播地址,有下面方法:
joinGroup(): 加入组
leaveGroup() : 离开组
有些系统中, 可能有多个网络接口, 使用 setInterface() 设置指定接口, getInterface() 获取接口。

MulticastSocket 多一个 setTimeToLive() 方法。 设置最多跨越多少个网络
ttl :
0: 本地主机
1: 本地局域网, 默认值
32: 本站点网络
64: 本地区
128: 本大洲
255:所有地方

public class MulticastSocketTest implements Runnable {

    //使用常量作为本程序多点广播的IP地址
    private static final String BROADCAST_IP = "230.0.0.1";
    //使用常量作为本程序的多点广播的目的地端口
    public static final int BROADCAST_PORT = 3000;
    //定义每个数据报大小最大为4kb
    private static final int DATA_LEN = 4096;
    //定义本程序的MulticastSocket实例
    private MulticastSocket socket = null;
    private InetAddress broadcastAddress = null;
    private Scanner scan = null;

    //定义接收网络数据的字节数组
    byte[] inBuff = new byte[DATA_LEN];
    //以指定字节数组创建准备接收数据的MulticastSocket对象
    private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);

    //定义一个用于发送的DatagramPacket对象
    private DatagramPacket outPacket = null;

    public void init() throws IOException {
        //创建键盘输入流
        Scanner scan = new Scanner(System.in);
        //创建用于发送、接收数据的MulticastSocket对象,由于该MulticastSocket需要接收数据,所以有指定端口
        socket = new MulticastSocket(BROADCAST_PORT);
        broadcastAddress = InetAddress.getByName(BROADCAST_IP);

        //将该socket加入到指定的多点广播地址
        socket.joinGroup(broadcastAddress);
        //设置本MulticastSocket发送的数据报会被回送到自身
        socket.setLoopbackMode(false);

        //初始化发送用的DatagramSocket,它包含一个长度为0的字节数组
        outPacket = new DatagramPacket(new byte[0], 0, broadcastAddress, BROADCAST_PORT);

        //启动本实例的run()方法作为线程执行体的线程
        new Thread(this).start();

        //不断的读取键盘输入
        while (scan.hasNextLine()) {
            //将键盘输入的一行字符转换成字节数组
            byte[] buff = scan.nextLine().getBytes();
            //设置发送用的DatagramPacket里的字节数据
            outPacket.setData(buff);
            //发送数据报
            socket.send(outPacket);
        }
        socket.close();
    }


    public void run() {

        while (true) {
            //读取Socket中的数据,读到的数据放入inPacket所封装的字节组里
            try {
                socket.receive(inPacket);
                //打印从socket读取到的内容
                System.out.println("聊天信息:" + new String(inBuff, 0, inPacket.getLength()));
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            if (socket != null) {
                //让该socket离开多点IP广播地址
                try {
                    socket.leaveGroup(broadcastAddress);
                    //关闭socket对象
                    socket.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }

            System.exit(1);
        }
    }

    public static void main(String[] args) {
        try {
            new MulticastSocketTest().init();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
展开阅读全文

没有更多推荐了,返回首页