Java IO:NIO实现网络通信

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

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

    java.nio.channels.Channel 接口:
    |–SelectableChannel
    |–SocketChannel
    |–ServerSocketChannel
    |–DatagramChannel

    |–Pipe.SinkChannel
    |–Pipe.SourceChannel

  2. 缓冲区(Buffer):负责数据的存取

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

二、实现网络通信

2.1 SocketChannel

//客户端
@Test
public void client() throws IOException{
	//1. 获取通道
	SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
	
	//2. 切换非阻塞模式
	sChannel.configureBlocking(false);
	
	//3. 分配指定大小的缓冲区
	ByteBuffer buf = ByteBuffer.allocate(1024);
	
	//4. 发送数据给服务端
	Scanner scan = new Scanner(System.in);
	
	while(scan.hasNext()){
		String str = scan.next();
		buf.put((new Date().toString() + "\n" + str).getBytes());
		buf.flip();
		sChannel.write(buf);
		buf.clear();
	}
	
	//5. 关闭通道
	sChannel.close();
}

//服务端
@Test
public void server() throws IOException{
	//1. 获取通道
	ServerSocketChannel ssChannel = ServerSocketChannel.open();
	
	//2. 切换非阻塞模式
	ssChannel.configureBlocking(false);
	
	//3. 绑定连接
	ssChannel.bind(new InetSocketAddress(9898));
	
	//4. 获取选择器
	Selector selector = Selector.open();
	
	//5. 将通道注册到选择器上, 并且指定“监听接收事件”
	ssChannel.register(selector, SelectionKey.OP_ACCEPT);
	
	//6. 轮询式的获取选择器上已经“准备就绪”的事件
	while(selector.select() > 0){
		
		//7. 获取当前选择器中所有注册的“选择键(已就绪的监听事件)”
		Iterator<SelectionKey> it = selector.selectedKeys().iterator();
		
		while(it.hasNext()){
			//8. 获取准备“就绪”的是事件
			SelectionKey sk = it.next();
			
			//9. 判断具体是什么事件准备就绪
			if(sk.isAcceptable()){
				//10. 若“接收就绪”,获取客户端连接
				SocketChannel sChannel = ssChannel.accept();
				
				//11. 切换非阻塞模式
				sChannel.configureBlocking(false);
				
				//12. 将该通道注册到选择器上
				sChannel.register(selector, SelectionKey.OP_READ);
			}else if(sk.isReadable()){
				//13. 获取当前选择器上“读就绪”状态的通道
				SocketChannel sChannel = (SocketChannel) sk.channel();
				
				//14. 读取数据
				ByteBuffer buf = ByteBuffer.allocate(1024);
				
				int len = 0;
				while((len = sChannel.read(buf)) > 0 ){
					buf.flip();
					System.out.println(new String(buf.array(), 0, len));
					buf.clear();
				}
			}
			
			//15. 取消选择键 SelectionKey
			it.remove();
		}
	}
}

2.2 DatagramChannel

@Test
public void send() throws IOException{
	DatagramChannel dc = DatagramChannel.open();
	
	dc.configureBlocking(false);
	
	ByteBuffer buf = ByteBuffer.allocate(1024);
	
	Scanner scan = new Scanner(System.in);
	
	while(scan.hasNext()){
		String str = scan.next();
		buf.put((new Date().toString() + ":\n" + str).getBytes());
		buf.flip();
		dc.send(buf, new InetSocketAddress("127.0.0.1", 9898));
		buf.clear();
	}
	
	dc.close();
}

	@Test
	public void receive() throws IOException{
	DatagramChannel dc = DatagramChannel.open();
	
	dc.configureBlocking(false);
	
	dc.bind(new InetSocketAddress(9898));
	
	Selector selector = Selector.open();
	
	dc.register(selector, SelectionKey.OP_READ);
	
	while(selector.select() > 0){
		Iterator<SelectionKey> it = selector.selectedKeys().iterator();
		
		while(it.hasNext()){
			SelectionKey sk = it.next();
			
			if(sk.isReadable()){
				ByteBuffer buf = ByteBuffer.allocate(1024);
				
				dc.receive(buf);
				buf.flip();
				System.out.println(new String(buf.array(), 0, buf.limit()));
				buf.clear();
			}
		}
		
		it.remove();
	}
}

2.3 pipe

//1. 获取管道
Pipe pipe = Pipe.open();
	
//2. 将缓冲区中的数据写入管道
ByteBuffer buf = ByteBuffer.allocate(1024);
Pipe.SinkChannel sinkChannel = pipe.sink();
buf.put("通过单向管道发送数据".getBytes());
buf.flip();
sinkChannel.write(buf);
	
//3. 读取缓冲区中的数据
Pipe.SourceChannel sourceChannel = pipe.source();
buf.flip();
int len = sourceChannel.read(buf);
System.out.println(new String(buf.array(), 0, len));
	
sourceChannel.close();
sinkChannel.close();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值