Java-NIO非阻塞式网络通信

使用 NIO 完成网络通信的三个核心:

  1. 通道(Channel):负责连接

    java.nio.channels.Channel 接口:
     	|--SelectableChannel
     		|--SocketChannel
     		|--ServerSocketChannel
     		|--DatagramChannel
    
     		|--Pipe.SinkChannel
     		|--Pipe.SourceChannel
    
  2. 缓冲区(Buffer):负责数据的存取

  3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况


基于通道的普通阻塞式网络通信演示

import org.junit.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class NetSocketTest {
    @Test
    public void client(){
        SocketChannel sc = null;
        FileChannel fc = null;
        try {
            sc = SocketChannel.open(new InetSocketAddress("192.168.0.102",18888));
            fc = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
            ByteBuffer buf = ByteBuffer.allocate(1024*1024);
            while (fc.read(buf)!=-1){
                buf.flip();
                sc.write(buf);
                buf.clear();
            }
            sc.shutdownOutput();
        } catch (IOException e) {
        }finally {
            try {
                fc.close();
                sc.close();
            } catch (IOException e) {
            }
        }
    }
    @Test
    public void Server(){
        ServerSocketChannel ssc = null;
        FileChannel fc = null;
        ServerSocketChannel sscPort = null;
        try {
            ssc = ServerSocketChannel.open();
            sscPort = ssc.bind(new InetSocketAddress(18888));
            fc = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE);
            ByteBuffer buf =ByteBuffer.allocate(1024*1024);
				
			//阻塞式方法
            SocketChannel accept = sscPort.accept();
            while (accept.read(buf) != -1) {
                buf.flip();
                fc.write(buf);
                buf.clear();
            }
            accept.shutdownInput();
        } catch (IOException e) {
        }finally {
            try {
                fc.close();
                sscPort.close();
                ssc.close();
            } catch (IOException e) {
            }
        }
    }
}

基于选择器的无阻塞式网络通信

public class NetSocketTest2 {
    @Test
    public void Client() {
        SocketChannel sc = null;
        FileChannel file = null;
        try {
            sc = SocketChannel.open(new InetSocketAddress("192.168.0.103", 18888));
            file = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
            //切换为非阻塞式
            sc.configureBlocking(false);

            ByteBuffer buf = ByteBuffer.allocate(1024 * 1024);
            while (file.read(buf) != -1) {
                buf.flip();
                sc.write(buf);
                buf.clear();
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                file.close();
                sc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    @Test
    public void Server() {
        ServerSocketChannel ssc = null;
        FileChannel file = null;
        try {
            //将客户端的数据存储到本地
            file = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

            //获取服务端通道
            ssc = ServerSocketChannel.open();
            //设置为非阻塞式
            ssc.configureBlocking(false);
            //绑定服务监听端口号
            ssc.bind(new InetSocketAddress(18888));

            //获取选择器
            Selector selector = Selector.open();

            //选择器监控通道,因此
            //将通道注册选择器上,并设置选择键,选择键是明确选择器监控的是那种状态
            ssc.register(selector, SelectionKey.OP_ACCEPT);

            //迭代选择器上已经准备好了的事件(OP_ACCEPT)
            while (selector.select() > 0) {//如果选择器上准备就绪的事件大于0
                //如果有就绪的事件,就把它们拿出来迭代,
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();

                while (it.hasNext()){
                    SelectionKey selectionKey = it.next();
                    //判断具体什么事件准备就绪,
                    if (selectionKey.isAcceptable()) {//如果是接收事件准备就绪
                        //则获取客户端的套接字通道
                        SocketChannel accept = ssc.accept();
                        //并把套接字通道设置为非阻塞式
                        accept.configureBlocking(false);


                        //得到了客户端的通道后,也注册到选择器上,并监听他的读事件是否就绪
                        //同个下面的代码,就明确了无论是客户端还是服务端获取的通道,
                        // 都可以放在选择器中进行监听状态
                        accept.register(selector, SelectionKey.OP_READ);
                    } else if (selectionKey.isReadable()) {
                        //如果当前选择键位读就绪,就获取他的套接字通道,
                        SocketChannel sc = (SocketChannel) selectionKey.channel();
                        ByteBuffer buf = ByteBuffer.allocate(1024 * 1024);
                        //开始读写数据
                        while (sc.read(buf) > 0) {
                            buf.flip();
                            file.write(buf);
                            buf.clear();
                        }
                    }
                    //如果不是上面if else 语句中的事件,或已经执行完成了的,就可以清空掉。
                    it.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                file.close();
                ssc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

UDP版无阻塞式

import org.junit.Test;

import java.io.IOException;
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.util.Iterator;
import java.util.Scanner;

public class UDPNetSocketTest {
    public static void main(String[] args) {
        new UDPNetSocketTest().Cline();
    }
    @Test
    public void Cline() {
        DatagramChannel dc = null;
        Scanner sc = null;
        try {
            dc = DatagramChannel.open();
            dc.configureBlocking(false);
            ByteBuffer buf = ByteBuffer.allocate(1024);
            sc = new Scanner(System.in);
            String str = null;
            while ((str = sc.next()) != null) {
                buf.put(str.getBytes());
                buf.flip();
                dc.send(buf, new InetSocketAddress("127.0.0.1", 18888));
                buf.clear();
            }
        } catch (IOException e) {
        } finally {
            if (sc != null) {
                sc.close();
            }
            try {
                if (dc != null) {
                    dc.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    @Test
    public void Server() {
        DatagramChannel dc = null;
        try {
            dc = DatagramChannel.open();

            dc.bind(new InetSocketAddress(18888));

            dc.configureBlocking(false);

            Selector selector = Selector.open();
            dc.register(selector, SelectionKey.OP_READ);

            while (selector.select() > 0) {
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    if (selectionKey.isReadable()) {
                        ByteBuffer buf = ByteBuffer.allocate(1024);

                        dc.receive(buf);
                        buf.flip();
                        System.out.println(new String(buf.array(),0,buf.limit()));
                        buf.clear();
                    }
                    //一定要注意清空
                    iterator.remove();
                }
            }
        } catch (IOException e) {
        } finally {
            try {
                if (dc != null) {
                    dc.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
发布了51 篇原创文章 · 获赞 20 · 访问量 1538

猜你喜欢

转载自blog.csdn.net/qq_39711439/article/details/101844101