网络编程之NIO
Posted itbac
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程之NIO相关的知识,希望对你有一定的参考价值。
一、Buffer缓冲区
package com.itbac.net.NIO.BIO; import java.nio.ByteBuffer; /** * Buffer缓冲区 */ public class BufferDemo { public static void main(String[] args) { //构建一个byte 字节缓冲区,容量是4 // allocate分配内存(在堆中)堆中内存要复制到堆外才能用于网络通信,因为堆中有GC管理 // allocateDirect 分配内存(在堆外),减少一次内存复制操作,提高性能,但是要自己回收内存 ByteBuffer byteBuffer = ByteBuffer.allocate(4); System.out.println(String.format("初始化:capacity容量:%s,position位置:%s,limit限制:%s", byteBuffer.capacity(), byteBuffer.position(), byteBuffer.limit())); //写入3字节的数据 byteBuffer.put((byte) 1); byteBuffer.put((byte) 2); byteBuffer.put((byte) 3); System.out.println(String.format("写入3字节后:capacity容量:%s,position位置:%s,limit限制:%s", byteBuffer.capacity(), byteBuffer.position(), byteBuffer.limit())); System.out.println("开始读取——"); byteBuffer.flip(); System.out.println(String.format("切换成读模式:capacity容量:%s,position位置:%s,limit限制:%s", byteBuffer.capacity(), byteBuffer.position(), byteBuffer.limit())); byte a = byteBuffer.get(); System.out.println("读第1个:"+a); System.out.println(String.format("读取1字节后:capacity容量:%s,position位置:%s,limit限制:%s", byteBuffer.capacity(), byteBuffer.position(), byteBuffer.limit())); byte b = byteBuffer.get(); System.out.println("读第2个:"+b); System.out.println(String.format("读取2字节后:capacity容量:%s,position位置:%s,limit限制:%s", byteBuffer.capacity(), byteBuffer.position(), byteBuffer.limit())); /** * 继续写入3字节,此时读模式下,limit=3,position=2,继续写入只能写入一条数据,再多就会溢出。java.nio.BufferOverflowException * clear()方法清除整个缓存去。 compact()方法仅清除已阅读的数据。==> 两个方法都会转为写入模式。 */ byteBuffer.compact(); System.out.println(String.format("compact()后:capacity容量:%s,position位置:%s,limit限制:%s", byteBuffer.capacity(), byteBuffer.position(), byteBuffer.limit())); byteBuffer.put((byte) 4); byteBuffer.put((byte) 5); byteBuffer.put((byte) 6); System.out.println(String.format("最终的情况:capacity容量:%s,position位置:%s,limit限制:%s", byteBuffer.capacity(), byteBuffer.position(), byteBuffer.limit())); // byteBuffer.rewind(); 重置position为0 // byteBuffer.mark(); 标记position的位置 // byteBuffer.reset(); 重置position为上次 mark()标记的位置 } }
二、Channel通道
客户端
package com.itbac.net.NIO.BIO; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.Scanner; //客户端 public class NIOClient { public static void main(String[] args) throws IOException { //创建客户端通道 SocketChannel socketChannel = SocketChannel.open(); //设置非阻塞 socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080)); while (!socketChannel.finishConnect()) { //没连接上,则让出CPU ,一直等待 Thread.yield(); } Scanner scanner = new Scanner(System.in); System.out.println("请输入:"); //发送内容 String msg = scanner.nextLine(); //包装要发出的数据 ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes()); while (byteBuffer.hasRemaining()) { //如果Buffer缓冲区有剩余,循环写入通道中。 socketChannel.write(byteBuffer); } //读取响应 System.out.println("收到服务端响应:"); //开辟接收数据内存空间,堆内 ByteBuffer requestBuffer = ByteBuffer.allocate(1024); //通道开启状态 & 从通道读数据,写入到 buffer缓存区 while (socketChannel.isOpen()&& socketChannel.read(requestBuffer) !=-1) { //长连接情况下,需要手动判断数据有没有读取结束。 if (requestBuffer.position()==requestBuffer.limit()) { //buffer缓冲区,写入模式切换成读取模式 requestBuffer.flip(); byte[] content = new byte[requestBuffer.limit()]; requestBuffer.get(content); System.out.println(new String(content)); //清空已读,转成写模式 requestBuffer.compact(); } } //大于0 ,有写入的数据 if (requestBuffer.position()>0) { //buffer缓冲区,写入模式切换成读取模式 requestBuffer.flip(); byte[] content = new byte[requestBuffer.limit()]; requestBuffer.get(content); System.out.println(new String(content)); //清空已读,转成写模式 requestBuffer.compact(); } //关闭键盘录入 scanner.close(); //关闭通道 socketChannel.close(); } }
服务端
package com.itbac.net.NIO.BIO; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; public class Nioserver { public static void main(String[] args) throws IOException { //创建网络服务端 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //设置非阻塞IO serverSocketChannel.configureBlocking(false); //获取服务端套接字 ServerSocket serverSocket = serverSocketChannel.socket(); //绑定地址 serverSocket.bind(new InetSocketAddress("127.0.0.1",8080)); while (true) { SocketChannel socketChannel = serverSocketChannel.accept(); // 获取新tcp连接通道 // tcp请求 读取/响应 if (socketChannel != null) { System.out.println("收到新连接 : " + socketChannel.getRemoteAddress()); socketChannel.configureBlocking(false); // 默认是阻塞的,一定要设置为非阻塞 try { ByteBuffer requestBuffer = ByteBuffer.allocate(1024); while (socketChannel.isOpen() && socketChannel.read(requestBuffer) != -1) { // 长连接情况下,需要手动判断数据有没有读取结束 (此处做一个简单的判断: 超过0字节就认为请求结束了) if (requestBuffer.position() > 0) { break; } } if(requestBuffer.position() == 0) { continue; // 如果没数据了, 则不继续后面的处理 } requestBuffer.flip(); byte[] content = new byte[requestBuffer.limit()]; requestBuffer.get(content); System.out.println(new String(content)); System.out.println("收到数据,来自:"+ socketChannel.getRemoteAddress()); // 响应结果 200 String response = "HTTP/1.1 200 OK " + "Content-Length: 11 " + "Hello World"; ByteBuffer buffer = ByteBuffer.wrap(response.getBytes()); while (buffer.hasRemaining()) { socketChannel.write(buffer);// 非阻塞 } } catch (IOException e) { e.printStackTrace(); } } } // 用到了非阻塞的API, 在设计上,和BIO可以有很大的不同.继续改进 } }
以上是关于网络编程之NIO的主要内容,如果未能解决你的问题,请参考以下文章