Java网络IO模型、阻塞与非阻塞、同步与异步

您所在的位置:网站首页 nio异步非阻塞 Java网络IO模型、阻塞与非阻塞、同步与异步

Java网络IO模型、阻塞与非阻塞、同步与异步

2023-04-26 03:51| 来源: 网络整理| 查看: 265

网络IO模型阻塞式I/O

Java网络IO模型、阻塞与非阻塞、同步与异步_java

默认情况下,所有的套接字的方法都是阻塞的,如上面的accept、recv。

对应的代码如下:

package com.morris.bio; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; public class BioSingleThreadServer { public static final int PORT = 8899; public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(); // socket(AF_INET6, SOCK_STREAM, IPPROTO_IP) = 5 serverSocket.bind(new InetSocketAddress(PORT)); // bind(5, {sa_family=AF_INET6, sin6_port=htons(8899), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = 0 // bind()里面会调用listen方法 listen(5, 50) System.out.println("server is start at " + PORT); // while (true) { try { Socket socket = serverSocket.accept(); // accept(3, {sa_family=AF_INET6, sin6_port=htons(34270), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) = 5 InputStream inputStream = socket.getInputStream(); System.out.println("connect success " + socket.getPort()); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); System.out.println("receive from client: " + reader.readLine()); // recv(5, "hello\r\n", 8192, 0) = 7 socket.close(); } catch (Exception e) { e.printStackTrace(); } } } }

上面的代码需要在jdk1.4上面运行才会出现accept的阻塞,高版本的jdk会使用poll方法阻塞,poll方法返回后才会去调用accept获取连接。

当然使用NIO的阻塞模式也会出现上面的效果,只不过recv函数会缓存read函数。

非阻塞式I/O

Java网络IO模型、阻塞与非阻塞、同步与异步_epoll_02

NIO需要调用套接字的方法前,将套接字设置为非阻塞模式,这样调用方法时就不会因为阻塞而进入休眠,而是立即返回,如果没有数据,就会返回一个错误。

对应的代码如下:

package com.morris.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.LinkedList; import java.util.Objects; public class NioSingleThreadServer { public static final int PORT = 8899; public static void main(String[] args) throws IOException, InterruptedException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // socket serverSocketChannel.bind(new InetSocketAddress(PORT)); // bind+listen serverSocketChannel.configureBlocking(false); // 设置accept不阻塞 LinkedList socketChannels = new LinkedList(); // 存放所有的客户端连接 while (true) { Thread.sleep(1000); // 获取连接 SocketChannel socketChannel = serverSocketChannel.accept(); // 不阻塞 accept if (Objects.isNull(socketChannel)) { // 没有连接会返回null System.out.println("null"); } else { System.out.println("connect success: " + socketChannel.socket().getPort()); socketChannel.configureBlocking(false); // 设置read不阻塞 socketChannels.add(socketChannel); } // 读取数据 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024); for (SocketChannel s : socketChannels) { if (s.isOpen()) { int readLength = s.read(byteBuffer); // 不阻塞,没数据返回-1 read if (readLength > 0) { byteBuffer.flip(); byte[] bytes = new byte[byteBuffer.limit()]; byteBuffer.get(bytes); System.out.println("receive from client: " + new String(bytes)); s.close(); byteBuffer.clear(); } } } } } }I/O复用(select/poll/epoll)

Java网络IO模型、阻塞与非阻塞、同步与异步_网络_03

虽然I/O多路复用的函数也是阻塞的,但是其与以上两种还是有不同的,I/O多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用如accept之上。

对应的代码如下:

package com.morris.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class NioSingleSelectorServer { public static final int PORT = 8899; private static Selector selector; public static void main(String[] args) throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(PORT)); serverSocketChannel.configureBlocking(false); selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 关心接受连接事件 System.out.println("server is start at " + PORT); while (true) { while (selector.select() > 0) { // selector.select()不带时间会一直阻塞,可以带一个超时时间 Set selectionKeys = selector.selectedKeys(); Iterator iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); if(key.isValid()) { if(key.isAcceptable()) { ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); // select只会返回有数据的FD,真正获取连接和读取数据还需要调用accept和read SocketChannel socketChannel = ssc.accept(); System.out.println("connect success: " + socketChannel.socket().getPort()); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } if(key.isReadable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024); socketChannel.read(byteBuffer); byteBuffer.flip(); byte[] bytes = new byte[byteBuffer.limit()]; byteBuffer.get(bytes); System.out.println("receive from client: " + new String(bytes)); socketChannel.close(); } } } } } } }异步I/O

Java网络IO模型、阻塞与非阻塞、同步与异步_阻塞_04

异步IO的工作机制是告知内核启动某个操作,并让内核在整个操作(包括将数据从内核拷贝到用户空间)完成后通知我们。

总结

阻塞与非阻塞:调用方法能立刻返回就是非阻塞,调用方法要等有数据了才返回就是阻塞。

同步与异步:访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞;异步只需要I/O操作完成的通知,并不主动读写数据,由操作系统内核完成数据的读写。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3