java中网络编程之socket编程(四)--udp同步非阻塞式

一、简介
同tcp通信一样,udp通信也有同步非阻塞式,可同时发送和读取数据,与多个客户端通信。
二、关键类
1、java.net.DatagramSocket
报文socket,同udp阻塞通信一样。
2、java.nio.channels.DatagramChannel
报文通道,可同时与多个客户端通信,常用方法有:
DatagramChannel.open():静态方法,生成DatagramChannel;
configureBlocking(boolean block):指定通道是否为阻塞模式,默认为阻塞式;
connect(SocketAddress remote):虚拟连接目标地址,此时只能向指定地址通信,实际udp通信时不事先建立连接;
disconnect():释放虚拟链接,通道又可与多个地址通信;
write(ByteBuffer src):发送数据,只适用于建了虚拟连接的channel;
send(ByteBuffer src, SocketAddress target):发送数据,同时指定目标地址;
receive(ByteBuffer dst):接收数据;
read(ByteBuffer dst):接收数据,只适用于建了虚拟连接的channel;
3、java.nio.channels.Selector
事件监听器,同tcp中一样;
4、java.nio.channels.SelectionKey
事件,同tcp中一样;
三、示例
1、服务器代码

import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class UdpNioServer {
    private static Charset charset = Charset.forName("utf8");

    public static void main(String[] args) throws Exception {
        //创建channel
        DatagramChannel channel = DatagramChannel.open();
        //指定为非阻塞方式
        channel.configureBlocking(false);
        DatagramSocket socket = channel.socket();
        //绑定ip和端口
        InetSocketAddress address = new InetSocketAddress(7001);
        socket.bind(address);

        //创建监听器
        Selector selector = Selector.open();
        //注册读事件
        channel.register(selector, SelectionKey.OP_READ);

        //记录前一客户端地址,用于新起发送线程,仅示例,实际中用map等方式标记
        String preClientAddress = "";

        //读缓冲
        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
        while (true) {
            //等事件出现
            if (selector.select() < 1) {
                continue;
            }

            //获取发生的事件
            Set keys = selector.selectedKeys();
            Iterator it = keys.iterator();
            while (it.hasNext()) {
                //获取事件,移除正在处理的事件
                SelectionKey key = it.next();
                it.remove();

                //读取消息
                if(key.isReadable()){
                    DatagramChannel datagramChannel = (DatagramChannel) key.channel();
                    readBuffer.clear();
                    SocketAddress sa = datagramChannel.receive(readBuffer);
                    //新建发送消息线程
                    if(!preClientAddress.equals(sa.toString())){
                        new WriteThread(channel, sa).start();
                        preClientAddress = sa.toString();
                    }
                    readBuffer.flip();
                    String msg = charset.decode(readBuffer).toString();
                    System.out.println("server receive msg : " + msg);

                }
            }
        }
    }


    /**
     * 发送消息线程
     */
    public static class WriteThread extends Thread {
        //channel
        private DatagramChannel channel;
        //客户端地址
        private SocketAddress socketAddress;

        public WriteThread(DatagramChannel channel, SocketAddress socketAddress) {
            this.channel = channel;
            this.socketAddress = socketAddress;
        }

        @Override
        public void run() {
            try {
                //写缓冲
                ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                Scanner scanner = new Scanner(System.in);
                while (true) {
                    System.out.print("server send msg:");
                    String msg = scanner.nextLine();

                    //消息写入缓冲区,并发送消息
                    writeBuffer.put(msg.getBytes());
                    writeBuffer.flip();
                    channel.send(writeBuffer, socketAddress);

                    //发送完后,清空缓冲区
                    writeBuffer.clear();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

2、客户端代码

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class UdpNioClient {
    private static Charset charset = Charset.forName("utf8");

    public static void main(String[] args) throws Exception {
        //创建channel
        DatagramChannel channel = DatagramChannel.open();
        //指定为非阻塞方式
        channel.configureBlocking(false);

        //指定发送目的地
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 7001);
        //虚拟连接,实际没有连接,仅绑定目的地,也可不绑定,在实际发送指定目标地址
        channel.connect(address);

        //新起发送消息线程
        new UdpNioClient.WriteThread(channel).start();

        //创建监听器
        Selector selector = Selector.open();
        //注册读事件
        channel.register(selector, SelectionKey.OP_READ);
        //读缓冲
        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
        while (true) {
            //等事件出现
            if (selector.select() < 1) {
                continue;
            }

            //获取发生的事件
            Set keys = selector.selectedKeys();
            Iterator it = keys.iterator();
            while (it.hasNext()) {
                //获取事件,移除正在处理的事件
                SelectionKey key = it.next();
                it.remove();

                //读取消息
                if (key.isReadable()) {
                    readBuffer.clear();
                    channel.receive(readBuffer);
                    readBuffer.flip();
                    String msg = charset.decode(readBuffer).toString();
                    System.out.println("receive msg : " + msg);

                }
            }
        }
    }


    /**
     * 发送消息线程
     */
    public static class WriteThread extends Thread {
        private DatagramChannel channel;

        public WriteThread(DatagramChannel channel) {
            this.channel = channel;
        }

        @Override
        public void run() {
            try {
                //写缓冲
                ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                Scanner scanner = new Scanner(System.in);
                while (true) {
                    System.out.print("client send msg:");
                    String msg = scanner.nextLine();

                    //消息写入缓冲区,并发送消息
                    writeBuffer.put(msg.getBytes());
                    writeBuffer.flip();
                    //发送消息,发送时还可指定目的地址channel.send(writeBuffer, socketAddress);
                    channel.write(writeBuffer);

                    //发送完后,清空缓冲区
                    writeBuffer.clear();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

 

你可能感兴趣的:(java)