socker TCP UDP BIO NIO
BIO: Java 1.4 以前只有之中方式。
bio:阻塞式IO, 一个 socker 连接占用一个 线程。如果 IO 阻塞,会在传输速度限制,这个线程也会一直等待在这里,等待从socker 的 IO 流 中读写数据。
Java 基于 socker 的 连接方式都是 BIO,都是阻塞式的IO。
TCP:
server:
ServerSocket ss = new ServerSocket(20000); while( true ) { Socket socket = ss.accept(); List<String> datas = IOUtils.readLines( socket.getInputStream() ); System.out.println( datas.get(0) ); socket.close(); }
cilent:
Socket socket = new Socket("127.0.0.1", 20000); IOUtils.write("hello", socket.getOutputStream() ); socket.getOutputStream().flush(); socket.getOutputStream().close(); socket.close();
原理图:
UDP:
server:
DatagramSocket ds = new DatagramSocket(20000); while( true ) { byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf, buf.length); ds.receive( dp ); System.out.println( new String(dp.getData()) ); }
client:
DatagramSocket ds = new DatagramSocket(); String data = "hello"; DatagramPacket dp = new DatagramPacket(data.getBytes(),data.length(),InetAddress.getLocalHost(),20000); ds.send(dp);
NIO: Java 1.4 以后支持的IO方式
NIO: 非阻塞式的IO, NIO 不再是 传统socker 方式。 一个 IO请求过来,会有一个线程,来处理这个IO请求,但是如果这个IO阻塞,那么这个线程会被处理别的 IO请求的 的事情。如果阻塞的IO的操作完成(完成一个块数据写入缓冲区),那么就会有一个线程分配过来处理这部分数据。
selecter: NIO 不再是 传统socke的概念。 socker 一个 连接,就固定一个 线程来处理,并且这个线程值服务这个 socker。但是 NIO 改变了这种IO 模型, selecter 里面跑着一个独立的线程,这个线程管理 一些渠道( channel , 有点 类似 socker ),如果 某个
渠道的 数据准备好了,那么 selecter 就会分配一个线程来 读写 这个channet 的数据。
buffer: 前面 一直强调 当数据准备好了,什么样才叫数据准备好了? socker 方式的 io 模式是一个 字节 一个字节读写的。 NIO 是通过 一块一块读写的( 一块就是 多个字节 ),但是这个这个 一块数据是 需要 一个临时存放区域的,这就是buffer 。当一个 块 数据满了以后,就分配 线程 来执行这个请求。
channel: 相当于以前的 流的 概念,可以 李杰成一个管道。BIO 一个连接一个线程,并且在socker结束以前一直占用着这个线程。NIO 是一个 也是一个连接一个线程。但是这个线程不会一直 等待这个 连接的IO操作。
例子代码:
server:
package comcxygg.test.nio; 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.nio.charset.Charset; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.UUID; public class NioServer { public static void main(String[] args) throws Exception { /** * 开启一个服务端 * 设置为非阻塞 * 绑定端口号 */ ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); serverSocketChannel.bind(new InetSocketAddress(8080)); System.out.println("serverSocketChannel:" + serverSocketChannel.hashCode() ); Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //UUID->客户端连接 Map<String,SocketChannel> clientMap = new HashMap<>(); while (true) { selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); selectionKeys.forEach(selectionKey -> { try { if (selectionKey.isAcceptable()) { /** * 服务端接收到连接 * 保存接收到的客户端连接 */ ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel(); System.out.println("server:" + server.hashCode() ); SocketChannel socketChannel = server.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector,SelectionKey.OP_READ); String key = UUID.randomUUID().toString(); clientMap.put(key,socketChannel); System.out.println(socketChannel.getRemoteAddress()+"连接上了服务器"); } else if (selectionKey.isReadable()) { /** * 读取客户端消息 * 转发到所有客户端 */ SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); System.out.println( "服务器端socketChannel:" + socketChannel.hashCode()); try { ByteBuffer buffer = ByteBuffer.allocate(1024); int len = socketChannel.read(buffer); if (len > 0) { buffer.flip(); Charset charset = Charset.forName("UTF-8"); String receiveMsg = String.valueOf(charset.decode(buffer).array()); String key = null; for (Map.Entry<String,SocketChannel> entry : clientMap.entrySet()) { if (entry.getValue() == socketChannel) { key = entry.getKey(); break; } } String sendMsg = key + ":" + receiveMsg; System.out.println(sendMsg); ByteBuffer writeBuffer = ByteBuffer.allocate(1024); writeBuffer.put(sendMsg.getBytes()); writeBuffer.flip(); clientMap.get(key).write( writeBuffer ); /* for (Map.Entry<String,SocketChannel> entry : clientMap.entrySet()) { ByteBuffer writeBuffer = ByteBuffer.allocate(1024); writeBuffer.put(sendMsg.getBytes()); writeBuffer.flip(); entry.getValue().write(writeBuffer); }*/ } }catch (Exception e) { e.printStackTrace();//java.io.IOException: 远程主机强迫关闭了一个现有的连接。 socketChannel.close(); } } } catch (Exception e) { e.printStackTrace(); } }); selectionKeys.clear(); } } }
client:
package comcxygg.test.nio; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class NioClient { public static void main(String[] args) throws Exception { /** * 开启一个客户端 * 设置为非阻塞 * 连接到服务器 */ SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress("localhost",8080)); Selector selector = Selector.open(); socketChannel.register(selector, SelectionKey.OP_CONNECT); System.out.println( "socketChannel:" + socketChannel.hashCode() ); while (true) { selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); for (SelectionKey selectionKey : selectionKeys) { if (selectionKey.isConnectable()) { /** * 客户端已连接 * 开启一个线程监听控制台输入 */ SocketChannel client = (SocketChannel) selectionKey.channel(); System.out.println( "client1:" + client.hashCode() ); if (client.isConnectionPending()) { client.finishConnect(); } client.register(selector,SelectionKey.OP_READ); ExecutorService executor = Executors.newSingleThreadExecutor(); System.out.println(socketChannel.getLocalAddress()+"连上了服务器"); ByteBuffer writeBuffer = ByteBuffer.allocate(1024); executor.submit(()->{ try { while (true) { writeBuffer.clear(); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String line = reader.readLine(); writeBuffer.put(line.getBytes()); writeBuffer.flip(); client.write(writeBuffer); } }catch (Exception e) { e.printStackTrace(); } }); } else if (selectionKey.isReadable()) { /** * 打印服务端消息 */ SocketChannel client = (SocketChannel) selectionKey.channel(); System.out.println( "client2:" + client.hashCode() ); ByteBuffer readBuffer = ByteBuffer.allocate(1024); int len = client.read(readBuffer); System.out.println(new String(readBuffer.array(),0,len)); } } selectionKeys.clear(); } } }
BIO 图:
NIO 的 UDP 协议使用:
别的基本一样 只是 渠道使用的 DatagramChannel 。
posted on 2019-05-16 14:56 zhangyukun 阅读(306) 评论(0) 编辑 收藏 举报